Search Unity

How can I access the Terrain script?

Discussion in 'Editor & General Support' started by angel_m, Oct 8, 2019.

  1. angel_m

    angel_m

    Joined:
    Nov 4, 2005
    Posts:
    1,160
    Because, the terrain script is hidden in the unity editor, is there a way to access the Terrain script, assuming I have a terrain in the scene?
    I 'm using Unity 5.
     
  2. kdgalla

    kdgalla

    Joined:
    Mar 15, 2013
    Posts:
    4,639
    I don't know what you mean by the "terrain script" but certain aspects of a terrain object are scriptable through the API:
    https://docs.unity3d.com/560/Documentation/ScriptReference/Terrain.html
    On the menu to the left, also notice other terrain-related classes such as TerrainCollider and TerrainData. These may also have what you want, depending on what that is.
     
  3. angel_m

    angel_m

    Joined:
    Nov 4, 2005
    Posts:
    1,160
    In Unity 4 and 5 the terrain in the scene has two components:
    Terrain(Script) and Terrain Collider.
    When trying to edit or open the supposed Terrain script, through the provided menu, with the script editor, nothing happens and I am not able to find it in the project window.
     
  4. kdgalla

    kdgalla

    Joined:
    Mar 15, 2013
    Posts:
    4,639
    I don't have Unity 5 anymore but are you sure that these components are even scripts? Not every component is a script.
     
  5. angel_m

    angel_m

    Joined:
    Nov 4, 2005
    Posts:
    1,160
    The first (Terrain) is the main component and it is an script for sure.
     
  6. Kragh

    Kragh

    Joined:
    Jan 22, 2008
    Posts:
    657
    The terrain is a built in system, and so it is hidden from us. You can't open the "MeshRenderer" as well ;)
    BUT... There exists an API that lets you make changes to it, and with reflection you can probably even dig out some hidden stuff as well. I know I did MANY years ago. Here is a script that will place grass (Or some other detail element you have added to the terrain settings) on whatever texture you want it to. I made the basic functionality a decade ago (The stuff inside void CreateGrass()), and someone else made an actual EditorWindow around it. But it is still very simple, and not very helpful in the UI. Maybe it can get you going in terms of how to send and receive data from the terrain.

    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEditor;
    3.  
    4. public class GrassPlacement : EditorWindow
    5. {
    6.  
    7.     public Terrain terrain;
    8.     public int detailIndexToMassPlace;
    9.     public int[] splatTextureIndicesToAffect = new int[] { 0, };
    10.     public int detailCountPerDetailPixel = 1;
    11.  
    12.     [MenuItem("Tools/Terrain/Auto Grass Placement")]
    13.  
    14.     static void Init()
    15.     {
    16.         GrassPlacement window = (GrassPlacement)GetWindow(typeof(GrassPlacement));
    17.         window.Show();
    18.         window.titleContent = new GUIContent("Grass Creator");
    19.         window.Focus();
    20.         window.ShowUtility();
    21.         if (Selection.activeGameObject && Selection.activeGameObject.GetComponent<Terrain>())
    22.         {
    23.             window.terrain = Selection.activeGameObject.GetComponent<Terrain>();
    24.         }
    25.     }
    26.  
    27.     void OnGUI()
    28.     {
    29.         GUILayout.Label("Grass Creator", EditorStyles.boldLabel);
    30.         GUILayout.Label("Settings for Grass", EditorStyles.centeredGreyMiniLabel);
    31.  
    32.         ScriptableObject ter = this;
    33.         SerializedObject terrainso = new SerializedObject(ter);
    34.         SerializedProperty property = terrainso.FindProperty("terrain");
    35.         EditorGUILayout.PropertyField(property, new GUIContent("Terrain Object:", "Place your terrain object in here."), true);
    36.         terrainso.ApplyModifiedProperties();
    37.  
    38.         if (terrain != null)
    39.         {
    40.             detailCountPerDetailPixel = EditorGUILayout.IntSlider(new GUIContent("Detail Counter Per Detail Pixel:", "The detail count per detail pixel"), detailCountPerDetailPixel, 1, 16);
    41.             detailIndexToMassPlace = EditorGUILayout.IntSlider(new GUIContent("Detail Index to Place:", "Select the grass index to mass place"), detailIndexToMassPlace, 0, terrain.terrainData.detailPrototypes.Length - 1);
    42.  
    43.             ScriptableObject target = this;
    44.             SerializedObject so = new SerializedObject(target);
    45.             SerializedProperty stringsProperty = so.FindProperty("splatTextureIndicesToAffect");
    46.             EditorGUILayout.PropertyField(stringsProperty, new GUIContent("Splat Textures to place on:", "Indicate the splatmap index to place on."), true);
    47.             so.ApplyModifiedProperties();
    48.  
    49.             if (GUILayout.Button(new GUIContent("Mass Place Grass", "Work the magic :D")))
    50.             {
    51.                 CreateGrass();
    52.             }
    53.         }
    54.     }
    55.  
    56.     void CreateGrass()
    57.     {
    58.  
    59.         if (!terrain)
    60.         {
    61.             Debug.LogError("You have not selected a terrain object");
    62.             return;
    63.         }
    64.  
    65.         if (detailIndexToMassPlace >= terrain.terrainData.detailPrototypes.Length)
    66.         {
    67.             Debug.LogError("You have chosen a detail index which is higher than the number of detail prototypes in your detail libary. Indices starts at 0");
    68.             return;
    69.         }
    70.  
    71.         if (splatTextureIndicesToAffect.Length > terrain.terrainData.terrainLayers.Length)
    72.         {
    73.             Debug.LogError("You have selected more splat textures to paint on, than there are in your libary.");
    74.             return;
    75.         }
    76.  
    77.         for (int i = 0; i < splatTextureIndicesToAffect.Length; i++)
    78.         {
    79.             if (splatTextureIndicesToAffect[i] >= terrain.terrainData.terrainLayers.Length)
    80.             {
    81.                 Debug.LogError("You have chosen a splat texture index which is higher than the number of splat prototypes in your splat libary. Indices starts at 0");
    82.                 return;
    83.             }
    84.         }
    85.  
    86.         if (detailCountPerDetailPixel > 16)
    87.         {
    88.             Debug.LogError("You have selected a non supported amount of details per detail pixel. Range is 0 to 16");
    89.             return;
    90.         }
    91.  
    92.         int alphamapWidth = terrain.terrainData.alphamapWidth;
    93.         int alphamapHeight = terrain.terrainData.alphamapHeight;
    94.         int detailWidth = terrain.terrainData.detailResolution;
    95.         int detailHeight = detailWidth;
    96.         float resolutionDiffFactor = (float)alphamapWidth / detailWidth;
    97.         float[,,] splatmap = terrain.terrainData.GetAlphamaps(0, 0, alphamapWidth, alphamapHeight);
    98.         int[,] newDetailLayer = new int[detailWidth, detailHeight];
    99.  
    100.         for (int i = 0; i < splatTextureIndicesToAffect.Length; i++)
    101.         {
    102.  
    103.             for (int j = 0; j < detailWidth; j++)
    104.             {
    105.  
    106.                 for (int k = 0; k < detailHeight; k++)
    107.                 {
    108.                     float alphaValue = splatmap[(int)(resolutionDiffFactor * j), (int)(resolutionDiffFactor * k), splatTextureIndicesToAffect[i]];
    109.                     newDetailLayer[j, k] = (int)Mathf.Round(alphaValue * ((float)detailCountPerDetailPixel)) + newDetailLayer[j, k];
    110.                 }
    111.  
    112.             }
    113.  
    114.         }
    115.         terrain.terrainData.SetDetailLayer(0, 0, detailIndexToMassPlace, newDetailLayer);
    116.     }
    117. }
    As you can see, the main entrance point to getting to the inner workings of the terrain, is not through the "Terrain" object itself, but through the "terrainData" property it holds (Which is an object of type "TerrainData").
    So, as long as you have a reference to a Terrain object, you can access its terrainData object, and play around.
     
    Last edited: Oct 9, 2019
    ehsan_wwe1 likes this.
  7. angel_m

    angel_m

    Joined:
    Nov 4, 2005
    Posts:
    1,160
    Ok, thank you very much.