Mask field in the editor

How do i make a mask selection popup like the layer selection popup you get in the inspector for any LayerMask, except for my own mask object.

EnumPopup is almost right but does not allow multiple selection.

I’ve written some code for this, unfortunately I cannot use the √ character to indicate a selected layer, I have to use instead which looks a bit ugly. But it works. It does even support the Everything and Nothing selections (that’s the ‘showSpecial’ flag).

public static LayerMask LayerMaskField (string label, LayerMask selected) {
	return LayerMaskField (label,selected,true);
}

public static LayerMask LayerMaskField (string label, LayerMask selected, bool showSpecial) {
	
	List<string> layers = new List<string>();
	List<int> layerNumbers = new List<int>();
	
	string selectedLayers = "";
	
	for (int i=0;i<32;i++) {
		
		string layerName = LayerMask.LayerToName (i);
		
		if (layerName != "") {
			if (selected == (selected | (1 << i))) {
				
				if (selectedLayers == "") {
					selectedLayers = layerName;
				} else {
					selectedLayers = "Mixed";
				}
			}
		}
	}
	
	EventType lastEvent = Event.current.type;
	
	if (Event.current.type != EventType.MouseDown && Event.current.type != EventType.ExecuteCommand) {
		if (selected.value == 0) {
			layers.Add ("Nothing");
		} else if (selected.value == -1) {
			layers.Add ("Everything");
		} else {
			layers.Add (selectedLayers);
		}
		layerNumbers.Add (-1);
	}
	
	if (showSpecial) {
		layers.Add ((selected.value == 0 ? "[X] " : "     ") + "Nothing");
		layerNumbers.Add (-2);
		
		layers.Add ((selected.value == -1 ? "[X] " : "     ") + "Everything");
		layerNumbers.Add (-3);
	}
	
	for (int i=0;i<32;i++) {
		
		string layerName = LayerMask.LayerToName (i);
		
		if (layerName != "") {
			if (selected == (selected | (1 << i))) {
				layers.Add ("[X] "+layerName);
			} else {
				layers.Add ("     "+layerName);
			}
			layerNumbers.Add (i);
		}
	}
	
	bool preChange = GUI.changed;
	
	GUI.changed = false;
	
	int newSelected = 0;
	
	if (Event.current.type == EventType.MouseDown) {
		newSelected = -1;
	}
	
	newSelected = EditorGUILayout.Popup (label,newSelected,layers.ToArray(),EditorStyles.layerMaskField);
	
	if (GUI.changed && newSelected >= 0) {
		//newSelected -= 1;
		
		Debug.Log (lastEvent +" "+newSelected + " "+layerNumbers[newSelected]);
		
		if (showSpecial && newSelected == 0) {
			selected = 0;
		} else if (showSpecial && newSelected == 1) {
			selected = -1;
		} else {
			
			if (selected == (selected | (1 << layerNumbers[newSelected]))) {
				selected &= ~(1 << layerNumbers[newSelected]);
				//Debug.Log ("Set Layer "+LayerMask.LayerToName (LayerNumbers[newSelected]) + " To False "+selected.value);
			} else {
				//Debug.Log ("Set Layer "+LayerMask.LayerToName (LayerNumbers[newSelected]) + " To True "+selected.value);
				selected = selected | (1 << layerNumbers[newSelected]);
			}
		}
	} else {
		GUI.changed = preChange;
	}
	
	return selected;
}

This can now be done without any custom editor classes with the [Flags] attribute:

using System;

[System.Flags]
public enum MyEnum
{
     Nothing = 0,
     Val1 = 1,
     Val2 = 2,
     Val3 = 4,
     Val4 = 8
     Everything = 0b1111
}

And your class/struct:

[System.Serializable]
public struct MyStruct
{
    public MyEnum myValue;
}

Result:

166557-flagattribute.png

What about the EditorGUILayout.EnumMaskField?
http://unity3d.com/support/documentation/ScriptReference/EditorGUILayout.EnumMaskField.html

Another option might be EditorGUILayout.MaskField if you want to define custom strings:
http://unity3d.com/support/documentation/ScriptReference/EditorGUILayout.MaskField.html

Note: Specifying null seems to insert horizontal splitter (at expense of a flag)

I believe this is what your looking for: http://wiki.unity3d.com/index.php/EnumFlagPropertyDrawer

Turn your enum into a multi flag selection variable :wink:

I’ve just modified my BitMaskField wrapper which i’ve written some time ago and made a LayerMask field that works exactly like the original.

EditorGUI.MaskField has the problem that you can’t specifiy which value which item has. It just assumes a linear list starting at 1 ( ,2,4,8,16,32, …) which is not very useful in most cases.

If you need my general BitMaskField, see [this question][1]

public static class EditorExtension
{
	private static string[] m_LayerNames = null;
	private static int[]    m_LayerMasks = null;
	
	static EditorExtension()
	{
		var tmpNames = new List< string >();
		var tmpMasks = new List< int >();
		for(int i = 0; i < 32; i++)
		{
			try
			{
				var name = LayerMask.LayerToName(i);
				if (name != "")
				{
					tmpNames.Add(name);
					tmpMasks.Add(1 << i);
				}
			}
			catch{}
		}
		m_LayerNames = tmpNames.ToArray();
		m_LayerMasks = tmpMasks.ToArray();
	}
	
	public static int DrawLayerMaskField(Rect aPosition, int aMask, GUIContent aLabel)
	{
		int val = aMask;
		int maskVal = 0;
		for(int i = 0; i < m_LayerNames.Length; i++)
		{
			if (m_LayerMasks *!= 0)*
  •  	{*
    

if ((val & m_LayerMasks) == m_LayerMasks*)
_ maskVal |= 1 << i;
}
else if (val == 0)
maskVal |= 1 << i;
}_
int newMaskVal = EditorGUI.MaskField(aPosition, aLabel, maskVal, m_LayerNames);
_ int changes = maskVal ^ newMaskVal;*_

* for(int i = 0; i < m_LayerMasks.Length; i++)
_ {
if ((changes & (1 << i)) != 0) // has this list item changed?
{
if ((newMaskVal & (1 << i)) != 0) // has it been set?
{
if (m_LayerMasks == 0) // special case: if “0” is set, just set the val to 0
{
val = 0;
break;
}
else*

val |= m_LayerMasks*;*
* }
else // it has been reset*

* {_
val &= ~m_LayerMasks;
_ }
}
}
return val;
}
}
[1]: http://answers.unity3d.com/questions/393992/custom-inspector-multi-select-enum-dropdown.html*_

THX numberkrouncher!!!

public static LayerMask LayerField(LayerMask layer)
		{
			LayerMask[] layers = new LayerMask[32];
			int m = 0;
			// search all leyers [32 is max layer count for Unity]
			for(int i = 0; i < 32; i++)
			{
				int layerID = i;
				string name = LayerMask.LayerToName(layerID);
				if(name != null && name.Length > 0)
				{
					layers[m] = layerID;
					m++;
				}
			}
			
			string[] names = new string[m];
			for(int i = 0; i < m; i++)
			{
				names _= LayerMask.LayerToName(layers*);*_

* }*

* LayerMask result = EditorGUILayout.MaskField ( layer.value, names );*

* return result;*
* }*

Hello,

I made this code for our custom editor project it behaves and looks exactly like unity layer mask field.

using System;
using UnityEngine;
using UnityEditor;

public class LayerMaskAttribute : PropertyAttribute { }

[CustomPropertyDrawer(typeof(LayerMaskAttribute))]
public class LayerMaskAttributeDrawer : BasePropertyDrawer {
    public override void OnGUI(UnityEngine.Rect position, UnityEditor.SerializedProperty property, UnityEngine.GUIContent label) {
        property.intValue = InspectorExtensions.LayerMaskField(position, label.text, property.intValue);
    }
}

public static class LayerMaskDrawer {

	public static int LayerMaskField(string label, int layermask) {
		return FieldToLayerMask(EditorGUILayout.MaskField(label, LayerMaskToField(layermask),
			InternalEditorUtility.layers));
	}

	public static int LayerMaskField(Rect position, string label, int layermask) {
		return FieldToLayerMask(EditorGUI.MaskField(position, label, LayerMaskToField(layermask),
			InternalEditorUtility.layers));
	}

	/// <summary>
	/// Converts field LayerMask values to in game LayerMask values
	/// </summary>
	/// <param name="field"></param>
	/// <returns></returns>
	private static int FieldToLayerMask(int field) {
		if (field == -1) return -1;
		int mask = 0;
		var layers = InternalEditorUtility.layers;
		for (int c = 0; c < layers.Length; c++) {
			if ((field & (1 << c)) != 0) {
				mask |= 1 << LayerMask.NameToLayer(layers
);
    			}
    			else {
    				mask &= ~(1 << LayerMask.NameToLayer(layers[c]));
    			}
    		}
    
    		return mask;
    	}
    
    	/// <summary>
    	/// Converts in game LayerMask values to field LayerMask values
    	/// </summary>
    	/// <param name="mask"></param>
    	/// <returns></returns>
    	private static int LayerMaskToField(int mask) {
    		if (mask == -1) return -1;
    		int field = 0;
    		var layers = InternalEditorUtility.layers;
    		for (int c = 0; c < layers.Length; c++) {
    			if ((mask & (1 << LayerMask.NameToLayer(layers[c]))) != 0) {
    				field |= 1 << c;
    			}
    		}
    
    		return field;
    	}
    }
var mask : LayerMask;