10
\$\begingroup\$

I've finally figured out why my layer masks for my ground collision code weren't working. I was using NameToLayer() to get the layer I needed, but layer masks use bit shifting to actually set the layer mask value. This is extremely unusual and I don't see any reason why this isn't handled in the code behind. Why do we have to use code like this:

mask = 1 << LayerMask.NameToLayer("Default");

when something like this:

mask = LayerMask.NameToLayer("Default");

makes more intuitive sense and works similar to the rest of the Unity API?

\$\endgroup\$
1
  • 2
    \$\begingroup\$ Using the string version takes more processing power. Not to mention the string is internally an array which is a reference type and gets added to the garbage collector. \$\endgroup\$
    – Krythic
    Oct 27, 2016 at 10:54

2 Answers 2

4
\$\begingroup\$

This is extremely performant. That's all there is to it - comparing strings as the obvious example is slower by a factor of 10. And physics calculations have to be very optimized, so it's good that someone who knew what's going on wrote it this way.

So the obvious follow-up question is - why isn't this wrapped in a helper method to handle the conversion and bit-shifting. I think that no one actually got to it - I've rolled up my own nifty helper utility and that is the common practice.

\$\endgroup\$
3
  • 1
    \$\begingroup\$ The correct design choice by the Unity team would have been to use an integer as an indexer, instead of a string. I cringe when I think about how crazy the garbage collector is likely going with their current implementation, not to mention the string array allocations. \$\endgroup\$
    – Krythic
    Oct 28, 2016 at 3:35
  • 1
    \$\begingroup\$ Absolutely - any array is best referenced by integers. Integers could easily become humanly readable by a simple enum. \$\endgroup\$ Oct 28, 2016 at 13:40
  • \$\begingroup\$ A quick and dirty implementation works, but for bigger projects I use [Type Safe] (assetstore.unity3d.com/en/#!/content/35903). \$\endgroup\$ Oct 28, 2016 at 13:41
20
\$\begingroup\$

Using bit shifting allows you to take into account multiple layers in one physics operation:

 Physics.Raycast(ray, out hitInfo, Mathf.Infinity, layerMask )

Without bit shifting, you would be allowed to raycast in one layer and only one. While with bit shifting, you can raycast in multiple specific layers:

layerMask = (1 << LayerMask.NameToLayer("MyLayer1")) | (1 << LayerMask.NameToLayer("MyLayer2")) ;

You can also raycast in every layers except specific ones :

layerMask = (1 << LayerMask.NameToLayer("MyLayer1")) | (1 << LayerMask.NameToLayer("MyLayer2")) ;
layerMask = ~layerMask;

If you look at the "Layer manager" in Unity, layers can be seen as the indices of a simple one-dimension array.


EDIT : I've never seen it before, but the LayerMask class has a utility function to get the "computed" layer mask given the layers names :

Debug.Log( LayerMask.GetMask("UserLayerA", "UserLayerB") ) ;

Suppose UserLayerA and UserLayerB are the tenth and eleventh layers. These will have a User Layer values of 10 and 11. To obtain their layer mask value their names can be passed into GetMask. The argument can either be a list of their names or an array of strings storing their names. In this case the return value will be 2^10 + 2^11 = 3072.

Link to documentation : https://docs.unity3d.com/ScriptReference/LayerMask.GetMask.html

\$\endgroup\$
7
  • 2
    \$\begingroup\$ You should use bitwise OR | instead of integer addition + when making union of masks, integer addition might produce unexpected behavior. \$\endgroup\$
    – wondra
    Oct 27, 2016 at 11:20
  • 1
    \$\begingroup\$ But then again, they could have done that internally and provided a method like LayerMask.NamesToLayers(params string[] layerNames) \$\endgroup\$
    – QBrute
    Oct 27, 2016 at 12:11
  • \$\begingroup\$ Good point @wondra ! ;) - Yes, they could have done this QBrute, but it's far more flexible to use bit shiffting operation if you want to dynamically change the layer mask (adding, removing layer, inverting, ...) \$\endgroup\$
    – Hellium
    Oct 27, 2016 at 12:34
  • \$\begingroup\$ Bitwise operations are also extremely fast. They are an excellent way to composite large binary data. \$\endgroup\$
    – Gusdor
    Oct 27, 2016 at 13:20
  • \$\begingroup\$ This doesn't really answer the question of why the method doesn't just straight up return a layer mask instead of a layer number though. \$\endgroup\$
    – Cubic
    Oct 27, 2016 at 16:50

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .