Search Unity

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.
     
    missladybug likes this.
  3. Dreamora

    Dreamora

    Joined:
    Apr 5, 2008
    Posts:
    26,601
    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,667
    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.
     
    Bunny83 likes this.
  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)
     
    Bunny83 likes this.
  9. guneyozsan

    guneyozsan

    Joined:
    Feb 1, 2012
    Posts:
    99
    Last edited: Apr 10, 2017
    BrainSlugs83 and GodlikeAurora like this.
  10. andrew-lukasik

    andrew-lukasik

    Joined:
    Jan 31, 2013
    Posts:
    249
    @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
    Kokowolo and guneyozsan like this.
  11. andrew-lukasik

    andrew-lukasik

    Joined:
    Jan 31, 2013
    Posts:
    249
    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
    Tigromvivo likes this.
  12. MitchStan

    MitchStan

    Joined:
    Feb 26, 2007
    Posts:
    568
    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.
     
    tbriz likes this.
  13. andrew-lukasik

    andrew-lukasik

    Joined:
    Jan 31, 2013
    Posts:
    249
    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
    tbriz, PrimalCoder and MitchStan like this.
  14. guneyozsan

    guneyozsan

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

    bentunnadine

    Joined:
    Oct 21, 2016
    Posts:
    9
    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))
     
  16. isbdnt

    isbdnt

    Joined:
    May 16, 2017
    Posts:
    35
    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)
     
    CengizKuscu likes this.
  17. FlightOfOne

    FlightOfOne

    Joined:
    Aug 1, 2014
    Posts:
    668
    PinkPanteRus and ksf000 like this.
  18. andrew-lukasik

    andrew-lukasik

    Joined:
    Jan 31, 2013
    Posts:
    249
    No it doesn't.
     
    tbriz likes this.
  19. FlightOfOne

    FlightOfOne

    Joined:
    Aug 1, 2014
    Posts:
    668
    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?
     
  20. andrew-lukasik

    andrew-lukasik

    Joined:
    Jan 31, 2013
    Posts:
    249
    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)
     
    Last edited: Apr 16, 2019
  21. FeastSC2

    FeastSC2

    Joined:
    Sep 30, 2016
    Posts:
    978
    Is there a way to get all the layer values in the LayerMask?
    You're all talking about finding the ONE layer's value in the LayerMask. But what if it's a LayerMask that has several layers in it?


    Code (CSharp):
    1. using System.Collections.Generic;
    2. using UnityEngine;
    3.  
    4. /// <summary>
    5. /// https://forum.unity.com/threads/get-the-layernumber-from-a-layermask.114553/
    6. /// </summary>
    7. public class LayerMaskUtility
    8. {
    9.     public static bool IsInLayer(int _gameObjectLayer, LayerMask _mask)
    10.     {
    11.         return ((1 << _gameObjectLayer) & _mask) != 0;
    12.     }
    13.  
    14.     /// <summary>
    15.     /// Returns a value between [0;31].
    16.     /// Important: This will only work properly if the LayerMask is one created in the inspector and not a LayerMask
    17.     /// with multiple values.
    18.     /// </summary>
    19.     public static int GetLayerNumber(LayerMask _mask)
    20.     {
    21.         var bitmask = _mask.value;
    22.         int result = bitmask > 0 ? 0 : 31;
    23.         while (bitmask > 1)
    24.         {
    25.             bitmask = bitmask >> 1;
    26.             result++;
    27.         }
    28.         return result;
    29.     }
    30.  
    31.     /// <summary>
    32.     /// Returns a list of values between [0;31].
    33.     /// </summary>
    34.     public static List<int> GetAllLayerNumbers(LayerMask _mask)
    35.     {
    36.         // ??
    37.     }
    38. }
     
    siddharth3322 likes this.
  22. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,338
    Code (csharp):
    1.  
    2. /// <summary>
    3. /// Returns a list of values between [0;31].
    4. /// </summary>
    5. public static List<int> GetAllLayerMaskInspectorIndex(LayerMask _mask)
    6. {
    7.     List<int> layers = new List<int>();
    8.     var bitmask = _mask.value;
    9.     for (int i = 0; i < 32; i++) {
    10.         if (((1 << i) & bitmask) != 0) {
    11.             layers.Add(i);
    12.         }
    13.     }
    14.     return layers;
    15. }
    But you probably don't want to do that, unless you want to print the values for debug purposes?
     
    Kokowolo, Bunny83, Plaximo and 4 others like this.
  23. FeastSC2

    FeastSC2

    Joined:
    Sep 30, 2016
    Posts:
    978
    Thanks :) Is bit shifting costly that you're saying this?
     
    granit likes this.
  24. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,537
    kind of... but really it's creating a list that is costly
     
  25. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,338
    Bit shifting is dirt cheap. @lordofduct is right in that it's creating the list that's costly, but more than that it's the simple fact that you already have the data there, so converting it to a List format is probably not necessary.

    What do you think you need the list for?
     
    Bunny83 likes this.
  26. FeastSC2

    FeastSC2

    Joined:
    Sep 30, 2016
    Posts:
    978
    I was curious to see if it was possible. But the reason was to use Physics.IgnoreLayerCollision() That being said I see now that this applies to all colliders and so it's not what I want.

    I wanted the ability to make my projectiles only collide with certain layers while leaving them on the default layer. I wanted to make them not interact with certain layers based on a LayerMask I exposed in the inspector. I realize now that this is impossible. At least it looks like it. But it's no big deal, it wasn't a necessity, only a curiosity.
     
  27. Stjenderman

    Stjenderman

    Joined:
    Apr 23, 2020
    Posts:
    1
    LayerNumber = MathF.Log(LayerMask.value, 2);
    This should work
     
  28. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,338
    The question you're answering was asked in 2011, and your answer is wrong and bad.

    Mathf.Log is float-based and layers are ints, so you'll have to round and hope that your apple to oranges conversion works. And layermasks can contain several values, so you'd end up with bogus answers.
     
    a436t4ataf likes this.
  29. tgaldi

    tgaldi

    Joined:
    Oct 28, 2015
    Posts:
    102
    Small modification to code posted earlier if you are using layermasks for single layers.

    Code (CSharp):
    1.  
    2. using UnityEngine;
    3. public static class LayerMaskExtensions
    4. {
    5.     /// <summary> Converts given layermask to layer number </summary>
    6.     /// <returns> Layer number </returns>
    7.     /// <assert> Mask represents multiple layers </assert>
    8.     public static int MaskToLayer( LayerMask mask )
    9.     {
    10.         var bitmask = mask.value;
    11.  
    12.         UnityEngine.Assertions.Assert.IsFalse( ( bitmask & ( bitmask - 1 ) ) != 0,
    13.         "MaskToLayer() was passed an invalid mask containing multiple layers." );
    14.  
    15.         int result = bitmask > 0 ? 0 : 31;
    16.         while( bitmask > 1 )
    17.         {
    18.             bitmask = bitmask >> 1;
    19.             result++;
    20.         }
    21.         return result;
    22.     }
    23.  
    24.     /// <summary> Converts layermask to layer number </summary>
    25.     internal static int ToLayer( this LayerMask mask )
    26.     {
    27.         return MaskToLayer( mask );
    28.     }
    29. }
    30.  
     
    Last edited: Apr 24, 2020
  30. vanderFeest

    vanderFeest

    Joined:
    Apr 12, 2017
    Posts:
    8
    I think this is just a tiny bit more readable (optimisation-wise it's the same amount of operations because of my 0 not using a register to compare, or could be that I'm miscounting?).
    If you just want to get the layer index of the first bit that is set (The Assertion is a bit expensive for my taste, but it depends how you're using it of course):

    Code (CSharp):
    1. using UnityEngine;
    2. public static class LayerMaskExtensions
    3. {
    4.     public static int FirstSetLayer(this LayerMask mask)
    5.     {
    6.         int value = mask.value;
    7.         if (value == 0) return 0;  // Early out
    8.         for (int l = 1; l < 32; l++)
    9.             if ((value & (1 << l)) != 0) return l;  // Bitwise
    10.         return -1;  // This line won't ever be reached but the compiler needs it
    11.     }
    12. }
     
    Babiole likes this.
  31. tgaldi

    tgaldi

    Joined:
    Oct 28, 2015
    Posts:
    102
    I think assertions are only in development builds.
     
  32. Eristen

    Eristen

    Joined:
    Jun 17, 2021
    Posts:
    19
    Thanks for the function Bastet! JFYI I used this code to get layer numbers so I can use them with camera.LayerCullDistances like so:

    Code (CSharp):
    1.  
    2. [SerializeField] LayerMask layersToCull;
    3.  
    4. [SerializeField] float cullDistance;
    5.  
    6. void Start()
    7. {
    8.   Camera camera = GetComponent<Camera>();
    9.  
    10.   List<int> layerNumbers = GetAllLayerMaskInspectorIndex(layersToCull);
    11.  
    12.   float[] distances = new float[32];
    13.  
    14.   foreach (int layerNumber in layerNumbers)
    15.   {
    16.     distances[layerNumber] = cullDistance;
    17.   }
    18.  
    19.   camera.layerCullDistances = distances;
    20. }
    21.  
     
  33. Bunny83

    Bunny83

    Joined:
    Oct 18, 2010
    Posts:
    4,003
    Since there's a need to iterate through all layer indices contained in a layer mask, here's a garbage free solution using a struct enumerable:
    Code (CSharp):
    1.  
    2.     public static class BitIteratorExtension
    3.     {
    4.         public static BitIterator IterateBitIndices(this int aMask)
    5.         {
    6.             return new BitIterator(aMask);
    7.         }
    8.         public static BitIterator IterateBitIndices(this LayerMask aMask)
    9.         {
    10.             return new BitIterator(aMask);
    11.         }
    12.     }
    13.  
    14.     public struct BitIterator
    15.     {
    16.         ulong m_Mask;
    17.         int m_Index;
    18.         public BitIterator(ulong aMask)
    19.         {
    20.             m_Mask = aMask;
    21.             m_Index = -1;
    22.         }
    23.         public BitIterator(long aMask) : this((ulong)aMask) { }
    24.         public BitIterator(uint aMask) : this((ulong)aMask) { }
    25.         public BitIterator(int aMask) : this((ulong)aMask) { }
    26.         public BitIterator(LayerMask aMask) : this((ulong)aMask.value) { }
    27.  
    28.         public BitIterator GetEnumerator() => this;
    29.         public int Current => m_Index;
    30.         public bool MoveNext()
    31.         {
    32.             if (m_Mask == 0)
    33.                 return false;
    34.             while ((m_Mask & 1) == 0)
    35.             {
    36.                 m_Mask >>= 1;
    37.                 ++m_Index;
    38.             }
    39.             m_Mask >>= 1;
    40.             return ++m_Index < 64;
    41.         }
    42.     }
    43.  
    The extension class is not really necessary, it just makes the usage a bit simpler. Now we can simply do this:

    Code (CSharp):
    1. // your layer mask
    2. int myLayerMask;
    3.  
    4. foreach(int layer in myLayerMask.IterateBitIndices())
    5. {
    6.     // do something.
    7. }
    Without the extension class, you could use the enumerable like this:

    Code (CSharp):
    1. // your layer mask
    2. int myLayerMask;
    3.  
    4. foreach(int layer in new BitIterator(myLayerMask))
    5. {
    6.     // do something.
    7. }
    Since BitIterator is a struct, there's no memory allocation at all. So it's handy when you just need to iterate through the layer indices. Of course, this can not be used with Linq or other fancy stuff, though the point was to avoid allocations ^^.

    This solution works for any bitmask up to ulong (64 bit). It starts with the lowest index 0 and goes up to max 63. Though it should be quite efficient. For example if only the lower 3 bits are set, it will just iterate 3 times and immediately finish as soon as the internal m_Mask is 0. The while loop is only used to skip "0" gaps.
     
  34. OrdinaryDev83

    OrdinaryDev83

    Joined:
    May 8, 2014
    Posts:
    23
    Code (CSharp):
    1. bool IsGOInMask(GameObject a, LayerMask m)
    2. {
    3.     return (1 >> a.layer & m) != 0;
    4. }
     
  35. Bunny83

    Bunny83

    Joined:
    Oct 18, 2010
    Posts:
    4,003
    I just came along this thread again. This code actually does not work. The bitshift is in the wrong direction. A right shift would shift the bits out to the right. The lowest bit is on the right side. So for any layer index greater than 0, this method would always return false. It should be
    Code (CSharp):
    1.     return ((1 << a.layer) & m) != 0;
    Note I added another pair of brackets. They are not necessary, because the
    <<
    operator has a higher priority than the bitwise
    &
    operator. However that's something most people do not remember so it's better to be explicit.
     
    oAzuehT and Kokowolo like this.
  36. Kokowolo

    Kokowolo

    Joined:
    Mar 26, 2020
    Posts:
    60
    Works perfect, great! Thanks!
     
    tbriz likes this.