say i want to raycast against the first thirteen layers, and none after that. i set my calculator to binary, i punch 1111111111111 into it, and switch back to decimal. 8191
so can i just enter that as a layer mask and expect it to work?
say i want to raycast against the first thirteen layers, and none after that. i set my calculator to binary, i punch 1111111111111 into it, and switch back to decimal. 8191
so can i just enter that as a layer mask and expect it to work?
Generally better if you use a public LayerMask variable rather than hardcoding numbers.
var raycastMask : LayerMask;
Then use “raycastMask.value” in Raycast.
–Eric
You’re advising me against hardcoding? I’m not that dumb I just mean, can i set it to a decimal value rather than bothering with barrel shifts and such
public LayerMask raycastMask = 8191;
Sure you can. If you have something against readability or need to hand obfuscate your code, then go for it.
Unfortunately binary literals aren’t possible (in Unity, yet); I’d rather see “0b11111111111110000000000000000000” than “8191” since the former makes the meaning more obvious. Not actually sure what the clearest way to write it would be…“(2<<13)-1”?
–Eric
I create a static class where I store all my layers and masks as constants:
using UnityEngine;
using com.spacepuppy;
namespace com.apoc
{
public class Constants
{
#region Tags
//inherited from com.spacepuppy
public const string TAG_MULTITAG = SPConstants.TAG_MULTITAG;
public const string TAG_ROOT = SPConstants.TAG_ROOT;
public const string TAG_PLAYER = SPConstants.TAG_PLAYER;
//custom to this game
public const string TAG_NPC = "NPC";
public const string TAG_DEBRIS = "Debris";
public const string TAG_ENTITYEVENTRIGGER = "EntityEventTrigger";
#endregion
#region Layers
public const int LAYER_DEFAULT = 0;
public const int LAYER_WATER = 4;
//mobile layers
public const int LAYER_PLAYER = 8;
public const int LAYER_NPC = 9;
public const int LAYER_DEBRIS = 10;
public const int LAYER_PROJECTILE = 14;
//terrain layers
public const int LAYER_STATIC_DEPRECATED = 11;
public const int LAYER_DYNAMIC_DEPRECATED = 12;
public const int LAYER_BGRUN = 13;
public const int LAYER_NONCLIMBABLE = 18;
public const int LAYER_EFFECT = 15;
//hitbox layers
public const int LAYER_HITBOX = 16;
public const int LAYER_STRIKEBOX = 17;
public const int LAYER_RAGDOLLSELFCOLLISION = 30;
public const int MASK_DEFAULT = 1 << LAYER_DEFAULT;
public const int MASK_PLAYER = 1 << LAYER_PLAYER;
public const int MASK_NPC = 1 << LAYER_NPC;
public const int MASK_DEBRIS = 1 << LAYER_DEBRIS;
public const int MASK_PROJECTILE = 1 << LAYER_PROJECTILE;
public const int MASK_MOBILELAYERS = MASK_PLAYER | MASK_NPC | MASK_DEBRIS | MASK_PROJECTILE;
public const int MASK_PUSHABLEMOBILELAYERS = MASK_NPC | MASK_DEBRIS | MASK_PROJECTILE;
public const int MASK_STATIC_DEPRECATED = 1 << LAYER_STATIC_DEPRECATED;
public const int MASK_DYNAMIC_DEPRECATED = 1 << LAYER_DYNAMIC_DEPRECATED;
public const int MASK_BGRUN = 1 << LAYER_BGRUN;
public const int MASK_NONCLIMBABLE = 1 << LAYER_NONCLIMBABLE;
public const int MASK_SURFACE = MASK_DEFAULT | MASK_NONCLIMBABLE | MASK_STATIC_DEPRECATED | MASK_DYNAMIC_DEPRECATED;
public const int MASK_GRABBABLE = MASK_DEFAULT | MASK_STATIC_DEPRECATED | MASK_DYNAMIC_DEPRECATED;
/// <summary>
/// This pertains the surfaces that can be considered the 'ground' and walked on via 'GroundingResolver' and 'Platforming' movement styles
/// </summary>
public const int MASK_CLIMBABLE = MASK_DEFAULT | MASK_STATIC_DEPRECATED | MASK_DYNAMIC_DEPRECATED;
public const int MASK_HITBOX = 1 << LAYER_HITBOX;
public const int MASK_STRIKEBOX = 1 << LAYER_STRIKEBOX;
public const int MASK_EFFECT = 1 << LAYER_EFFECT;
public const int MASK_RAGDOLLSELFCOLLISION = 1 << LAYER_RAGDOLLSELFCOLLISION;
#endregion
#region Methods
/// <summary>
/// Returns the primary layer for some mob type.
/// </summary>
/// <param name="tp"></param>
/// <returns></returns>
public static int GetPrimaryLayerForMobType(EntityType tp)
{
switch (tp)
{
case EntityType.Player:
return LAYER_PLAYER;
case EntityType.NPC:
return LAYER_NPC;
case EntityType.Debris:
return LAYER_DEBRIS;
default:
return LAYER_DEFAULT;
}
}
/// <summary>
/// Gets the mob type for a layer.
/// </summary>
/// <param name="layer"></param>
/// <returns></returns>
public static EntityType GetMobTypeForLayer(int layer)
{
if (layer == LAYER_PLAYER)
{
return EntityType.Player;
}
else if (layer == LAYER_NPC)
{
return EntityType.NPC;
}
else if (layer == LAYER_DEBRIS)
{
return EntityType.Debris;
}
else
{
return EntityType.None;
}
}
#endregion
}
}
Then, even if I don’t have the constant defined, building a mask is easy:
var mask = Constants.MASK_PLAYER | Constants.MASK_NPC;
Much more readable in my opinion.
As for the original question. Yes, you can write the value in decimal notation.
Swift allows underscores in literals, e.g. 0b1111_1111_1111_1000__0000_0000_0000_0000, or 0xFFF8_0000. I’m unclear on what’s going on with this in C#.
That’s pretty nice. Actually, I wouldn’t mind seeing Swift language support in Unity.
–Eric
Indeed. Swift doesn’t have an equivalent of multicast delegates yet, but aside from that, I think it’s phenomenally good.
I am sorry about the off-topicness.
First time I saw that was an Eiffel, which made its debut way back in 1985 according to Wikipedia. It also had contracts, invariant, pre- and post- conditions on method calls, and multiple inheritance that actually made sense. Yet another demonstration that being first or being best has very little to do with success.
This is exactly what I do as well.
Now hold on a second there. I think you have a pretty warped definition of easy.
LayerMask mask = Constants.MASK_LAYERNAME | Constants.MASK_LAYERNAME | Constants.MASK_LAYERNAME | Constants.MASK_LAYERNAME | Constants.MASK_LAYERNAME | Constants.MASK_LAYERNAME | Constants.MASK_LAYERNAME | Constants.MASK_LAYERNAME | Constants.MASK_LAYERNAME | Constants.MASK_LAYERNAME | Constants.MASK_LAYERNAME | Constants.MASK_LAYERNAME | Constants.MASK_LAYERNAME;
Imagine those with proper names. But you see the problem, right? it’s a lot more effort to type Constants.whatever in an IDE, than it is to type 1 or 0 in a calculator. With my method i can look at the layers list from the top, and hit the appropriate binary input for whether i do/don’t want to collide with that layer, until i reach the point on the list where everything farther is 0. then hit Dec and paste in the result.
massive timesaver imo
It might be easier until you have to change that layer or mask to something different and then hunt down every instance of it in your project.
And there’s nothing saying your crazy mask example couldn’t be a constant as well in which case you would only write it once and then refer to it in a human readable way.
Might be faster to type (once) but I’d rather just look at something and know what it means in the context of my project than pull out a calculator every time in an effort to prove how clever I am.
Oh but hey, there is this little thing if you set it public
You know, that seems easier than any code solution. Or it would be if the menu didn’t dismiss with EVERY SINGLE CLICK UGH
Note in my example I have masks that are commonly used already turned into their own mask as well.
public const int MASK_SURFACE = MASK_DEFAULT | MASK_NONCLIMBABLE | MASK_STATIC_DEPRECATED | MASK_DYNAMIC_DEPRECATED;
So I can just say:
LayerMask mask = Constants.MASK_SURFACE; //a mask already made up of all the layers that are considered surfaces
It’s useful for reusability, in that if any masks change, you just change them in the Constants class. Rather than hunt down every place that uses it.
Also it’s more readable. If I read:
Constants.MASK_NPC | Constants.MASK_PLAYER
I know I have a mask that consists of the NPC and Player layers. I don’t have to just know what the number is… I know. It’s there in the code.
I’m sorry but this seems more readable to me:
if (Physics.Raycast(ray, float.PositiveInfinity, Constants.SURFACE))
{
...do stuff
}
Rather than
if (Physics.Raycast(ray, float.PositiveInfinity, 8192))
{
... do stuff
}
I’m going to put this out there.
I’ve been professionally writing software for almost a decade now. This is a standard… I did not make this up. This is what people DO, and have for decades.
It works.
It’s proven.
This is only useful for when you’re in a designer. You don’t always get to be in the designer.
Again though… if you change any of the layers… this thing buggers up as well and you have to go around and fix every place.
Try it, set a LayerMask on a component attached to a gameobject, then go edit the layers. Move them around. Go back, and you’ll see your mask is now wrong.
More editor scripts!