How to Ignore Collisions Between a Layer and a LayerMask?
This was a question I had, and couldn't find an answer for it. However, after some thought, I finally came up with a solution. I decided to post the question, and an answer to my question, for anyone else who ever has my same problem, as the answer was not apparent. Though, simultaneously, I am also looking for an improvement upon my answer.
I'm trying to ignore collision between a single layer, and a set of layers that I've defined in a LayerMask.
I have a LayerMask variable with whom I've marked a set of layers as being inapplicable for use. Like one might when making a mask for a raycast, to filter out results.
There is a Physics.IgnoreLayerCollision(int, int), but not really a Physics.IgnoreLayerCollision(int, LayerMask), or anything similar. Actually, the latter is syntactically acceptable as a LayerMask is an unsigned integer (it's just a just a 32-bit string), but does not actually do anything useful. It will actually throw an error if you have more than the 4th index of the LayerMasked marked true (b/c 2^5 = 32 and it only allows for input from 0-31, I believe).
So I'm looking for a suggestion on how to either decode the bit string and get an array of numbers representing the layers I want to ignore, or an alternative means of ignoring collision from one layer to a set of layers.
So hopefully I didn't miss a really obvious solution, but if anyone had anything better than this, then please share! Otherwise, hopefully at least one person finds this useful (see my answer below, in the answer section).
Answer by zckhyt · Jan 22, 2016 at 02:50 PM
My Solution
As mentioned, a LayerMask is a 32-bit string. Each bit stores a 1 if that layer is selected, or a 0 if it is not. Each Layer (index n) increases the value of the string by 2^n. E.g., if you mark the first layer as true, it increases the value of the LayerMask by 2^0 = 1.
So if we look at the value of the string, we can tell if it has a layer n marked as true if that n bit is 1. This is done by bit-shifting. As we shift the string n-bits to the right, if the value is still greater-than-zero, that means our current index is a 1. E.g., if the 31st index of a layermask (the highest index of a layermask) is true, that means to 32nd bit of the string is a 1 (the string goes from 1-32, whereas our layers go from 0-31). So our string is shifted 31-times to the right, which leaves us 1, which is greater-than-zero.
Whenever we do this, we need to subtract 1 << n from our bitstring, so we don't the bits from a previous layer in the continuing process.
So here is what I did for my player, who was in the "Player" layer, when I wanted to blacklist some layers with objects I didn't want my player to collide with:
LayerMask someLayerMask;
int playerLayer = LayerMask.NameToLayer("Player");
uint bitstring = (uint)someLayerMask.value;
for (int i = 31; bitstring > 0; i--)
if ((bitstring >> i) > 0)
{
bitstring = ((bitstring << 32 - i) >> 32 - i);
Physics.IgnoreLayerCollision(playerLayer, i);
}
We can break if the bitstring ever is equal to 0, because then there can obviously be nothing left.
A uint data type is used to represent the bitstring as a normal integer's leading-bit signifies if the number is positive or negative. This would mean that if you mark the last layer (and the last layer only), your bitstring value would be -2^31. Which the loop ignores, and would result in it missing the other marked layers. uint is C#'s version of the unsigned integer, which uses the entire 32-bits to represent a positive integer up to 2^32 - 1.
If the LayerMask is a public variable, you can edit it in the Unity Editor. Otherwise, you can set a bit to true by: someLayerMask = ~((1 << n1) | (1 << n2) | ... );
where n1, n2, ... are indices you want to mark true.
Old post I know, but this was very helpful! In the off chance anyone's still here... this solution works perfectly called from Awake() or Start() but if I put it in a method and call from Update() it completely locks up Unity while running a test. Same exact code. Any ideas?