Get the Layernumber from a LayerMask

I have a Problem with GameObject.layer which expects an int between 1-32 and LayerMask class

Lets say I have a Layer lets choose 20 and Call this layer “MyVeryImportantLayer”

Now there is a Class LayerMask and if this class is a public field then it shows up in my inspector, and I can say that it should represent “MyVeryImportantLayer”.

Now my LayerMask.value is the same as 1 << 20 or also int 1048576 I could also write
LayerMask.value == (int)LayerMask == 1 << 20 == 1048576
Now why is there even a LayerMask.value, if LayerMask cast to int works the same, does this make any sense?

But how do i get this 20 out of a LayerMask value to set a GameObject to this Layer? something like
myGameObject.layer = LayerMask.gievmetheLayerID()

I more or less need the opposite function of 1 << 20 does someone know how this works?

Not exactly what you are asking for, but maybe you can use NameToLayer if you know the name.

1 Like

it needs a dirty little trick. You have to ‘reverse the bit shifting’ of the layer value as it is a bit mask

layer.value = 1 << layerNumber (from editor) = 2^layerNumber

2 Likes

You may check if aCertainLayerMask also contains layer number 20 as follows:

(aCertainLayerMask.value (1 << 20)) != 0.

If you want to check for more than just number 20, you need to check each of those numbers separately as far as I know.

You could do something like this:

int layerNumber = 0;
int layer = myLayer.value;
while(layer > 0)
{
	layer = layer >> 1;
	layerNumber++;
}
return layerNumber;

Essentially it just counts the number of bitshifts to get clear the flag. And unless you have multiple bit flags active it should return 20, to your 1 << 20.

1 Like

Ok thx, I just wanted to know if there is a easy way, but seems like i need to calculate it on my own.

like dreamora said the values of 1 << x are equal to 2^x.
Now whats special about these numbers is (2^0 + 2^1 + 2^2…2^n-1) < 2^n
This gives the possibility to calculate all the checked layers, and if Multiple layers are checked I throw a warning or do something other clever

I must even say that I feel a little bit stupid about my Question of course a LayerMask represents not strictly 1 Layer, so calculating the Layers from a Mask maybe gives me several results, what I didn’t even think of -.-
But I think it was a good refresh about LayerMask’s for me, thanks everyone :wink:

Also believes its strange that given you have a Layermask you can’t get the Layer name or the Layer number with any simple function. Ive found that you have to convert back your layer mask number to the layer number and then use LayerToName.

Also in Unity 5 (as far as I know, havent checked the unity 4), the layer index start from 0, so you might wanna do
return layerNumber - 1; to get correct result
or while(layer > 1)

1 Like

You can use this if you are sure your layer mask has only a single layer in it.

layerNumber = Mathf.Log(myLayer.value, 2)

Edit: This doesn’t work for the single case of 1<<31 as mentioned in the reply below: Get the Layernumber from a LayerMask - Unity Engine - Unity Discussions

2 Likes

@guneyozsan , Thank you for sharing this. I tested your method and, correct me if I’m wrong, it seem to not work for single case of 1<<31 (because it refers to -2147483648). Other are ok tho
test results for “(int)Mathf.Log( (float)bitmask , 2f )”

1<<-1 returns -2147483648 expected fail
1<<0 returns 0 success
1<<1 returns 1 success
1<<17 returns 17 success
1<<30 returns 30 success
1<<31 returns -2147483648 fail
1<<32 returns 0 expected fail

2 Likes

I find this to be best solution so far:
(based on @anon_67940662 's post)

/// <summary> Converts given bitmask to layer number </summary>
/// <returns> layer number </returns>
public static int ToLayer ( int bitmask ) {
    int result = bitmask>0 ? 0 : 31;
    while( bitmask>1 ) {
        bitmask = bitmask>>1;
        result++;
    }
    return result;
}

test results

1<<-1 returns 31 expected fail
1<<0 returns 0 success
1<<1 returns 1 success
1<<17 returns 17 success
1<<30 returns 30 success
1<<31 returns 31 success
1<<32 returns 0 expected fail

Post please if you have something more clever than this

2 Likes

This. Screw all that bit shift crap. I have always used NameToLayer and LayerToName and never once used masks or bitshift.

You’ll save yourself endless headaches if you just let the unity functions do the work for you.

1 Like

This well may be true @MitchStan :slight_smile:
Or at least - matter of preference for string or integers. But since this topic seems dedicated to only one of them it makes sense to address it here and solve this volontary problem

Personally I just want to use nice, clear property names, like that:

Physics.Raycast( ray , out hit , 100f , Layer.Mask.Static|Layer.Mask.Actors )

and (as of since now) that:

if( otherGameObject.layer==Layer.Dynamic ) otherGameObject.layer = Layer.Static;

Layer.cs

public static class Layer {

    public static class Mask {

        public const int Static = 1<<12;
        public const int Dynamic = 1<<13;
        public const int Actors = 1<<9;
        public const int Bullets = 1<<10;

        /// <summary> Converts given bitmask to layer number </summary>
        /// <returns> layer number </returns>
        public static int ToLayer ( int bitmask ) {
            int result = bitmask>0 ? 0 : 31;
            while( bitmask>1 ) {
                bitmask = bitmask>>1;
                result++;
            }
            return result;
        }

    }

    public static int Static { get { return Mask.ToLayer( Mask.Static ); } }
    public static int Dynamic { get { return Mask.ToLayer( Mask.Dynamic ); } }
    public static int Actors  { get { return Mask.ToLayer( Mask.Actors ); } }
    public static int Bullets  { get { return Mask.ToLayer( Mask.Bullets ); } }

}

and I’m not totally sure yet which method would be better for this task. Seems like both can do the job just as well

3 Likes

@andrew-lukasik Wow, nice detail. Thanks for pointing out.

In case any one else needs to do something similar (useful for dynamic camera masks) and needs both the layer mask and the layer it may be helpful to make a stuct like so:

using System;
using UnityEngine;

[Serializable]
public struct Layer
{
    [SerializeField]
    int m_layer;

    public int Index { get { return m_layer; } set { m_layer = value; } }
    public int Mask { get { return 1 << m_layer; } }
    public string Name
    {
        get { return LayerMask.LayerToName(m_layer); }
        set { m_layer = LayerMask.NameToLayer(value); }
    }

    public static implicit operator int(Layer l)
    {
        return l.Index;
    }

    public static implicit operator Layer(int i)
    {
        return new Layer() { Index = i };
    }

    public Layer(int index)
    {
        m_layer = index;
    }
    public Layer(string name)
    {
        m_layer = LayerMask.NameToLayer(name);
    }
}

Can also be paired with a property drawer to make it display as a dropdown in the inspector:

[CustomPropertyDrawer(typeof(Layer))]
public class LayerDrawer : PropertyDrawer
{
    public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
    {
        SerializedProperty layerProp = property.FindPropertyRelative("m_layer");
        layerProp.intValue = EditorGUI.LayerField(position, label, layerProp.intValue);
    }
}

Or just to fix the negative issue with @guneyozsan 's code produces, you can cast it to a uint in the interim (as well as a cast back to int as Mathf.Log only returns a float:

layerNumber = (int)(Mathf.Log((uint)myLayer.value, 2))
7 Likes

layerNumber = (int)(Mathf.Log((uint)myLayer.value, 2)) got large negative number with the default layer and below works well.
layerNumber = (int)(uint)Mathf.Log((uint)myLayer.value, 2)

1 Like

here’s something I found that worked.

int layer =Mathf.RoundToInt(Mathf.Log(layerMask.value, 2))
2 Likes

No it doesn’t .

1 Like

I guess scrolled over that. Thanks for pointing it out.

I am using it to set the layer of the collider and it seems to work (although I haven’t tested -31,32). Apart from that while loop you posted, Have you found anything else?

You can use this method BUT you better include a note/comment in your code that this method wont work for layer 32 (i.e.: 1<<31) because it could bite you one day. Or oven better - test for this specific case and return corrected result or throw exception at least.

(-31 and 32 are outside valid value range so ignore that)