Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Swapping Terrain Layer order via c# - is it possible?

Discussion in 'Scripting' started by artofmining, Jul 25, 2022.

  1. artofmining

    artofmining

    Joined:
    May 1, 2016
    Posts:
    60
    HI folks,
    Does anyone know how to swap the terrain layers via script?
    I have a need to toggle/swap the layer order during runtime. 1st layer contains a plan texture covering the whole terrain, second layer contains ground texture. see image attached.

    I can manually drag the layer order in editor whilst the game is running and it works so that proves the concept. I just can't figure out the code to do this.

    NOTE: I do not want to swap the textures but instead merely swap the layer order that holds such textures. Also I do not want to do this with 2 terrains as I would need to keep ground sculpting updated.

    Nothing ideal found on google thus far.

    Many thanks in advance if you can help.
    Archie
     

    Attached Files:

  2. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,749
    How did it go with using
    TerrainData.terrainLayers
    ? That's where I would begin my investigation.
     
  3. artofmining

    artofmining

    Joined:
    May 1, 2016
    Posts:
    60
    I looked at it and tried simple code to get current layersand their index (which it did) and then storing those temp terrainlayers then rewriting the layers back to index 0 and 1 followed by a terrain.flush but nothing happened.
     
  4. artofmining

    artofmining

    Joined:
    May 1, 2016
    Posts:
    60
    This code will swap the textures (permanently but not a big issue with error trapping) but then I also have to edit the layer tiling settings as the first layer is a plan image that covers the whole terrain. So I would also need to edit the tiling settings of each layer which Im trying to avoid by simply swapping the layer order instead.

    I did also try swapping the terrain layers but nothing happened.


    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. public class terrainTextureswap : MonoBehaviour
    6. {
    7.     public Terrain myTerrain;
    8.     public Texture2D texture1;
    9.     public Texture2D texture2;
    10.     public TerrainLayer myLayer1;
    11.     public TerrainLayer myLayer2;
    12.  
    13.     // Start is called before the first frame update
    14.     void Start()
    15.     {
    16.      
    17.     }
    18.  
    19.     // Update is called once per frame
    20.     void Update()
    21.     {
    22.         if (Input.GetKeyDown(KeyCode.K))
    23.         {
    24.  
    25.             myTerrain.terrainData.terrainLayers[0].diffuseTexture = texture1;
    26.             myTerrain.terrainData.terrainLayers[1].diffuseTexture = texture2;
    27.             myTerrain.Flush();
    28.         }
    29.     }
    30. }
     
  5. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,749
    Pretty confident this construct (setting a single index field of a collection) will never work in Unity.

    Instead, copy the array out, change the references, assign the entire array back, assuming this array-based API property works like any other Unity array-based API.

    Here's why, in the context of Renderer.materials:

    Materials materials array of materials renderer:

    https://forum.unity.com/threads/cha...as-working-now-it-is-not.883372/#post-5923346

    https://forum.unity.com/threads/adding-and-removing-a-material-at-runtime.1048649/#post-6783635
     
  6. artofmining

    artofmining

    Joined:
    May 1, 2016
    Posts:
    60
    It works by swapping textures but as I stated you need to write code to trap and reset as its a permaent change after runtime. I'm using this method for now as I'm only using 2 layers so its fairly simply swap, I just had to add code to reset the tiling.
    Code (CSharp):
    1.  myTerrain.terrainData.terrainLayers[0].diffuseTexture = texture1;
    2.             myTerrain.terrainData.terrainLayers[1].diffuseTexture = texture2;
    3.          
    4.  
    5.             myTerrain.terrainData.terrainLayers[1].tileSize = new Vector2(766, 680);
    6.             myTerrain.terrainData.terrainLayers[0].tileSize = new Vector2(20, 20);
    7.             myTerrain.Flush();
     
  7. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,749
    Ah, this noise... I think what you want to do is instance the TerrainData at runtime so that your changes are committed to the instanced copy, not the asset on disk. You can see me doing this in my grenade / bomb terrain damager.



    See line 67-ish here:

    https://github.com/kurtdekker/makeg...TerrainStuff/TerrainDamager/TerrainDamager.cs

    Note the comment about it not being complete; there are complete terrain data cloners that can be found off google.

    MakeGeo is presently hosted at these locations:

    https://bitbucket.org/kurtdekker/makegeo

    https://github.com/kurtdekker/makegeo

    https://gitlab.com/kurtdekker/makegeo

    https://sourceforge.net/p/makegeo
     
  8. artofmining

    artofmining

    Joined:
    May 1, 2016
    Posts:
    60
    Cheers Kurt, possibly a little more ambitious than I need for this. Its simply so the user can toggle between a scaled plan overlay and a rock texture terrain. I generally do that with 2 terrains swapping out, but in this case there's a specific sculpt within the terrain thats required. Basically doing it witth swapping textures is fine for me now. I just reset them before exiting run via code.
    Thanks EVERYONE !
     
  9. artofmining

    artofmining

    Joined:
    May 1, 2016
    Posts:
    60
    Simple switch is fine