Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Create Terrain Hole at specific position on terrain by script

Discussion in 'World Building' started by protopop, Apr 21, 2021.

  1. protopop

    protopop

    Joined:
    May 19, 2009
    Posts:
    1,557
    Id like to place a dungeon entrance on my generated terrains and then create a terrain hole where they intersect.

    Can I either create a terrain hole where a sphere collider intersects with a terrain

    or

    Create a terrain hole at a certain position on a terrain, I am thinking x,z ordinates and maybe a width?

    how would I do this with a script , is it possible?

    Screen Shot 2021-04-21 at 12.11.36 AM.png
     
    AntonioModer likes this.
  2. In theory it is possible. I haven't tried it myself, so you will have to test it for yourself.

    You can give a coordinate (x, y) and an array to the TerrainData to define which sample points are holes and which ones are terrain and then when you're done, you can update everything.
    https://docs.unity3d.com/ScriptReference/TerrainData.SetHoles.html
    or
    https://docs.unity3d.com/ScriptReference/TerrainData.SetHolesDelayLOD.html

    Depends on your use-case. But you will have to calculate the area yourself.

    Edit
    ------
    Ok, so made a quick test, it works perfectly:
    Code (CSharp):
    1. using UnityEngine;
    2.  
    3. public class TerrainTest : MonoBehaviour
    4. {
    5.     [SerializeField] private Terrain t;
    6.     void Start()
    7.     {
    8.         var b = new bool[100,100];
    9.         for (var x = 0; x < 100; x++ )
    10.             for (var y = 0; y < 100; y++)
    11.                 b[x, y] = !(x > 20 && x < 80 && y > 20 && y < 80);
    12.         t.terrainData.SetHoles(0, 0, b);
    13.     }
    14. }
    15.  
    And the result in play mode:
    screenshot1.png
     
    Last edited by a moderator: Apr 21, 2021
    fontinixxl and protopop like this.
  3. protopop

    protopop

    Joined:
    May 19, 2009
    Posts:
    1,557
    This is really helpful. Thank you I’m going to test this out.

    part of my problem was I didn’t understand some of the code conventions.
    Like I don’t get what a bool[x,y] is. But looking at your example now I am thinking it creates a 100x100 2d array of Boolean values that default to false.

    and then the set holes function starts the position of the array at 0,0 on the terrain. So presumably I could start that position anywhere on my retain, ie where my dungeon entrance is, and size the hole (or rectangle) by changing the array size.

    anyways yeah this gives me something solid because I couldn’t find any examples as clear as this. I’ll report back :)

    E5E783D2-3947-427B-905F-A24D7B579896.png
     
    wyattt_, Rowlan and Lurking-Ninja like this.
  4. Yepp the
    t.terrainData.SetHoles(0, 0, b)
    tells that overlay a 100x100 boolean array over the terrain at the 0,0 (which can be anywhere, just change the 0,0 here).
    You also can draw more circular or any shape you can think of, just build it in the 2d array like:
    Code (CSharp):
    1. 0000001111000000
    2. 0000011111100000
    3. 0000111111110000
    4. 0000111111110000
    5. 0000111111110000
    6. 0000111111110000
    7. 0000011111100000
    If you represent this in the bool array (0 = false, 1 = true), then you will get a more circular shape. But I will leave it to you to figure it out. (Although you can always ask if something isn't clear, as always)
     
    Last edited by a moderator: Apr 22, 2021
    protopop likes this.
  5. protopop

    protopop

    Joined:
    May 19, 2009
    Posts:
    1,557
    I got the basics working thanks to your help:) thank you. Next set is finessing the results.

    Whats cool is I didn't realize it made an actual hole you can fall through. I thought to was just a visual thing. This helps a lot.

    Yeah for circles im sure there must be some kind of formula I can find that converts a square array into a circular pattern. But a square/rectangle works for now too.

    Screen Shot 2021-05-03 at 9.49.23 PM.png
     
    wyattt_ and Lurking-Ninja like this.
  6. protopop

    protopop

    Joined:
    May 19, 2009
    Posts:
    1,557
    Is it possible to UNDO a created hole? or remove all holes from a terrain?
     
  7. You can run the SetHole again with a full of 'true' value array. That will essentially erase the hole. You also can use the GetHole method to sample the terrain if there is a whole at the sample point.
     
    mtheodorou and protopop like this.
  8. protopop

    protopop

    Joined:
    May 19, 2009
    Posts:
    1,557
    Thanks that worked:)

    I notice it clears all the vegetation.

    Im wondering if I can use making a hole then undoing it immediately to clear grass under objects on teh terrain at runtime. Ive been looking for a way to do that.

    If I want to restore the grass I am thinking I need to grab the original terrain data and restore that?

    I also figured out how to make a circle hole.

    Code (CSharp):
    1. int offSetZ = holeWidth / 2;
    2.         int offSetX = holeHeight / 2;
    3.  
    4.         var b = new bool[holeWidth, holeHeight];
    5.  
    6.         Vector2 originOfCircle = new Vector2(offSetX, offSetZ) ;
    7.         for (var x = 0; x < holeWidth; x++)
    8.         {
    9.             for (var y = 0; y < holeWidth; y++)
    10.             {
    11.                
    12.                 if (Vector2.Distance(new Vector2(x, y), originOfCircle) <= offSetX) {
    13.                     b[x, y] = false;
    14.                 } else
    15.                 {
    16.                     b[x, y] = true;
    17.                 }
    18.             }
    19.         }
    20.  
    21.  
    22.         t.terrainData.SetHoles(xPos - offSetX, zPos - offSetZ, b);
    Heres my dungeon in progress. You can run into it through my terrain hole:)

    E00pJJsXsAE_qB8.jpeg

    And a swimming pool made using terrain holes.

    E06zwlgWEAAbkVc.jpeg
     
    hjohnsen and Lurking-Ninja like this.
  9. I haven't dug deep in terrain stuff yet, so I'm just guessing, you can manipulate the details somehow through the TerrainData just like the holes. Read up on it, I'm pretty sure you can figure it out (and then share it with us! :) )

    Looks great! Very good job!
     
    protopop likes this.
  10. protopop

    protopop

    Joined:
    May 19, 2009
    Posts:
    1,557
    I tried saving the original terrainData and then storing it but it didn't work

    originalTerrainData = closestTerrain.terrainData;
    then
    t.terrainData = originalTerrainData;

    But im going to look into this:)
     
    TimAidley likes this.
  11. protopop

    protopop

    Joined:
    May 19, 2009
    Posts:
    1,557
    For anyone wondering how to restore vegetation after creating and removing a hole, you can save your terrain tree instances before you did a hole then restore them after. I used the solution here:

    https://www.gamedev.net/forums/topic/707768-how-can-i-destroy-terrain-trees-in-unity-by-script/

    I just used these 3 lines

    private TreeInstance[] _originalTrees;

    // backup original terrain trees
    _originalTrees = terrain.terrainData.treeInstances;

    // restore original trees
    terrain.terrainData.treeInstances = _originalTrees;

    Ill look into how to do this with grass as well
     
    inmim likes this.
  12. inmim

    inmim

    Joined:
    Aug 23, 2019
    Posts:
    24
    thank you this has proven most helpful
     
  13. m_riley04

    m_riley04

    Joined:
    Apr 21, 2022
    Posts:
    4
    This is an old thread, but for those who are still looking for a solution to restoring terrain after dynamically placing holes, this is what I did:
    Code (CSharp):
    1. public void RemoveDynamicHoles(TerrainData terrainData, Vector3 position, int width, int height)
    2. {
    3.     var b = new bool[width, height];
    4.  
    5.     // Iterate through every hole position
    6.     for (var x = 0; x < width; x++)
    7.     {
    8.         for (var y = 0; y < height; y++)
    9.         {
    10.             // Set it to true
    11.             b[x, y] = true;
    12.         }
    13.     }
    14.  
    15.     // Set the hole values
    16.     terrainData.SetHoles((int)position.x, (int)position.z, b);
    17. }
    Essentially, I saved the position that I created the holes around and simply reversed it by setting `b[x,y]` equal to true, removing the hole at the x and y position. Hope this helps someone! :)