Search Unity

  1. Unity 6 Preview is now available. To find out what's new, have a look at our Unity 6 Preview blog post.
    Dismiss Notice
  2. Unity is excited to announce that we will be collaborating with TheXPlace for a summer game jam from June 13 - June 19. Learn more.
    Dismiss Notice
  3. Dismiss Notice

Question A simple way to remove grass under placible building objects

Discussion in 'Scripting' started by SirCuddlesWorth, May 23, 2024.

  1. SirCuddlesWorth

    SirCuddlesWorth

    Joined:
    May 8, 2022
    Posts:
    5
    Is it possible to remove grass from the terrain easily with a simple script? ive been trying to get it so a buildable object will remove all grass that the colider touches. but ive been looking for months and have not bin able to get help.youtube dont have any tutorials for it eather. im autistic-add-ocd and c+ or any code is alien to me. but i have been working on my first game for a long time now and i usaly buy assets for it so i can avoid most of the issues.

    but it dont have that feature and the dev said it might be added later. i hate the way the grass clips through the floor if anyone knows how to ad this right i would be very extremly greatful.
    i got tyhe code from the unity faq i think.
    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. public class BuildableDetailLayer : MonoBehaviour
    6. {
    7.     [SerializeField]
    8.      private Terrain terrain;
    9.      private TerrainData terrainData;
    10.  
    11.     private void CutGrass(Vector3 buildingCenter, int size)
    12.     {
    13.         Vector3 terrainPos = terrainManager.ConvertToSplatMapCoordinate(buildingCenter);
    14.         int[,] map = terrain.terrainData.GetDetailLayer((int)terrainPos.x - size / 2, (int)terrainPos.z - size / 2,
    15.           size, size, 0);
    16.         // For each pixel in the detail map...
    17.         for (int z = 0; z < size; z++)
    18.         {
    19.             for (int x = 0; x < size; x++)
    20.             {
    21.                 map[x, z] = 0;
    22.             }
    23.         }
    24.         // Get all of layer zero.
    25.         for (int i = 0; i < terrain.terrainData.terrainLayers.Length; i++)
    26.         {
    27.             // Assign the modified map back.
    28.             terrain.terrainData.SetDetailLayer((int)terrainPos.x - size / 2, (int)terrainPos.z - size / 2, i, map);
    29.         }
    30.     }
    31. }
    Code (CSharp):
    1. Assets\_Scripts\BuildableDetailLayer.cs(13,45): error CS0117: 'TerrainManager' does not contain a definition for 'ConvertToSplatMapCoordinate'
    i have a manager here.
    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. public class TerrainManager : MonoBehaviour
    6. {
    7.     const int xMax = 3; // How many tiles our north-south is
    8.     const int zMax = 6; // How many tiles east-west is
    9.     const float terrainSize = 500.0f; // Size of each terrain tile
    10.  
    11.     // How we remember where each terrain object is in a Dictionary
    12.     class TerrainKey
    13.     {
    14.         public int x;
    15.         public int z;
    16.  
    17.         // Unity uses z instead of y for east-west
    18.         public TerrainKey(int x, int z)
    19.         {
    20.             this.x = x;
    21.             this.z = z;
    22.         }
    23.  
    24.         // Get key from position
    25.         public TerrainKey(Vector3 pos)
    26.         {
    27.             x = Mathf.FloorToInt(pos.x / terrainSize);
    28.             z = Mathf.FloorToInt(pos.z / terrainSize);
    29.         }
    30.  
    31.         // Might be outside range
    32.         public bool isValid()
    33.         {
    34.             if (x < 0 || x >= xMax)
    35.                 return false;
    36.             if (z < 0 || z >= zMax)
    37.                 return false;
    38.             return true;
    39.         }
    40.  
    41.         // What tile keys are around us?
    42.         public TerrainKey[] getNeighbors()
    43.         {
    44.             if (!isValid())
    45.             {
    46.                 Debug.Log("Calling getNeighbors with invalid key");
    47.                 return new TerrainKey[] { };
    48.             }
    49.  
    50.             TerrainKey left = new TerrainKey(x - 1, z);
    51.             TerrainKey right = new TerrainKey(x + 1, z);
    52.             TerrainKey top = new TerrainKey(x, z + 1);
    53.             TerrainKey topleft = new TerrainKey(x - 1, z + 1);
    54.             TerrainKey topright = new TerrainKey(x + 1, z + 1);
    55.             TerrainKey bottom = new TerrainKey(x, z - 1);
    56.             TerrainKey bottomleft = new TerrainKey(x - 1, z - 1);
    57.             TerrainKey bottomright = new TerrainKey(x + 1, z - 1);
    58.  
    59.             if (left.isValid())
    60.             {
    61.                 if (right.isValid())
    62.                 {
    63.                     if (top.isValid())
    64.                     {
    65.                         if (bottom.isValid()) // Return all
    66.                             return new TerrainKey[] { topleft, top, topright, left, this, right, bottomleft, bottom, bottomright };
    67.                         else // Remove bottom
    68.                             return new TerrainKey[] { topleft, top, topright, left, this, right };
    69.                     }
    70.                     else
    71.                     {
    72.                         if (bottom.isValid()) // Remove top
    73.                             return new TerrainKey[] { left, this, right, bottomleft, bottom, bottomright };
    74.                     }
    75.                 }
    76.                 else
    77.                 {
    78.                     if (top.isValid())
    79.                     {
    80.                         if (bottom.isValid()) // Remove right
    81.                             return new TerrainKey[] { topleft, top, left, this, bottomleft, bottom };
    82.                         else // Remove right and bottom
    83.                             return new TerrainKey[] { topleft, top, left, this };
    84.                     }
    85.                     else
    86.                     {
    87.                         if (bottom.isValid()) // Remove right and top
    88.                             return new TerrainKey[] { left, this, bottomleft, bottom };
    89.                     }
    90.                 }
    91.             }
    92.             else
    93.             {
    94.                 if (right.isValid())
    95.                 {
    96.                     if (top.isValid())
    97.                     {
    98.                         if (bottom.isValid()) // Remove left
    99.                             return new TerrainKey[] { top, topright, this, right, bottom, bottomright };
    100.                         else // Remove left and bottom
    101.                             return new TerrainKey[] { top, topright, this, right };
    102.                     }
    103.                     else
    104.                     {
    105.                         if (bottom.isValid()) // Remove left and top
    106.                             return new TerrainKey[] { this, right, bottom, bottomright };
    107.                     }
    108.                 }
    109.             }
    110.             // Some cases are left out because we should never be less than size 3
    111.             Debug.Log("xMax or zMax cannot be less than 3");
    112.             return new TerrainKey[] { };
    113.         }
    114.  
    115.         // Returns top left most anchor point
    116.         public Vector3 getPos()
    117.         {
    118.             return new Vector3(x * terrainSize, 0, z * terrainSize);
    119.         }
    120.  
    121.         public override string ToString()
    122.         {
    123.             // Also used when loading the terrain resource
    124.             return string.Format("Terrain{0}_{1}", x, z);
    125.         }
    126.  
    127.         // Needed to compare two keys
    128.         public override bool Equals(object obj)
    129.         {
    130.             if (obj == null)
    131.                 return false;
    132.             TerrainKey tk = obj as TerrainKey;
    133.             return x == tk.x && z == tk.z;
    134.         }
    135.  
    136.         // Needed to compare two keys
    137.         public override int GetHashCode()
    138.         {
    139.             return x ^ z;
    140.         }
    141.     }
    142.  
    143.     class TerrainValue
    144.     {
    145.         // Our terrain data
    146.         public TerrainValue()
    147.         {
    148.             gameObject = null;
    149.             lastNeeded = 0;
    150.             isLoading = false;
    151.         }
    152.  
    153.         public GameObject gameObject;
    154.         public Terrain terrain;
    155.         public TerrainKey[] neighbors; // Probably not the right place to store this
    156.         public float lastNeeded; // Last time player needed tile
    157.         public bool isLoading; // Used with LoadAsync
    158.     }
    159.     Dictionary<TerrainKey, TerrainValue> terrainDictionary; // Store all terrain data here
    160.     float lastSetNeighbors = 0;
    161.  
    162.     private static TerrainManager instance = null;
    163.     public void Awake()
    164.     {
    165.         if (instance == null)
    166.             instance = this;
    167.         else if (instance != this)
    168.             Destroy(gameObject);
    169.     }
    170.  
    171.     void Start()
    172.     {
    173.         // Set up our dictionary
    174.         terrainDictionary = new Dictionary<TerrainKey, TerrainValue>(xMax * zMax);
    175.         for (int x = 0; x < xMax; x++)
    176.             for (int z = 0; z < zMax; z++)
    177.                 terrainDictionary.Add(new TerrainKey(x, z), new TerrainValue());
    178.  
    179.         foreach (var pair in terrainDictionary)
    180.             pair.Value.neighbors = pair.Key.getNeighbors();
    181.  
    182.         // Used in adding and removing terrain
    183.         StartCoroutine(manageDictionary());
    184.     }
    185.  
    186.     // As terrain is flagged as needed or not needed this routine will load or destroy terrain
    187.     IEnumerator manageDictionary()
    188.     {
    189.         while (true)
    190.         {
    191.             foreach (var pair in terrainDictionary)
    192.             {
    193.                 if (pair.Value.lastNeeded <= Time.timeSinceLevelLoad)
    194.                 {
    195.                     if (pair.Value.gameObject == null)
    196.                         continue;
    197.  
    198.                     // Don't need terrain so remove
    199.                     Destroy(pair.Value.gameObject);
    200.                 }
    201.                 else
    202.                 {
    203.                     if (pair.Value.gameObject != null)
    204.                         continue;
    205.  
    206.                     // Need terrain so load
    207.                     if (!pair.Value.isLoading)
    208.                     {
    209.                         pair.Value.isLoading = true;
    210.                         StartCoroutine(loadTerrain(pair));
    211.                     }
    212.                 }
    213.             }
    214.  
    215.             if (lastSetNeighbors != 0 && lastSetNeighbors < Time.timeSinceLevelLoad)
    216.             {
    217.                 lastSetNeighbors = 0;
    218.                 foreach (var pair in terrainDictionary)
    219.                 {
    220.                     if (pair.Value.gameObject != null)
    221.                         setNeighbors(pair.Key);
    222.                 }
    223.             }
    224.  
    225.             // Check every second
    226.             yield return new WaitForSeconds(1.0f);
    227.         }
    228.     }
    229.  
    230.     // Loads terrain from resource and sets to correct position
    231.     IEnumerator loadTerrain(KeyValuePair<TerrainKey, TerrainValue> pair)
    232.     {
    233.         ResourceRequest request = Resources.LoadAsync(pair.Key.ToString());
    234.         if (request.asset == null)
    235.         {
    236.             Debug.Log("Unable to find terrain " + pair.Key.ToString());
    237.             yield break;
    238.         }
    239.         yield return null; // Starts again when LoadAsync is done
    240.  
    241.         TerrainData t = request.asset as TerrainData;
    242.         pair.Value.gameObject = Terrain.CreateTerrainGameObject(t);
    243.         pair.Value.gameObject.transform.position = pair.Key.getPos();
    244.         pair.Value.terrain = pair.Value.gameObject.GetComponent<Terrain>();
    245.         pair.Value.isLoading = false;
    246.  
    247.         lastSetNeighbors = Time.timeSinceLevelLoad + 1.0f;
    248.     }
    249.  
    250.     // Called by player objects
    251.     public static void reportLocation(Vector3 pos)
    252.     {
    253.         if (instance == null)
    254.             return;
    255.         TerrainKey key = new TerrainKey(pos);
    256.         if (!key.isValid())
    257.         {
    258.             Debug.Log("Player outside terrain area");
    259.             return;
    260.         }
    261.  
    262.         // Mark neighbors as being needed
    263.         TerrainKey[] neighbors = instance.terrainDictionary[key].neighbors;
    264.         for (int i = 0; i < neighbors.Length; i++)
    265.         {
    266.             key = neighbors[i];
    267.             instance.terrainDictionary[key].lastNeeded = Time.timeSinceLevelLoad + 1.0f;
    268.         }
    269.     }
    270.  
    271.     Terrain getTerrain(TerrainKey tk)
    272.     {
    273.         if (!tk.isValid())
    274.             return null;
    275.         if (terrainDictionary[tk].gameObject == null)
    276.             return null;
    277.         return terrainDictionary[tk].terrain;
    278.     }
    279.  
    280.     void setNeighbors(TerrainKey tk)
    281.     {
    282.         if (!tk.isValid())
    283.             return;
    284.         Terrain left = getTerrain(new TerrainKey(tk.x - 1, tk.z));
    285.         Terrain top = getTerrain(new TerrainKey(tk.x, tk.z + 1));
    286.         Terrain right = getTerrain(new TerrainKey(tk.x + 1, tk.z));
    287.         Terrain bottom = getTerrain(new TerrainKey(tk.x, tk.z - 1));
    288.         getTerrain(tk).SetNeighbors(left, top, right, bottom);
    289.     }
    290. }
    it dont seem to detect it i dont know how to fix it.
    I hope everyone has a great weekend.
     
    Last edited: May 23, 2024