Search Unity

Terrain modification and game performance

Discussion in 'Scripting' started by Apicella, Aug 30, 2014.

  1. Apicella

    Apicella

    Joined:
    Aug 30, 2014
    Posts:
    3
    Hello anyone,
    I'm new to Unity, a beginner in scripting and this is my first post, so nice to meet you!

    I have this problem: I'm using a script to make an ojbect modify the terrain in real-time when a collision is detected. I've tried many scripts (I searched a lot) and this one is the only one I could customize and make it work, but the problem is that when I hit play game performance drop down enormously and the game is not usable anymore.
    It's a big terrain, with many different heights and maybe these are some factors of the factors (but I can't change it). What I can change is the script or the way to perform its task. Maybe delimiting calculation of SetHeights to a part of my terrain? But how?
    So, is there any way to optimize it, improving performance?
    The cose below is attacjed to a gameobject
    Thank you.

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. public class Flatter : MonoBehaviour {
    5.     public Terrain myTerrain;
    6.     TerrainData terrData;
    7.  
    8.     float[,] heightmapData; // prepare a matrix with terrain points
    9.     float ratio; // ratio between player position and terrain points
    10.  
    11.     // Use this for initialization
    12.     void Start () {
    13.      
    14.         terrData = myTerrain.terrainData;
    15.         int terrRes = terrData.heightmapResolution;
    16.         Vector3 terrSize = terrData.size;
    17.      
    18.         heightmapData = terrData.GetHeights(0, 0, terrRes, terrRes); // heights we will change during of walking
    19.      
    20.         ratio = terrSize.x / terrRes;
    21.      
    22.         Debug.Log ("heightmapData="+heightmapData +" terrRes="+terrRes+" terrSize="+terrSize + " ratio="+ratio);
    23.     }
    24.  
    25.     // Update is called once per frame
    26.     void Update () {
    27.      
    28.         int terrainPointX = Mathf.CeilToInt(transform.position.x / ratio);
    29.         int terrainPointZ = Mathf.CeilToInt(transform.position.z / ratio);
    30.      
    31.         Debug.Log (" terrainPointX="+terrainPointX+" x terrainPointZ="+terrainPointZ +": set height to 0.0");
    32.      
    33.         heightmapData[terrainPointZ,terrainPointX ] = 0.0f; // move terrain point to 0 (example)
    34.      
    35.     }
    36.  
    37.     void OnCollisionEnter (Collision col)
    38.     {
    39.         if(col.gameObject.tag == "myterrain")
    40.         {
    41.             Debug.Log("Saving");
    42.          
    43.             terrData.SetHeights(0, 0, heightmapData); // save terrain heights back
    44.             }
    45.         }
    46.  
    47.     }
    48.  
    49.  
    50.  
     
    Last edited: Aug 30, 2014
  2. ensiferum888

    ensiferum888

    Joined:
    May 11, 2013
    Posts:
    317
    Having a Debug.Log every frame will kill your frame rate. Also limit the setHeights to use a small area of your terrain only.
     
  3. Apicella

    Apicella

    Joined:
    Aug 30, 2014
    Posts:
    3
    Thank you ensiferum888,
    I removed Debug.Logs but had no improvements... How can I limit the setHeights to a small area using my script?
    I'm very new to c#
     
  4. ensiferum888

    ensiferum888

    Joined:
    May 11, 2013
    Posts:
    317
    You can decide the area to affect when you use GetHeights:

    Code (CSharp):
    1.  
    2. //This is to compensate if your terrain is not at 0,0,0 in world coordinates
    3. Vector3 tempPos = node.transform.position - terrain.transform.position;
    4.  
    5. //Declare a vector that will be the normalized terrain coordinate
    6. Vector3 coord;
    7.  
    8. //normalize the coordinates.
    9. coord.x = tempPos.x / terrain.terrainData.size.x;
    10. coord.y = tempPos.y / terrain.terrainData.size.y;
    11. coord.z = tempPos.z / terrain.terrainData.size.z;
    12.  
    13. //What you actually want are the coordinates on the heightMap, not the terrain position index, so multiply the normalized index by the height map size to get the exact index.
    14. int posX = (int) (coord.x * hmwidth);
    15. int posY = (int) (coord.z * hmwidth);
    16.  
    17. //Declare an offset, I have a public int on the script that's the radius I want to use
    18. int offset = Radius / 2;
    19.  
    20. //Get the height Map of the wanted region (position - offset in both direction
    21. /*float[,] heights = terrain.terrainData.GetHeights (posX - offset, posY - offset
    22.     ,Radius, Radius);    */      
    23.  
    24. //GetHeights returns the array as float[height, width] instead of float[width, height]
    25. for(int k = posY - offset ; k < posY + offset ; k++){
    26.     for(int i = posX - offset ; i < posX + offset ; i++){
    27.         if(Vector2.Distance (new Vector2(posY, posX), new Vector2(k, i)) < riverRadius){
    28.             heights[k, i] = 40.0f / maxHeight;   //maxHeight must have the same value as the "height" in your terrain settings.
    29.         }
    30.     }
    31. }
    32. //Then you call setHeights, since getHeights was performed using a determined offset and radius, the function will only update that part
    33. //Keep in mind if your heightMap is over 513 units you will see a framerate drop when calling this function
    34. terrainData.SetHeights(posX - offset, posY - offset, heights);
    35.  
     
    Apicella likes this.
  5. Apicella

    Apicella

    Joined:
    Aug 30, 2014
    Posts:
    3
    Thank you!