Search Unity

Why didnt I ever think of this before? Get LayerMasks from a master source

Discussion in 'Scripting' started by Marscaleb, Sep 25, 2021.

  1. Marscaleb

    Marscaleb

    Joined:
    Jan 7, 2014
    Posts:
    1,037
    I found an easier way to do things that I want to share with everyone. Maybe it's something you can use too.

    So I have a number of LayerMasks that I have to set up in various characters, in my enemies and in my players. I use them mostly for collision detection so I can ignore colliders for things like powerups when I'm looking for ground. I have a layerMask called "What is ground" and one called "What Is Wall" and I was just working on one that would be used to identify things a projectile can hit.

    These have been set up as public variables in my classes, but this meant I had to set these for each enemy prefab. Each new enemy type, I have to go through and select what layers count as something they can stand on.

    But I've also got a "Game Manager" class that I use to, well, manage the game and various universal elements. And my Game Manager has a static variable that is a reference to itself, which means any class can use GameManager.GM to get access to any public variable or function.

    And since I already have that set up within my game, well, why don't I put those layer masks as a variable within the GM? That way I can set up that layer mask JUST ONCE, and when my various characters in my scene need to use a layer mask, they can just find it in the GM.
    Or more specifically, the pawns will have a protected variable for the layer mask, and in their awake function they copy the GM value to that variable. That way they don't need to cast to the GM repeatedly.

    And sure, some characters may need a different mask, like how my players will respond to different layers for collision. Well then, I just make a separate "What is Ground for Player" LayerMask and retrieve that.

    This is brilliant. I can set up variables in a single master source rather than setting them up for each character prefab. Why didn't I think of this before?
     
  2. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,697
    I wouldn't recommend cluttering up the GameManager with stuff like that, although I suppose you could... it just doesn't scale well.

    My approach is to have a
    MyLayers.cs
    and
    MyTags.cs
    and
    MyGUIDepths.cs
    file, each with a static class and
    public const int Variable = 10;
    type variables inside of it.

    Here's the one for Layers in my Jetpack Kurt game:

    Code (csharp):
    1. using UnityEngine;
    2.  
    3. public static class MyLayers
    4. {
    5.     public const string s_NoShadow = "noshadow";
    6.     public const string s_Player = "player";
    7.     public static int i_Player { get { return LayerMask.NameToLayer (s_Player); } }
    8.     public static int MaskNotPlayer { get { return ~(1 << i_Player); } }
    9.     public const string s_Shots = "shots";
    10.     public static int i_Shots { get { return LayerMask.NameToLayer (s_Shots); } }
    11.     public const string s_Aliens = "aliens";
    12.     public const string s_Bombs = "bombs";
    13.     public const string s_Static = "static";
    14.     public const string s_Motes = "motes";
    15.     public const string s_Markers = "markers";
    16.     public static int i_Markers { get { return LayerMask.NameToLayer (s_Markers); } }
    17.  
    18.     public static void SetLayerRecursively( GameObject go, int layer)
    19.     {
    20.         go.layer = layer;
    21.         foreach( Transform tr in go.transform)
    22.         {
    23.             SetLayerRecursively( tr.gameObject, layer);
    24.         }
    25.     }
    26.  
    27.     public static void SetLayerRecursively( GameObject go, string layerName)
    28.     {
    29.         SetLayerRecursively (go, LayerMask.NameToLayer (layerName));
    30.     }
    31. }
     
  3. Marscaleb

    Marscaleb

    Joined:
    Jan 7, 2014
    Posts:
    1,037
    That sounds effective, but how would you set up specific combinations of layers for a layer mask? Like when a layer mask needs to include this this and this, how do you establish that?

    I don't know how to set that outside of the inspector.
     
  4. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,697
  5. Bunny83

    Bunny83

    Joined:
    Oct 18, 2010
    Posts:
    3,993
    I think you mean how to setup layer combination dynamically inside the editor so you only have to setup a certain combination once and give that combination of layer a certain name. To do this, using ScriptableObject is one of the most flexible approaches. Just something like this:

    Code (CSharp):
    1. [CreateAssetMenu("LayerMaskPreset")]
    2. public class LayerMaskPreset : ScriptableObject
    3. {
    4.     public LayerMask  mask;
    5. }
    Now you can simply create an asset in your project which you can name "GroundLayers". So you have a single place in your project where you define which layers should be considered ground. Whenever you want to use this layer combination, you simply create a variable like this:

    Code (CSharp):
    1. public LayerMaskPreset layers;
    You can simply drag and drop your "GroundLayers" asset onto that variable. In code you just have to access
    layers.mask


    This is the typical usecase for ScriptableObjects. Maybe you even find a better name for the scriptable object class. Also such an approach always allows to include more information into the same class that somehow belongs to this "preset". For example maybe you need / want to assign a certain color to each preset, so you can simply add that to your scriptable object and each preset you create will have an additional color value.
     
    Kurt-Dekker likes this.
  6. Bunny83

    Bunny83

    Joined:
    Oct 18, 2010
    Posts:
    3,993
    Another advantage when using a custom class like this is that for debugging purposes you could even wrap the access behind a property. So every script which uses that preset class would have to go through that property. Here you could log any access to the property. This can help when you track down issues in your code.