Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

can i write bitmasks in decimal?

Discussion in 'Scripting' started by Nanako, Nov 21, 2014.

  1. Nanako

    Nanako

    Joined:
    Sep 24, 2014
    Posts:
    1,047
    say i want to raycast against the first thirteen layers, and none after that. i set my calculator to binary, i punch 1111111111111 into it, and switch back to decimal. 8191

    so can i just enter that as a layer mask and expect it to work?
     
  2. Eric5h5

    Eric5h5

    Volunteer Moderator Moderator

    Joined:
    Jul 19, 2006
    Posts:
    32,401
    Generally better if you use a public LayerMask variable rather than hardcoding numbers.

    Code (javascript):
    1. var raycastMask : LayerMask;
    Then use "raycastMask.value" in Raycast.

    --Eric
     
  3. Nanako

    Nanako

    Joined:
    Sep 24, 2014
    Posts:
    1,047
    You're advising me against hardcoding? I'm not that dumb :p I just mean, can i set it to a decimal value rather than bothering with barrel shifts and such

    Code (CSharp):
    1. public LayerMask raycastMask = 8191;
     
  4. shaderop

    shaderop

    Joined:
    Nov 24, 2010
    Posts:
    942
    Sure you can. If you have something against readability or need to hand obfuscate your code, then go for it.
     
    Last edited: Nov 21, 2014
  5. Eric5h5

    Eric5h5

    Volunteer Moderator Moderator

    Joined:
    Jul 19, 2006
    Posts:
    32,401
    Unfortunately binary literals aren't possible (in Unity, yet); I'd rather see "0b11111111111110000000000000000000" than "8191" since the former makes the meaning more obvious. Not actually sure what the clearest way to write it would be..."(2<<13)-1"?

    --Eric
     
  6. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,513
    I create a static class where I store all my layers and masks as constants:

    Code (csharp):
    1.  
    2. using UnityEngine;
    3.  
    4. using com.spacepuppy;
    5.  
    6. namespace com.apoc
    7. {
    8.  
    9.     public class Constants
    10.     {
    11.  
    12.         #region Tags
    13.  
    14.         //inherited from com.spacepuppy
    15.         public const string TAG_MULTITAG = SPConstants.TAG_MULTITAG;
    16.         public const string TAG_ROOT = SPConstants.TAG_ROOT;
    17.         public const string TAG_PLAYER = SPConstants.TAG_PLAYER;
    18.         //custom to this game
    19.         public const string TAG_NPC = "NPC";
    20.         public const string TAG_DEBRIS = "Debris";
    21.  
    22.         public const string TAG_ENTITYEVENTRIGGER = "EntityEventTrigger";
    23.  
    24.         #endregion
    25.  
    26.         #region Layers
    27.  
    28.         public const int LAYER_DEFAULT = 0;
    29.         public const int LAYER_WATER = 4;
    30.      
    31.         //mobile layers
    32.         public const int LAYER_PLAYER = 8;
    33.         public const int LAYER_NPC = 9;
    34.         public const int LAYER_DEBRIS = 10;
    35.         public const int LAYER_PROJECTILE = 14;
    36.  
    37.         //terrain layers
    38.         public const int LAYER_STATIC_DEPRECATED = 11;
    39.         public const int LAYER_DYNAMIC_DEPRECATED = 12;
    40.         public const int LAYER_BGRUN = 13;
    41.         public const int LAYER_NONCLIMBABLE = 18;
    42.  
    43.         public const int LAYER_EFFECT = 15;
    44.  
    45.         //hitbox layers
    46.         public const int LAYER_HITBOX = 16;
    47.         public const int LAYER_STRIKEBOX = 17;
    48.  
    49.         public const int LAYER_RAGDOLLSELFCOLLISION = 30;
    50.  
    51.         public const int MASK_DEFAULT = 1 << LAYER_DEFAULT;
    52.  
    53.         public const int MASK_PLAYER = 1 << LAYER_PLAYER;
    54.         public const int MASK_NPC = 1 << LAYER_NPC;
    55.         public const int MASK_DEBRIS = 1 << LAYER_DEBRIS;
    56.         public const int MASK_PROJECTILE = 1 << LAYER_PROJECTILE;
    57.         public const int MASK_MOBILELAYERS = MASK_PLAYER | MASK_NPC | MASK_DEBRIS | MASK_PROJECTILE;
    58.         public const int MASK_PUSHABLEMOBILELAYERS = MASK_NPC | MASK_DEBRIS | MASK_PROJECTILE;
    59.  
    60.         public const int MASK_STATIC_DEPRECATED = 1 << LAYER_STATIC_DEPRECATED;
    61.         public const int MASK_DYNAMIC_DEPRECATED = 1 << LAYER_DYNAMIC_DEPRECATED;
    62.         public const int MASK_BGRUN = 1 << LAYER_BGRUN;
    63.         public const int MASK_NONCLIMBABLE = 1 << LAYER_NONCLIMBABLE;
    64.         public const int MASK_SURFACE = MASK_DEFAULT | MASK_NONCLIMBABLE | MASK_STATIC_DEPRECATED | MASK_DYNAMIC_DEPRECATED;
    65.         public const int MASK_GRABBABLE = MASK_DEFAULT | MASK_STATIC_DEPRECATED | MASK_DYNAMIC_DEPRECATED;
    66.  
    67.         /// <summary>
    68.         /// This pertains the surfaces that can be considered the 'ground' and walked on via 'GroundingResolver' and 'Platforming' movement styles
    69.         /// </summary>
    70.         public const int MASK_CLIMBABLE = MASK_DEFAULT | MASK_STATIC_DEPRECATED | MASK_DYNAMIC_DEPRECATED;
    71.  
    72.         public const int MASK_HITBOX = 1 << LAYER_HITBOX;
    73.         public const int MASK_STRIKEBOX = 1 << LAYER_STRIKEBOX;
    74.  
    75.         public const int MASK_EFFECT = 1 << LAYER_EFFECT;
    76.  
    77.         public const int MASK_RAGDOLLSELFCOLLISION = 1 << LAYER_RAGDOLLSELFCOLLISION;
    78.  
    79.         #endregion
    80.  
    81.         #region Methods
    82.  
    83.         /// <summary>
    84.         /// Returns the primary layer for some mob type.
    85.         /// </summary>
    86.         /// <param name="tp"></param>
    87.         /// <returns></returns>
    88.         public static int GetPrimaryLayerForMobType(EntityType tp)
    89.         {
    90.             switch (tp)
    91.             {
    92.                 case EntityType.Player:
    93.                     return LAYER_PLAYER;
    94.                 case EntityType.NPC:
    95.                     return LAYER_NPC;
    96.                 case EntityType.Debris:
    97.                     return LAYER_DEBRIS;
    98.                 default:
    99.                     return LAYER_DEFAULT;
    100.             }
    101.         }
    102.  
    103.         /// <summary>
    104.         /// Gets the mob type for a layer.
    105.         /// </summary>
    106.         /// <param name="layer"></param>
    107.         /// <returns></returns>
    108.         public static EntityType GetMobTypeForLayer(int layer)
    109.         {
    110.             if (layer == LAYER_PLAYER)
    111.             {
    112.                 return EntityType.Player;
    113.             }
    114.             else if (layer == LAYER_NPC)
    115.             {
    116.                 return EntityType.NPC;
    117.             }
    118.             else if (layer == LAYER_DEBRIS)
    119.             {
    120.                 return EntityType.Debris;
    121.             }
    122.             else
    123.             {
    124.                 return EntityType.None;
    125.             }
    126.         }
    127.  
    128.         #endregion
    129.  
    130.     }
    131.  
    132. }
    133.  
    Then, even if I don't have the constant defined, building a mask is easy:

    Code (csharp):
    1.  
    2. var mask = Constants.MASK_PLAYER | Constants.MASK_NPC;
    3.  
    Much more readable in my opinion.

    As for the original question. Yes, you can write the value in decimal notation.
     
    KelsoMRK likes this.
  7. Jessy

    Jessy

    Joined:
    Jun 7, 2007
    Posts:
    7,325
    Swift allows underscores in literals, e.g. 0b1111_1111_1111_1000__0000_0000_0000_0000, or 0xFFF8_0000. I'm unclear on what's going on with this in C#.
     
  8. Eric5h5

    Eric5h5

    Volunteer Moderator Moderator

    Joined:
    Jul 19, 2006
    Posts:
    32,401
    That's pretty nice. Actually, I wouldn't mind seeing Swift language support in Unity.

    --Eric
     
    Jessy likes this.
  9. Jessy

    Jessy

    Joined:
    Jun 7, 2007
    Posts:
    7,325
    Indeed. Swift doesn't have an equivalent of multicast delegates yet, but aside from that, I think it's phenomenally good.

    I am sorry about the off-topicness.
     
  10. shaderop

    shaderop

    Joined:
    Nov 24, 2010
    Posts:
    942
    First time I saw that was an Eiffel, which made its debut way back in 1985 according to Wikipedia. It also had contracts, invariant, pre- and post- conditions on method calls, and multiple inheritance that actually made sense. Yet another demonstration that being first or being best has very little to do with success.
     
  11. KelsoMRK

    KelsoMRK

    Joined:
    Jul 18, 2010
    Posts:
    5,539
    This is exactly what I do as well.
     
  12. Nanako

    Nanako

    Joined:
    Sep 24, 2014
    Posts:
    1,047
    Now hold on a second there. I think you have a pretty warped definition of easy.
    Code (CSharp):
    1. LayerMask mask = Constants.MASK_LAYERNAME | Constants.MASK_LAYERNAME | Constants.MASK_LAYERNAME | Constants.MASK_LAYERNAME | Constants.MASK_LAYERNAME | Constants.MASK_LAYERNAME | Constants.MASK_LAYERNAME | Constants.MASK_LAYERNAME | Constants.MASK_LAYERNAME | Constants.MASK_LAYERNAME | Constants.MASK_LAYERNAME | Constants.MASK_LAYERNAME | Constants.MASK_LAYERNAME;
    Imagine those with proper names. But you see the problem, right? it's a lot more effort to type Constants.whatever in an IDE, than it is to type 1 or 0 in a calculator. With my method i can look at the layers list from the top, and hit the appropriate binary input for whether i do/don't want to collide with that layer, until i reach the point on the list where everything farther is 0. then hit Dec and paste in the result.

    massive timesaver imo
     
  13. KelsoMRK

    KelsoMRK

    Joined:
    Jul 18, 2010
    Posts:
    5,539
    It might be easier until you have to change that layer or mask to something different and then hunt down *every* instance of it in your project.

    And there's nothing saying your crazy mask example couldn't be a constant as well in which case you would only write it once and then refer to it in a human readable way.

    Might be faster to type (once) but I'd rather just look at something and know what it means in the context of my project than pull out a calculator every time in an effort to prove how clever I am.
     
  14. Nanako

    Nanako

    Joined:
    Sep 24, 2014
    Posts:
    1,047
    Oh but hey, there is this little thing if you set it public


    You know, that seems easier than any code solution. Or it would be if the menu didn't dismiss with EVERY SINGLE CLICK UGH
     
  15. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,513
    Note in my example I have masks that are commonly used already turned into their own mask as well.

    Code (csharp):
    1.  
    2. public const int MASK_SURFACE = MASK_DEFAULT | MASK_NONCLIMBABLE | MASK_STATIC_DEPRECATED | MASK_DYNAMIC_DEPRECATED;
    3.  
    So I can just say:

    Code (csharp):
    1.  
    2. LayerMask mask = Constants.MASK_SURFACE; //a mask already made up of all the layers that are considered surfaces
    3.  
    It's useful for reusability, in that if any masks change, you just change them in the Constants class. Rather than hunt down every place that uses it.

    Also it's more readable. If I read:

    Constants.MASK_NPC | Constants.MASK_PLAYER

    I know I have a mask that consists of the NPC and Player layers. I don't have to just know what the number is... I know. It's there in the code.


    I'm sorry but this seems more readable to me:

    Code (csharp):
    1.  
    2. if (Physics.Raycast(ray, float.PositiveInfinity, Constants.SURFACE))
    3. {
    4. ...do stuff
    5. }
    6.  
    Rather than
    Code (csharp):
    1.  
    2. if (Physics.Raycast(ray, float.PositiveInfinity, 8192))
    3. {
    4.    ... do stuff
    5. }
    6.  

    I'm going to put this out there.

    I've been professionally writing software for almost a decade now. This is a standard... I did not make this up. This is what people DO, and have for decades.

    It works.

    It's proven.
     
    KelsoMRK likes this.
  16. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,513
    This is only useful for when you're in a designer. You don't always get to be in the designer.

    Again though... if you change any of the layers... this thing buggers up as well and you have to go around and fix every place.

    Try it, set a LayerMask on a component attached to a gameobject, then go edit the layers. Move them around. Go back, and you'll see your mask is now wrong.
     
  17. hpjohn

    hpjohn

    Joined:
    Aug 14, 2012
    Posts:
    2,190
    More editor scripts!