- Home /
How to create LayerMask field in a custom EditorWindow?
Is there a way to manually invoke the LayerMask field in an EditorWindow? Like you can with TagField or LayerField...
I made one code to resove that in my GitHub, check out: https://github.com/Delgado-tech/layerMaskDrawer
Answer by Llockham-Industries · Aug 01, 2017 at 06:41 AM
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 needs to be the new updated answer! Good work, thanks very much!
Just to make sure people new at this are able to use this as well:
you need an accessible Layer$$anonymous$$ask variable in your non-editor script
[HideInInspector] public Layer$$anonymous$$ask my$$anonymous$$ask;
then on the editor script you declare myLayer$$anonymous$$ask outside OnInspectorGUI
Layer$$anonymous$$ask myLayer$$anonymous$$ask = new Layer$$anonymous$$ask();
then in OnInspectorGUi you do this:
public override void OnInspectorGUI(){
var t = ($$anonymous$$yScript)target;
DrawDefaultInspector();
Layer$$anonymous$$ask temp$$anonymous$$ask = EditorGUILayout.$$anonymous$$askField("$$anonymous$$y $$anonymous$$ask", InternalEditorUtility.Layer$$anonymous$$askToConcatenatedLayers$$anonymous$$ask(myLayer$$anonymous$$ask), InternalEditorUtility.layers);
myLayer$$anonymous$$ask = InternalEditorUtility.ConcatenatedLayers$$anonymous$$askToLayer$$anonymous$$ask(temp$$anonymous$$ask);
t.my$$anonymous$$ask = myLayer$$anonymous$$ask;
}
Answer by FlyingOstriche · Feb 21, 2015 at 07:35 PM
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[i]) & 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[i]);
}
layerMask.value = mask;
return layerMask;
}
Answer by Bunny83 · Jan 15, 2011 at 04:52 AM
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.
Answer by TowerOfBricks · Jul 26, 2012 at 07:18 PM
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[i] = layers[i];
}
selected.value = EditorGUILayout.MaskField (label,selected.value,layerNames);
return selected;
}
Answer by vexe · Jun 01, 2015 at 09:35 AM
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[i]));
int maskWithoutEmpty = 0;
for (int i = 0; i < layerNumbers.Count; i++)
{
if (((1 << layerNumbers[i]) & 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[i]);
}
layerMask.value = mask;
return layerMask;
}