Is there a way to manually invoke the LayerMask field in an EditorWindow? Like you can with TagField or LayerField...
I know this is quite old but Vexe brought up an answer using the editor Internal namespace. I went and explored it a little and it seems it has methods built specifically for this.
First of, make sure you include - using UnityEditorInternal;
at the top ;).
Then you can use a regular mask field with the methods Unity provides like such.
LayerMask tempMask = EditorGUILayout.MaskField( InternalEditorUtility.LayerMaskToConcatenatedLayersMask(myLayerMask), InternalEditorUtility.layers);
Then you can convert that mask back to the correct value using -
myLayerMask = InternalEditorUtility.ConcatenatedLayersMaskToLayerMask(tempMask);
Of course you can do this in a single line, but it’s better demonstrated using two, and better practice to store it in a temp value so you can Undo.Record the object in a change check before applying it.
Hope this helps,
Dan.
This one removes all empty layers. The performance runs at 0.02 over 1000 iterations.
static LayerMask LayerMaskField( string label, LayerMask layerMask) {
List<string> layers = new List<string>();
List<int> layerNumbers = new List<int>();
for (int i = 0; i < 32; i++) {
string layerName = LayerMask.LayerToName(i);
if (layerName != "") {
layers.Add(layerName);
layerNumbers.Add(i);
}
}
int maskWithoutEmpty = 0;
for (int i = 0; i < layerNumbers.Count; i++) {
if (((1 << layerNumbers*) & layerMask.value) > 0)*
maskWithoutEmpty |= (1 << i);
}
maskWithoutEmpty = EditorGUILayout.MaskField( label, maskWithoutEmpty, layers.ToArray());
int mask = 0;
for (int i = 0; i < layerNumbers.Count; i++) {
if ((maskWithoutEmpty & (1 << i)) > 0)
mask |= (1 << layerNumbers*);*
}
layerMask.value = mask;
return layerMask;
}
Hmm, it looks like they forgot to implement it / make it public. I've found that little helperclass which can convert a layer into the corresponding layername and reverse: LayerMask. With this you can try to implement something similar but it won't look like the popup in the inspector.
btw. if you add a public var with this type to a script the default inspector will show that popup, but it seems that we can't invoke it manually.
Here is an implementation which looks quite similar to the built in one. But it shows unnamed layers as well due to technical limitations.
To prevent too much memory usage is updates the layer name list only every second… but that should be enough.
I have an implementation for Unity 3.4 and earlier as well, but the code for that is a lot more hackish. But I can post it if anyone is interested.
public static List<string> layers;
public static List<int> layerNumbers;
public static string[] layerNames;
public static long lastUpdateTick;
/** Displays a LayerMask field.
* \param showSpecial Use the Nothing and Everything selections
* \param selected Current LayerMask
* \version Unity 3.5 and up will use the EditorGUILayout.MaskField instead of a custom written one.
*/
public static LayerMask LayerMaskField (string label, LayerMask selected, bool showSpecial) {
//Unity 3.5 and up
if (layers == null || (System.DateTime.Now.Ticks - lastUpdateTick > 10000000L && Event.current.type == EventType.Layout)) {
lastUpdateTick = System.DateTime.Now.Ticks;
if (layers == null) {
layers = new List<string>();
layerNumbers = new List<int>();
layerNames = new string[4];
} else {
layers.Clear ();
layerNumbers.Clear ();
}
int emptyLayers = 0;
for (int i=0;i<32;i++) {
string layerName = LayerMask.LayerToName (i);
if (layerName != "") {
for (;emptyLayers>0;emptyLayers--) layers.Add ("Layer "+(i-emptyLayers));
layerNumbers.Add (i);
layers.Add (layerName);
} else {
emptyLayers++;
}
}
if (layerNames.Length != layers.Count) {
layerNames = new string[layers.Count];
}
for (int i=0;i<layerNames.Length;i++) layerNames _= layers*;*_
}
selected.value = EditorGUILayout.MaskField (label,selected.value,layerNames);
return selected;
}
A slightly improved version of @FlyingOstriche - no need to allocate lists. The layer names can be accessed via InternalEditorUtility
in UnityEditorInternal
namespace. For the numbers list, a static list seems to do the job
static List<int> layerNumbers = new List<int>();
static LayerMask LayerMaskField(string label, LayerMask layerMask)
{
var layers = InternalEditorUtility.layers;
layerNumbers.Clear();
for (int i = 0; i < layers.Length; i++)
layerNumbers.Add(LayerMask.NameToLayer(layers*));*
int maskWithoutEmpty = 0;
for (int i = 0; i < layerNumbers.Count; i++)
{
if (((1 << layerNumbers*) & layerMask.value) > 0)*
maskWithoutEmpty |= (1 << i);
}
maskWithoutEmpty = UnityEditor.EditorGUILayout.MaskField(label, maskWithoutEmpty, layers);
int mask = 0;
for (int i = 0; i < layerNumbers.Count; i++)
{
if ((maskWithoutEmpty & (1 << i)) > 0)
mask |= (1 << layerNumbers*);*
}
layerMask.value = mask;
return layerMask;
}
since we using signed int that will be negative if most significant bit is set
if ((maskWithoutEmpty & (1 << i)) > 0)
is wrong
should be
if ((maskWithoutEmpty & (1 << i)) != 0)
Hello, i made this code that draws LayerMask field that behaves exactly like unity layermask 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;
}
}
You can use something like this:
yourLayerMask = EditorGUILayout.MaskField(new GUIContent("Variable name in inspector", "Tooltip."), yourLayerMask, UnityEditorInternal.InternalEditorUtility.layers);
- yourLayerMask is the LayerMask you want to set a value.
- EditorGUILayout.MaskField function makes a flag field in the inspector.
- The first argument new GUIContent contains the information about the variable name shown in the inspector, and a tooltip shown when the cursor is up the variable
- The second argument yourLayerMask is the LayerMask you want to set a value
- The third argument is the names that will be shown in the field. We need to set the layer names, so we can get the names using UnityEditorInternal.InternalEditorUtility.layers property.
Important
To make this works, you need to use the layers in order. I mean, this works only if you do not use a layer with a higher value than an unused layer.
Example:
- Wrong use of layers: User Layer 3 not used, User Layer 6 used, User Layer 7 used.
- Right use of layers: User Layer 3 used, User Layer 6 used, User Layer 7 used.
You can use something like this:
yourLayerMask = EditorGUILayout.MaskField(new GUIContent("Variable name in inspector", "Tooltip."), yourLayerMask, UnityEditorInternal.InternalEditorUtility.layers);
- yourLayerMask is the LayerMask you want to set a value.
- EditorGUILayout.MaskField function makes a flag field in the inspector.
- The first argument new GUIContent contains the information about the variable name shown in the inspector, and a tooltip shown when the cursor is up the variable
- The second argument yourLayerMask is the LayerMask you want to set a value
- The third argument is the names that will be shown in the field. We need to set the layer names, so we can get the names using UnityEditorInternal.InternalEditorUtility.layers property.
Important
To make this works, you need to use the layers in order. I mean, this works only if you do not use a layer with a higher value than an unused layer.
Example:
- Wrong use of layers: User Layer 3 not used, User Layer 6 used, User Layer 7 used.
- Right use of layers: User Layer 3 used, User Layer 6 used, User Layer 7 used.