11
\$\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\$ Commented 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\$ Commented 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\$ Commented 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\$ Commented 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\$ Commented 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\$ Commented 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\$ Commented Oct 27, 2016 at 12:34
  • \$\begingroup\$ Bitwise operations are also extremely fast. They are an excellent way to composite large binary data. \$\endgroup\$ Commented 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\$ Commented Oct 27, 2016 at 16:50

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.