🤔

Bitmasks and LayerMasks#

Bitmasks#

A bitmask is a representation of many states using a single value.

A bit is a 0 or a 1. In a bitmask 0 is an inactive layer, and a 1 is active.
As int is a 32 bit value, it can be used to represent 32 toggleable layers.

LayerMasks#

The LayerMask struct describes a bitmask of layers used by physics and rendering APIs in Unity. It's implicitly converted to int when using physics methods.

information

Because a LayerMask is a bitmask, it shouldn't be treated as a single layer.

Interactive diagram#

Bitmask
0
LayerMask

Select the bits and interact with the dropdown to see their relationship.

Creating masks#

To create a bitmask with a specific layer enabled, shift a single bit over to the position that matches the layer's index.

To create a mask with layer 5 active:

  1. Create int with the first bit enabled, a 1.
  2. Shift that bit over 5 places to the 6th index (layers are 0 indexed so this is layer 5).
int mask = 1 << 5;
//    1 : 000001
// mask : 100000
Note that the first bit (the least significant bit) is the rightmost bit, similar to a decimal integer.

A LayerMask can be serialized to easily author masks in the Inspector based on Unity's layers.

[SerializeField] private LayerMask _mask;

Or

public LayerMask Mask;

You can also create a LayerMask in code with LayerMask.GetMask.

Declaring custom masks#

Generally masks are declared as an enum so they can be easily referenced and manipulated by name.
The Flags attribute indicates the enum is a bitmask, and modifies ToString to return more relevant values.

Enums are int values by default, but any integral numeric type can be used when defining an enum.

Example#

[Flags]
public enum Days
{
None      = 0,      // 00000000, 0
Monday    = 1 << 0, // 00000001, 1
Tuesday   = 1 << 1, // 00000010, 2
Wednesday = 1 << 2, // 00000100, 4
Thursday  = 1 << 3, // 00001000, 8
Friday    = 1 << 4, // 00010000, 16
Saturday  = 1 << 5, // 00100000, 32
Sunday    = 1 << 6, // 01000000, 64

// See: Combining Masks
Weekdays = Monday | Tuesday | Wednesday | Thursday | Friday, // 00011111
Weekend = Saturday | Sunday,                                 // 01100000
Everyday = Weekdays | Weekend                                // 01111111
}
We could use an underlying type of byte, as the range is known to be less than 8 layers. Optimisations like these may decrease performance due to memory alignment.

Combining masks#

To combine a mask we perform a logical or. If a bit is enabled (1) in either one mask or the other, it will be in our result.

Examples#

Creating a mask where both layer 5 and 8 are enabled.

int mask = (1 << 5) | (1 << 8);
// (1 << 5) : 000100000
// (1 << 8) : 100000000
//     mask : 100100000

Creating a mask that includes Monday and the weekend.

var longWeekend = Days.Monday | Days.Weekend;
//    Days.Monday : 00000001
//   Days.Weekend : 01100000
//    longWeekend : 01100001

Inverting a mask#

Bitmasks can be inverted with the bitwise complement operator, which will reverse each bit.

layerMask = ~layerMask;
//  layerMask : 00000000000000000000000100100000
// ~layerMask : 11111111111111111111111011011111

Removing from a mask#

  1. Create an inverted mask using the ~ operator, where the layer to remove is now a 0, the rest 1's.
  2. Perform a logical and. If a bit is enabled (1) in one mask and the other, it will be in our result.
    Because the layer is a 0, it will not be in both masks, and is removed.

Examples#

Removing layer 8 from a mask.

mask &= ~(1 << 8);
//              mask : 00000000000000000000000100100000
//         ~(1 << 8) : 11111111111111111111111011111111
// mask &= ~(1 << 8) : 00000000000000000000000000100000

Removing Wednesday from a mask.

var mask = Days.Everyday;
var maskWithoutWednesday = mask & ~Days.Wednesday;
//        Days.Everyday : 01111111
//      ~Days.Wednesday : 11111011
// maskWithoutWednesday : 01111011

Checking if a mask...#

Contains a layer (contains any)#

If a mask contains a layer, it and a mask describing that layer mustn't be 0.

  1. Create a mask with our layer.
  2. Perform a logical and.
  3. Compare the result with 0.
if ((mask & (1 << layer)) != 0)
{
    // mask contains layer.
}

This logic can be used to conservatively check if values are in a mask.

Contains another mask (contains all)#

If a mask contains all the values of another mask, the values in it and the other mask must be the mask you expect.

  1. Perform a logical and.
  2. Compare the result with the mask you expect.
if ((mask & queryMask) == queryMask)
{
    // mask contains queryMask.
}

When dealing with an enum, you could alternatively choose to use the Enum.HasFlag method, note that this may box.

Toggling mask values#

The logical exclusive or (XOR) will not alter values not present in the second operand, while toggling the mask when a match is found.

Examples#

Toggling layer 8 (in this case, off).

mask ^= 1 << 8;
//           mask : 00000000000000000000000100100000
//         1 << 8 : 00000000000000000000000100000000
// mask ^= 1 << 8 : 00000000000000000000000000100000

Toggling Wednesday (in this case, off).

var maskToggledWednesday = mask ^ Days.Wednesday;
//        Days.Everyday : 01111111            Days.Weekend : 01100000
//       Days.Wednesday : 00000100          Days.Wednesday : 00000100
// maskToggledWednesday : 01111011    maskToggledWednesday : 01100100

Creating a mask with all values enabled#

Invert 0 to enable all bits.

var enabledMask = ~0;

Note that this value may not be the same as "Everything" or "All", and different implementations may or may not interpret it that way. Note how Days.Everyday is 01111111, not all bits are enabled. Your implementations may need to consider this.

Setting a mask using another mask#

If setOn is true then ~mask will invert the original mask, & Days.Weekend isolates the bits we want to enable, giving us a mask that only contains the weekend bits that aren't enabled in the original mask. We then toggle the mask with this value.

var weekendCorrectedMask = mask ^ ((setOn ? ~mask : mask) & Days.Weekend);
//                 mask : 01001001  setOn: true
//                ~mask : 10110110
//       & Days.Weekend : 00100000
// weekendCorrectedMask : 01101001

If setOn is false then mask does nothing, & Days.Weekend isolates the bits we want to disable, giving us a mask which contains the weekend bits that are enabled in the original mask. We then toggle the mask with this value.

var weekendCorrectedMask = mask ^ ((setOn ? ~mask : mask) & Days.Weekend);
//                 mask : 01001001  setOn: false
//       & Days.Weekend : 01000000
// weekendCorrectedMask : 00001001

Logic tables#

& • Logical AND operator#

A & B 0 1
0 0 0
1 0 1

| • Logical OR operator#

A | B 0 1
0 0 1
1 1 1

^ • Logical exclusive OR operator#

A ^ B 0 1
0 0 1
1 1 0