Search code examples
c#unity-game-engineunity3d-terrain

how to set the SetAlphamaps to one certain texture?


i want to change the texture of my terrain with certain texture. i got confuse to set the splatmapdata, anyone can help me out??

private void ChangeTexture(Vector3 WorldPos)
{
    print ("changeTexture");

    int mapX = (int)(((WorldPos.x - terrainPos.x) / terrainData.size.x) * terrainData.alphamapWidth);
    int mapZ = (int)(((WorldPos.z - terrainPos.z) / terrainData.size.z) * terrainData.alphamapHeight);


    float[,,] splatmapData = terrainData.GetAlphamaps(3, 3, 15, 15);
    terrainData.SetAlphamaps (mapX, mapZ, splatmapData);
    terrain.Flush ();
}

Solution

  • The data returned by GetAlphamaps

    The returned array is three-dimensional - the first two dimensions represent x and y coordinates on the map, while the third denotes the splatmap texture to which the alphamap is applied.

    Or in simple words a float[x, y, l] where

    • x = width in pixels
    • y = height in pixels
    • l = Texture-Layer

    So lets say you want to set it to a certain texture at this pixel coordinates what you do is

    • set the weight for the texture's layer to 1
    • set all other layers weight to 0

    So let's say you have e.g. 3 Layers and you want the second one (= index 1) to be the full weighted texture:

    float[,,] splatmapData = terrainData.GetAlphamaps(mapX, mapZ, 15, 15);
    
    // Iterate over x-y coordinates within the array
    for(var y = 0; i < 15; y++)
    {
        for(var x = 0; x < 15; x++)
        {
            // Set first layers weight to 0
            splatmapData[x, y, 0] = 0;
    
            // Set second layer's weight to 1
            splatmapData[x, y, 1] = 1;
    
            // Set third layer's weight to 0
            splatmapData[x, y, 2] = 0;
        }
    }
    
    terrainData.SetAlphamaps(mapX, mapZ, splatmapData);
    

    I would then implement an enum for the layers like let's say

    public enum TerrainLayer
    {
        Default = 0,
     
        Green,
        Red
    }
    

    so you can simply pass the according layer index as a parameter - a bit more secure than passing in the int values themselves:

    private void ChangeTexture(Vector3 worldPos, TerrainLayer toLayer)
    {
        print ("changeTexture");
    
        int mapX = (int)(((worldPos.x - terrainPos.x) / terrainData.size.x) * terrainData.alphamapWidth);
        int mapZ = (int)(((worldPos.z - terrainPos.z) / terrainData.size.z) * terrainData.alphamapHeight);
    
        float[,,] splatmapData = terrainData.GetAlphamaps(mapX, mapZ, 15, 15);
    
        for(var z = 0; z < 15; z++)
        {
            for(var x = 0; x < 15; x++)
            {
                // This ofcourse would be more efficient if you do this only once
                // e.g. in Awake since the enum won't change on runtime
                var values = (TerrainLAyer[])Enum.GetValues(typeof(TerrainLayer));
    
                // Iterate through the enum and 
                for(var l = 0; l < values.Length; l++)
                {
                    // set all layers to 0 except the toLayer
                    splatmapData[x, z, l] = values[l] == toLayer ? 1 : 0;
                }
            }
        }
    
        terrainData.SetAlphamaps (mapX, mapZ, splatmapData);
        terrain.Flush ();
    }
    

    Now you would simply call it e.g.

    ChangeTexture(somePosition, TerrainLayer.Green);