How to use layer masks (layermask) and what is 1

Good time of day!

I understand the work of Unity, look at the lessons, some scripts, and sometimes I come across a construction of the form 1 << layer, where layer is an integer. I find this more often in methods related to physics, for example Raycast:

public int groundLayer = 5; 

void SomeMethod() {
    // do smth...
    if (Physics.RaycastAll(transform.position, transform.forward, 100.0f, 1 << groundLayer).Length > 0) { 
        ...
    }
    // do smth...
}

And not just with physics:

LayerMask Collisionmask;
int layer = 5;

void SomeMethod() {
    // do smth...
    if ((Collisionmask.value & (1 << layer)) == 0) {
        ...
    }
    // do smth...
}

I read that these are some kind of masks. They point to the layers. But I do not understand what these masks are, how to use them, and what do the layers have to do with them? What is 1 << layer, for example?

Can someone explain in detail? Thanks.

1 answers

Stock up on popcorn and cola (pies and tea). It will be long, but informative. ))

What is it and what is it for?

In Unity, how are we all (or not just all of us) we know that there are layers. To refresh your memory of what it is (and if you don't know, then read and familiarize yourself), you can in documentation. They are used in different ways. For example, in 2D, you can use them to identify and separate the background/middle/foreground. Something will be at the back, something at the front. In more complex games, there may be more layers and objects can be grouped by these layers. For example, you can make a layer for NPC and select a layer from it for hostile NPCs - Enemies and others.

In general, manipulations with the layerMask (mask) allow you to find out whether the object being checked (GameObject) is in the specified layer or not (it does not matter what kind of object it is and how it is checked). Depending on the result of the check, perform the planned actions.

Consider, for example, Raycast. If we turn to documentation, then you can see that the method looks like this:

public static RaycastHit2D Raycast(Vector2 origin, Vector2 direction, float distance = Mathf.Infinity, int layerMask = DefaultRaycastLayers, float minDepth = -Mathf.Infinity, float maxDepth = Mathf.Infinity);

One of the parameters: int layerMask = DefaultRaycastLayers. It works like this: from a certain point in a certain direction, a ray is released (let's say this is such magic at a distance), this ray falls on some obstacle (object) and, for example, if this object is in the layer Enemies - hit it. We will talk about the example (Collisionmask.value & (1 << layer)) == 0 below. But the essence of it is similar: define belonging by mask > do something depending on...

I hope the essence is clear, let's go further.

How it works.

Number of layers in Unity: 32. They are numbered from 0 to 31.

i1

LayerMask (mask) - is represented as a 32-bit integer:

0000 0000 0000 0000 0000 0000 0000 0000

I have separated every 4 bits for better readability.

The mask uses its 32 bits to represent the various flags (the state of the object, for example: 1-enabled, 0-disabled). In this particular case, each flag (bit) represents a specific layer. Let's take the first 8 layers for parsing and write down the binary representation of each layer (The first bit (the lowest) is on the right side).

слой №    Двоичное    Десятичное
----------------------------------
Layer 0 = 0000 0001 = 1
Layer 1 = 0000 0010 = 2
Layer 2 = 0000 0100 = 4
Layer 3 = 0000 1000 = 8
Layer 4 = 0001 0000 = 16
Layer 5 = 0010 0000 = 32
Layer 6 = 0100 0000 = 64
Layer 7 = 1000 0000 = 128

This is what layers are in binary representation. As you can see, a specific layer is defined by one specific bit.

Now we try to understand for example, if ((Collisionmask.value & (1 << layer)) == 0).

We see the variable layer, which is an integer. This variable is layer number (0, 1, 2, 3, 4 ...), NOT decimal and NOT binary representation. Collisionmask in the inspector is a drop-down list with layers:

i2

And Collisionmask.value, in turn, is a binary representation of the combination of the selected layers (since we can choose not one but several layers). For understanding: for example, if we want to create a mask for layer #2 and # 5, the binary representation will be:

0010 0100  // #2,#5

Or for example layers #1, #2, #5, #6 they will consist of:

0110 0110 // #1,#2,#5,#6

What do we want? We want to find out whether the layer we are checking layer is included in this mask? And depending on this, do something. In the example above, we want to go into the condition and do something if our object located in layer 5 is not included in the specified mask.

Admit LayerMask = 0110 0110; // #1,#2,#5,#6, and the number of our layer is int layer = 5;

You need to bring layer 5 to its binary representation, corresponding in layers. Let's look at the sign at the top. Layer 5 has a decimal representation of 32. It, in turn, in binary representation is 0010 0000. It is obtained by applying the bitwise left shift operator << relative to the lowest bit (0000 0001) by exactly 5 values (fantastic!). Shift the unit 5 times left:

i3

As you can see, the number 0010 0000 turned out and it is really included in our mask 0110 0110, because the mask contains a unit at position number 6 on the right, and layer 5 has a unit at the same position. If you don't take my word for it, then it's easy to check: you need to perform a bitwise multiplication (bitwise and "&") between the mask and the value being checked. This, by the way, is done in the line Collisionmask.value & (1 << layer)

0110 0110 // #1,#2,#5,#6
& (and)
0010 0000 // #5
---------
0010 0000

We see what goes into the mask. If I hadn't, then bitwise multiplication would give the result 0. As an example of the proof, we can check whether layer 7 is included in this mask or not. Shift the lowest bit 7 positions to the left:

i3.2

We get 1000 0000. Checking

0110 0110 // #1,#2,#5,#6
& (and)
1000 0000 // #7
---------
0000 0000

See 0. Not included. And if it is not included, then our condition from above is triggered:

if ((Collisionmask.value & (1 << layer)) == 0) => if (0 == 0)  => true 

So we go into the condition and do what we need. This is what we were looking for.


Extension 1

In addition to checking a specific layer for entering the mask, you can check, on the contrary, all layers, except specified. You only need to use, so to speak, the inversion operator ( ~ ). It changes in the bit representation of the operand 0 to 1, and 1 to 0.Thus, ~00000010 will be equal to 11111101, which will test every layer except #2. If you want to exclude several layers, you will need to combine the masks using the "OR" operator:

int layer1 = 3;
int layer2 = 5;
int layermask = ~((1 << layer1) | (1 << layer2)) // НЕПРАВИЛЬНО: ~(1 << layer1) | ~(1 << layer2)

Extension 2

In fact, you don't always need to write 1 << что-то. For example, in Raycast , you can pass Collisionmask.value to the parameter, where Collisionmask has the type LayerMask, and it, in turn, looks like a drop-down list in the inspector (see image #2). Very convenient.

Also, do not hardcode numbers, i.e. do not write 1 << 7 or 1 << 5, because you can forget what it is for 5 and 7. In order to find out the desired number of the layer, you can use LayerMask.NameToLayer, example:

int npcLayer = LayerMask.NameToLayer("NPCLayer");   
int npcMask = 1 << npcLayer;

And the same thing with "group" masks:

int npcGoodLayer = LayerMask.NameToLayer("NPCGood");
int friendsLayer = LayerMask.NameToLayer("Friends");
int npcGoodMask = 1 << npcGoodLayer;
int friendsMask = 1 << friendsLayer;
int finalmask = npcGoodMask | friendsMask; // Or, (1 << npcGoodLayer) | (1 << friendsLayer)

Supplement 3

In fact, this construction 1 << ... is found not only in Unity. This is a well-known practice in general in programming. For example, a manufacturer has a product, an application, and it provides an API for other programmers.

API-a set of ready-made classes, procedures, functions, structures, and constants, provided by an application (library, service) or operating system for use in external software products. It is used by programmers when writing various applications. (c) Wikipedia

The API may have access restrictions of various kinds. The programmer specifies the mask to which he wants to access, and the application server checks this and, in case of a mismatch in the mask, issues a warning or something else. The simplest example of Vkontakte and his API access rights

i4

It specifies a decimal representation, but it does not change the essence.


 44
Author: Алексей Шиманский, 2017-12-16 12:23:23