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:

Back view of terrain
(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:
Side view of terrain

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.)

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);
	}
}