Splat-mapping Abstraction in Unity

Hello all! I’m posting today with a brief follow up on my post about terrain texture detection in Unity. If you haven’t checked that out, it will definitely be informing this post! I have been working further on getting this system to work with my current PlayerMovement system, and have come to a neat solution of abstraction which can be branched to communicate anywhere in your game what the current terrain texture is beneath the player.

Here’s what this solution solves:

Our player’s movement is effected constantly by what type of ground the player is on. So for example, braking power & amount of friction (2 separate variables in the PlayerMovement). Since we need these values to be dynamic, a layer of abstraction is helpful in allowing these values to be continuously modified by factors such as the terrain.

Our designers have not finalized the variables effecting movement at large, this solution allows for testing of multiple combinations of these movement variables as extreme ease to the design team. This will be used for AB testing in our playtests down the road.

The solution:

It’s very simple, but has helped a lot with working this data into my PlayerMovement in a clean and clutter free way. All I have done is created a new Scriptable Object type, MovementVariables, and moved all of the “designer variables” into this scriptable object. Additionally, I have created a simple class, TerrainType, which stores all of the variables that are dynamic and dependent upon the terrain the player is on. I’ve made this class serializable, and within my MovementVariables I have a public array of TerrainType that allows the designers to set each terrain type uniquely for each variant of the MovementVariable type.

MovementVariables has a public function, SetToTerrain(), that takes in an int representing the terrain texture the player is currently on (remember this is stored in an int by the Alpha map). Upon taking that int, MovementVariables will set the necessary variables to match those of the corresponding TerrainType in the local array. So, for example, terrainTypes[0] is created by the designer to have float frictionLevel = 1, and float brakingPower = 3. Once SetToTerrain() takes in an int 0, then MovementVariables will set the frictionLevel and brakingPower according to whatever is in terrainTypes[0].

From here, all that is necessary for setup (besides setting the designer variables) is to create a reference to a MovementVariables in both the PlayerMovement and the TerrainTextureGetter. The former will simply read the scripted values from this Scriptable Object, and the latter will pass the int representing the texture into SetToTerrain().

… and that’s it! It’s a super simple solution but really has helped me in passing these terrain settings into my movement, in making my PlayerMovement less beefy, and in aiding the design team with finalizing their movement variables… Here’s some code

public class TerrainType
   public string terrainTypeName;
   [Range(0f, 100f)]public float stridePushPower;
   [Range(0f, 15f)] public float brakingPower;
   [Range(0f, 5f)] public float slidingFrictionPower;
   [Range(0f, 10f)] public float turningSpeed;

This is the TerrainType class which is key in allowing this abstraction to work. These are the variables in PlayerMovement which we want to be altered by the terrain type beneath the player.

//Variables set by terrain
[ShowOnly]public float turningSpeedMod;
[ShowOnly]public float slidingFrictionPower;
[ShowOnly]public float brakingPower;
[ShowOnly]public float stridePushPower;

public void SetToTerrain(int terrainNumber)
    if (terrainNumber == 0 && terrainVariableSets[0] != null)
        turningSpeedMod = terrainVariableSets[0].turningSpeed;
        slidingFrictionPower = terrainVariableSets[0].slidingFrictionPower;
        brakingPower = terrainVariableSets[0].brakingPower;
        stridePushPower = terrainVariableSets[0].stridePushPower;
    else if (terrainNumber == 1 && terrainVariableSets[1] != null)
        turningSpeedMod = terrainVariableSets[1].turningSpeed;
        slidingFrictionPower = terrainVariableSets[1].slidingFrictionPower;
        brakingPower = terrainVariableSets[1].brakingPower;
        stridePushPower = terrainVariableSets[1].stridePushPower;
    else if (terrainNumber == 2 && terrainVariableSets[2] != null)
        turningSpeedMod = terrainVariableSets[2].turningSpeed;
        slidingFrictionPower = terrainVariableSets[2].slidingFrictionPower;
        brakingPower = terrainVariableSets[2].brakingPower;
        stridePushPower = terrainVariableSets[2].stridePushPower;
    else if (terrainNumber == 3 && terrainVariableSets[3] != null)
        turningSpeedMod = terrainVariableSets[3].turningSpeed;
        slidingFrictionPower = terrainVariableSets[3].slidingFrictionPower;
        brakingPower = terrainVariableSets[3].brakingPower;
        stridePushPower = terrainVariableSets[3].stridePushPower;
    else if (terrainNumber > terrainVariableSets.Length - 1)Debug.LogError("Unsupported Terrain Type. Add to MovementVariablePackage");

This chunk of code above is located within my MovementVariables Scriptable Object. The top variables are what are being referenced within PlayerMovement, but they are being set below. SetToTerrain() is called every time a terrain texture change is detected. An important note is the [ShowOnly] editor attribute was written and made public by Stack Overflow user Lev-Lukomskyi. Huge thanks for that, as it keeps the designers from touching things they shouldnt ;). I’m just kidding… I hope that this post was helpful for anyone who needed a follow up from my last post about terrain texture detection! Until next time!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s