Search Unity

  1. Improved Prefab workflow (includes Nested Prefabs!), 2D isometric Tilemap and more! Get the 2018.3 Beta now.
    Dismiss Notice
  2. The Unity Pro & Visual Studio Professional Bundle gives you the tools you need to develop faster & collaborate more efficiently. Learn more.
    Dismiss Notice
  3. Let us know a bit about your interests, and if you'd like to become more directly involved. Take our survey!
    Dismiss Notice
  4. Improve your Unity skills with a certified instructor in a private, interactive classroom. Watch the overview now.
    Dismiss Notice
  5. Want to see the most recent patch releases? Take a peek at the patch release page.
    Dismiss Notice

Get the Layernumber from a LayerMask

Discussion in 'Scripting' started by gamma.psh, Dec 6, 2011.

  1. gamma.psh

    gamma.psh

    Joined:
    Jan 25, 2011
    Posts:
    44
    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?
     
  2. ivkoni

    ivkoni

    Joined:
    Jan 26, 2009
    Posts:
    978
    Not exactly what you are asking for, but maybe you can use NameToLayer if you know the name.
     
  3. Dreamora

    Dreamora

    Joined:
    Apr 5, 2008
    Posts:
    26,598
    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
     
  4. Dantus

    Dantus

    Joined:
    Oct 21, 2009
    Posts:
    5,668
    You may check if aCertainLayerMask also contains layer number 20 as follows:

    Code (csharp):
    1. (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.
     
    Last edited: Dec 6, 2011
  5. Ntero

    Ntero

    Joined:
    Apr 29, 2010
    Posts:
    1,436
    You could do something like this:

    Code (csharp):
    1.  
    2. int layerNumber = 0;
    3. int layer = myLayer.value;
    4. while(layer > 0)
    5. {
    6.     layer = layer >> 1;
    7.     layerNumber++;
    8. }
    9. return layerNumber;
    10.  
    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.
     
  6. gamma.psh

    gamma.psh

    Joined:
    Jan 25, 2011
    Posts:
    44
    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 ;)
     
  7. popeye1

    popeye1

    Joined:
    Dec 19, 2014
    Posts:
    28
    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.
     
    Last edited: Feb 25, 2015
  8. kingandroid

    kingandroid

    Joined:
    Nov 13, 2014
    Posts:
    9
    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
    Code (csharp):
    1. return layerNumber - 1;
    to get correct result
    or
    Code (csharp):
    1. while(layer > 1)
     
  9. guneyozsan

    guneyozsan

    Joined:
    Feb 1, 2012
    Posts:
    47
    Last edited: Apr 10, 2017
  10. andrew-lukasik

    andrew-lukasik

    Joined:
    Jan 31, 2013
    Posts:
    101
    @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
    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
     
    Last edited: Apr 5, 2017
    guneyozsan likes this.
  11. andrew-lukasik

    andrew-lukasik

    Joined:
    Jan 31, 2013
    Posts:
    101
    I find this to be best solution so far:
    (based on @Ntero's post)
    Code (CSharp):
    1. /// <summary> Converts given bitmask to layer number </summary>
    2. /// <returns> layer number </returns>
    3. public static int ToLayer ( int bitmask ) {
    4.     int result = bitmask>0 ? 0 : 31;
    5.     while( bitmask>1 ) {
    6.         bitmask = bitmask>>1;
    7.         result++;
    8.     }
    9.     return result;
    10. }
    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
     
    Last edited: Apr 5, 2017
  12. MitchStan

    MitchStan

    Joined:
    Feb 26, 2007
    Posts:
    505
    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.
     
  13. andrew-lukasik

    andrew-lukasik

    Joined:
    Jan 31, 2013
    Posts:
    101
    This well may be true @MitchStan :)
    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:
    Code (CSharp):
    1. Physics.Raycast( ray , out hit , 100f , Layer.Mask.Static|Layer.Mask.Actors )
    and (as of since now) that:
    Code (CSharp):
    1. if( otherGameObject.layer==Layer.Dynamic ) otherGameObject.layer = Layer.Static;
    Code (CSharp):
    1. public static class Layer {
    2.  
    3.     public static class Mask {
    4.  
    5.         public const int Static = 1<<12;
    6.         public const int Dynamic = 1<<13;
    7.         public const int Actors = 1<<9;
    8.         public const int Bullets = 1<<10;
    9.  
    10.         /// <summary> Converts given bitmask to layer number </summary>
    11.         /// <returns> layer number </returns>
    12.         public static int ToLayer ( int bitmask ) {
    13.             int result = bitmask>0 ? 0 : 31;
    14.             while( bitmask>1 ) {
    15.                 bitmask = bitmask>>1;
    16.                 result++;
    17.             }
    18.             return result;
    19.         }
    20.  
    21.     }
    22.  
    23.     public static int Static { get { return Mask.ToLayer( Mask.Static ); } }
    24.     public static int Dynamic { get { return Mask.ToLayer( Mask.Dynamic ); } }
    25.     public static int Actors  { get { return Mask.ToLayer( Mask.Actors ); } }
    26.     public static int Bullets  { get { return Mask.ToLayer( Mask.Bullets ); } }
    27.  
    28. }
    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
     
    Last edited: Apr 5, 2017
    MitchStan likes this.
  14. guneyozsan

    guneyozsan

    Joined:
    Feb 1, 2012
    Posts:
    47
    @andrew-lukasik Wow, nice detail. Thanks for pointing out.
     
  15. bentunnadine

    bentunnadine

    Joined:
    Oct 21, 2016
    Posts:
    8
    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:

    Code (CSharp):
    1. using System;
    2. using UnityEngine;
    3.  
    4. [Serializable]
    5. public struct Layer
    6. {
    7.     [SerializeField]
    8.     int m_layer;
    9.  
    10.     public int Index { get { return m_layer; } set { m_layer = value; } }
    11.     public int Mask { get { return 1 << m_layer; } }
    12.     public string Name
    13.     {
    14.         get { return LayerMask.LayerToName(m_layer); }
    15.         set { m_layer = LayerMask.NameToLayer(value); }
    16.     }
    17.  
    18.     public static implicit operator int(Layer l)
    19.     {
    20.         return l.Index;
    21.     }
    22.  
    23.     public static implicit operator Layer(int i)
    24.     {
    25.         return new Layer() { Index = i };
    26.     }
    27.  
    28.     public Layer(int index)
    29.     {
    30.         m_layer = index;
    31.     }
    32.     public Layer(string name)
    33.     {
    34.         m_layer = LayerMask.NameToLayer(name);
    35.     }
    36. }
    Can also be paired with a property drawer to make it display as a dropdown in the inspector:

    Code (CSharp):
    1. [CustomPropertyDrawer(typeof(Layer))]
    2. public class LayerDrawer : PropertyDrawer
    3. {
    4.     public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
    5.     {
    6.         SerializedProperty layerProp = property.FindPropertyRelative("m_layer");
    7.         layerProp.intValue = EditorGUI.LayerField(position, label, layerProp.intValue);
    8.     }
    9. }
    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:
    Code (CSharp):
    1. layerNumber = (int)(Mathf.Log((uint)myLayer.value, 2))
     
    guneyozsan and andrew-lukasik like this.
  16. isbdnt

    isbdnt

    Joined:
    May 16, 2017
    Posts:
    29
    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)