- Home /
Possible to make custom editor script to set terrain height?
I'm working on a collab project for a 2D racing game involving hills. Since making smooth hills with an EdgeCollider2D would be a pain in the butt, I decided working with 3d colliders would be the best solution (plus I've experienced weird physics with 2d colliders in the past).
Creating the hills with a terrain collider wasn't bad at first. I used a view from behind the track (z-axis) to "paint in" the sprite using the terrain collider:
(Side note: These hills are just for testing the racing mechanics and not an actual track.)
Seemed okay at first, until I viewed everything from the side:
Oops. I wasn't expect all the hills to be unaligned. A solution I'd like to use is to make the height the same along the entire z-axis. I could do that with a script by looping over the height matrix, looping over the z-axis twice: the first time getting the max height, and setting the height the second time around. Of course, doing that at runtime would be completely unoptimal. It would be nice to have a button on a component that does this in the editor, but I'm not familiar with making custom editor scripts. Is it possible to do this with the terrain? Is there a better way of solving my problem?
(If necessary, I'll make the custom editor script myself, but I need to know how to make one.)
if you are making it as fully 2D, then terrain would be quite heavy for that purpose.. could try with flat mesh ins$$anonymous$$d. but for terrain can set heights using https://docs.unity3d.com/ScriptReference/TerrainData.SetHeights.html and to make editor script https://docs.unity3d.com/ScriptReference/EditorWindow.html
Well, we wanted to add trees later as background decorations (plus I don't really have any external programs for making meshes).
I didn't know we could make our own windows. I'll see if I can figure out the documentation and come up with something.
Answer by CaRmAgE · Apr 04, 2018 at 09:20 PM
I figured it out. This is the code I created to solve my problem (in case anyone else finds it useful for their 2D projects):
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using UnityEditor;
public class HillsGenerator2DWindow : EditorWindow {
public GameObject hills = null;
Terrain terrain;
// Use this for initialization
[MenuItem("Window/Generate Hills...")]
static void Init(){
EditorWindow window = EditorWindow.GetWindow(typeof(HillsGenerator2DWindow));
window.titleContent.text = "2D Hills Creator";
window.position = new Rect(0,0,180,80);
window.Show();
}
// OnGUI is called when drawing the window
void OnGUI(){
hills = (GameObject)EditorGUI.ObjectField(new Rect(3,3,position.width - 6,20),
hills,typeof(GameObject),true);
if(hills){
terrain = hills.GetComponent<Terrain>();
if(!terrain){
EditorGUI.LabelField(new Rect(3,25,position.width - 6,20),
hills.name+" does not have a Terrain component!");
} else if(!terrain.isActiveAndEnabled){
EditorGUI.LabelField(new Rect(3,25,position.width - 6,20),
hills.name+"'s Terrain component must be enabled!");
} else if(GUI.Button(new Rect(3,25,position.width - 6,20),"Generate Hills") &&
EditorUtility.DisplayDialog("Confirmation",
"Are you sure you want to '2D-ize' the TerrainData on "+hills.name+
"? You cannot undo this action.","Start Operation","Cancel")){
EditorUtility.DisplayProgressBar("Generating Hills...","Initializing...",0f);
float heightmapHeight = (float)terrain.terrainData.heightmapHeight;
foreach(int i in GenerateHills(terrain)){
float progress = i / heightmapHeight;
if (progress < 1.0f){
EditorUtility.DisplayProgressBar("Generating Hills...",
"Updating samples "+(i+1)+" / "+heightmapHeight+"...",progress);
} else {
EditorUtility.DisplayProgressBar("Generating Hills...","Finalizing...",
1.0f);
}
}
EditorUtility.ClearProgressBar();
EditorUtility.DisplayDialog("Hills Generated","The terrain on "+
hills.name+" has been '2D-ized'.","OK");
}
} else {
EditorGUI.LabelField(new Rect(3,25,position.width - 6,20),"Select a GameObject.");
}
}
// OnInspectorUpdate is called whenever a change is made in the inspector view
void OnInspectorUpdate(){
Repaint();
}
// Update the terrain heightmap to make heights the same across the entire Z-axis
private IEnumerable<int> GenerateHills(Terrain terrain){
TerrainData terrainData = terrain.terrainData;
float maxHeight, currentHeight;
int heightmapWidth = terrainData.heightmapWidth;
int heightmapHeight = terrainData.heightmapHeight;
float[,] heightmap = terrainData.GetHeights(0,0,heightmapWidth,heightmapHeight);
for(int x=0; x<heightmapHeight; x++){
maxHeight = 0f;
yield return x;
for(int z=0; z<heightmapWidth; z++){
currentHeight = heightmap[z,x];
if(currentHeight > maxHeight){
maxHeight = currentHeight;
}
}
for(int z=0; z<heightmapWidth; z++){
heightmap[z,x] = maxHeight;
}
}
yield return heightmapHeight;
terrainData.SetHeights(0,0,heightmap);
}
}