Search Unity

C# Prefab Terrain Modification Runtime (Helping others to understand Beginner)

Discussion in 'Scripting' started by sentar, Nov 21, 2019.

  1. sentar

    sentar

    Joined:
    Feb 27, 2017
    Posts:
    44
    Hi Unity Pros,

    Disclamer: This is most likely a solution to help guide you and is not an end all type of thing (I'm a beginner too, you know), and I didn't really know where else to post this.

    As the title mentions "Helping others to understand Beginner" I struggled for about two years learning how the world (heck) do I modify terrain with a prefab. After taking 6 month break from it, looking at dozens of forums, and many youtube videos I was able to piece together a entry level solution (I'm not the smartest crayon in the box, you know).

    This was done in Unity3d 2019.2.1f1

    Firstly, I placed a box as my reference source (as all I wanted was to get a square terrain area for placing objects), rigidbody, and two box colliders (one trigger and the other not a trigger), and set the terrain tag to "Terrain".

    The Script:
    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. public class Object_Terrain_Modifiy : MonoBehaviour
    6. {
    7.     public Terrain terrain; //get terrain to manipulate
    8.     public float strength = 0.001f; //keep the strength low as we are working in [0, 1] increments as 1 is maximum
    9.     public float Ray_distance = 2f; // get raycast distance from Gameobject
    10.  
    11.     private int heightmapWidth; //map width
    12.     private int heightmapHeight; //map height
    13.     private float[,] heights; //still don't know what this really does
    14.     private TerrainData terrainData; //all the data ever needed for terrains
    15.  
    16.  
    17.     // Baisically call everything so we can work with the terrain in the start function
    18.     void Start()
    19.     {
    20.         terrainData = terrain.terrainData;
    21.  
    22.         heightmapHeight = terrainData.heightmapHeight;
    23.         heightmapWidth = terrainData.heightmapWidth;
    24.         heights = terrainData.GetHeights(0, 0, heightmapWidth, heightmapHeight);
    25.     }
    26.  
    27.     // I used fix so it is more smooth with how the terrain is leveled (as this costs cpu resources)
    28.     void FixedUpdate()
    29.     {
    30.         RaycastHit hit;
    31.      
    32.             //if raycast from object is either above/below find what it hits
    33.             //AND make sure we hit a tag for "Terrain"
    34.             if (Physics.Raycast(this.gameObject.transform.position, -Vector3.up, out hit, Ray_distance) || Physics.Raycast(this.gameObject.transform.position, Vector3.up, out hit))
    35.             {
    36.             if (hit.point.y < (this.gameObject.transform.position.y - (this.gameObject.transform.localScale.y / 2)) && hit.collider.tag == "Terrain")
    37.             {
    38.                 //raise terrain
    39.                 RaiseTerrain(hit.point);
    40.             }
    41.          
    42.  
    43.             if (hit.point.y > (this.gameObject.transform.position.y - (this.gameObject.transform.localScale.y / 2)) + 0.5f && hit.collider.tag == "Terrain")
    44.             {
    45.                 //lower terrain
    46.                 LowerTerrain(hit.point);
    47.             }
    48.  
    49.             // if the terrain hit spot is not too high or low turn off script so we don't waste resources on manipulating the terrain anymore
    50.             if (!(hit.point.y < (this.gameObject.transform.position.y - (this.gameObject.transform.localScale.y / 2))) && (!(hit.point.y > (this.gameObject.transform.position.y - (this.gameObject.transform.localScale.y / 2)) + 0.5f)) && hit.collider.tag == "Terrain")
    51.             {
    52.                 this.gameObject.GetComponent<Object_Terrain_Modifiy>().enabled = false;
    53.             }
    54.  
    55.         }
    56.  
    57.             //see in real time when we move the box where we are attempting to hit
    58.         Debug.DrawRay(this.gameObject.transform.position, -Vector3.up, Color.red, Ray_distance);
    59.     }
    60.  
    61.     private void RaiseTerrain(Vector3 point)
    62.     {
    63.         //get the world coordinate of our hit location and convert it to terrain location
    64.         int mouseX = (int)((point.x / terrainData.size.x) * heightmapWidth);
    65.         int mouseZ = (int)((point.z / terrainData.size.z) * heightmapHeight);
    66.  
    67.         Debug.Log("what is X: " + mouseX); //check to see if we are in the right area
    68.         Debug.Log("what is Z: " + mouseZ); //check to see if we are in the right area
    69.  
    70.         float[,] modifiedHeights = new float[1, 1]; //set the maximum heights to 1 as we want to make sure we can manipulate the terrain at any height level
    71.         float y = heights[mouseX, mouseZ]; //unsure still of what heights[,] really accomplishes
    72.  
    73.      
    74.         //raise terrain
    75.             y += strength * Time.deltaTime;
    76.      
    77.  
    78.      
    79.         //if object is higher than terrain max height spot increasing terrain.
    80.         if (y > terrainData.size.y)
    81.         {
    82.             y = terrainData.size.y;
    83.         }
    84.  
    85.         //get our lowest point that we want to start in
    86.         modifiedHeights[0, 0] = y;
    87.  
    88.      
    89.         // this is where the magic terrain manipulation magic starts to happen //
    90.         // I set both i & z to a negative scale value to create a starting point behind and away from the box, and keep adjusting terrain till it is positively over the box's area
    91.         for (int i = -(int)this.gameObject.transform.localScale.x; i < (this.gameObject.transform.localScale.x * 2); i++)
    92.         {
    93.             for (int z = -(int)this.gameObject.transform.localScale.z; z < (this.gameObject.transform.localScale.z * 2); z++)
    94.             {
    95.                 heights[mouseX - i, mouseZ - z] = y;
    96.                 terrainData.SetHeights(mouseX - i, mouseZ - z, modifiedHeights);
    97.             }
    98.         }
    99.  
    100.  
    101.     }
    102.  
    103.     //does pretty much the opposite but it does it very very fast (still the work in progress part of the script)
    104.     private void LowerTerrain(Vector3 point)
    105.     {
    106.         int mouseX = (int)((point.x / terrainData.size.x) * heightmapWidth);
    107.         int mouseZ = (int)((point.z / terrainData.size.z) * heightmapHeight);
    108.  
    109.         float[,] modifiedHeights = new float[1, 1];
    110.         float y = heights[mouseX, mouseZ];
    111.         y -= strength * Time.deltaTime;
    112.  
    113.         if (y < terrainData.size.y)
    114.         {
    115.             y = 0;
    116.         }
    117.  
    118.         modifiedHeights[0, 0] = y;
    119.      
    120.         for (int i = -(int)this.gameObject.transform.localScale.x; i < (this.gameObject.transform.localScale.x * 2); i++)
    121.         {
    122.             for (int z = -(int)this.gameObject.transform.localScale.z; z < (this.gameObject.transform.localScale.z * 2); z++)
    123.             {
    124.                 heights[mouseX - i, mouseZ - z] = y;
    125.                 terrainData.SetHeights(mouseX - i, mouseZ - z, modifiedHeights);
    126.             }
    127.         }
    128.  
    129.     }
    130. }
    The end result in the png picture.
     

    Attached Files:

    Last edited: Nov 21, 2019
    mgear likes this.