9
\$\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\$
  • 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 '16 at 10:54
2
\$\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\$
  • 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 '16 at 3:35
  • 1
    \$\begingroup\$ Absolutely - any array is best referenced by integers. Integers could easily become humanly readable by a simple enum. \$\endgroup\$ – Jordan Georgiev Oct 28 '16 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\$ – Jordan Georgiev Oct 28 '16 at 13:41
18
\$\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\$
  • 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 '16 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 '16 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 '16 at 12:34
  • \$\begingroup\$ Bitwise operations are also extremely fast. They are an excellent way to composite large binary data. \$\endgroup\$ – Gusdor Oct 27 '16 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 '16 at 16:50

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.