- Home /
Terrain hill generating by code
Hello, friends.
I have a small problem which I couldnt find a solution for.
I am working on a terrain which randomly generates hills on it, I have made a method which makes hills, but makes them very pointy, more like round pyramids.
Thats the call for the method:
CreateHill(Random.Range(0,513),Random.Range(0,513),Random.Range(0.005f,0.1f),Random.Range(1f,10f));
and thats the method itself:
public void CreateHill(int x, int y, float height,float pointyness)
{
float point = 0;
float distanceFromTop;
terrainGRID[x,y]=height;
for(int a=0;a<513;a++)
{
for(int b=0;b<513;b++)
{
distanceFromTop=Mathf.Sqrt( Mathf.Pow((y-b),2) + Mathf.Pow((x-a),2) );
point=((height-terrainGRID[a,b])*1000 - distanceFromTop*pointyness)/1000 ;
if(point<0)
point=0;
terrainGRID[a,b]+=point;
}
}
}
what comes from it is:
![alt text][1]
What I'm trying to achive is a hilly hill, something more like ![alt text][2]
(The second one was made in the editor, not by code lol).
MANY thanks for anyone who helps me out! :) [1]: /storage/temp/13506-untitled.png [2]: /storage/temp/13507-untitled2.png
Answer by tnetennba · Jul 25, 2013 at 01:26 PM
you need something like this to smooth your terrain points and round everything off a little. It's not the best example of terrain smoothing but it should give you an idea of what to do.
private void Smooth()
{
float[,] height = terrain.terrainData.GetHeights(0, 0, terrain.terrainData.heightmapWidth,
terrain.terrainData.heightmapHeight);
float k = 0.5f;
/* Rows, left to right */
for (int x = 1; x < terrain.terrainData.heightmapWidth; x++)
for (int z = 0; z < terrain.terrainData.heightmapHeight; z++)
height[x, z] = height[x - 1, z] * (1 - k) +
height[x, z] * k;
/* Rows, right to left*/
for (int x = terrain.terrainData.heightmapWidth - 2; x < -1; x--)
for (int z = 0; z < terrain.terrainData.heightmapHeight; z++)
height[x, z] = height[x + 1, z] * (1 - k) +
height[x, z] * k;
/* Columns, bottom to top */
for (int x = 0; x < terrain.terrainData.heightmapWidth; x++)
for (int z = 1; z < terrain.terrainData.heightmapHeight; z++)
height[x, z] = height[x, z - 1] * (1 - k) +
height[x, z] * k;
/* Columns, top to bottom */
for (int x = 0; x < terrain.terrainData.heightmapWidth; x++)
for (int z = terrain.terrainData.heightmapHeight; z < -1; z--)
height[x, z] = height[x, z + 1] * (1 - k) +
height[x, z] * k;
terrain.terrainData.SetHeights(0, 0, height);
}
Looks good, I'll try it out and see if it works for this issue :)
It works great! $$anonymous$$any thanks, that is what I was looking for! :)
Answer by raygun · Sep 01, 2016 at 08:36 AM
OK, so I took your code that process a grid, using the distance from the centre to calculate the hill height.
Now, if you use a linear function you'll get a linear/sharp hill.
So, my version uses a bezier function which creates a nice sloping hill.
[ExecuteInEditMode] public class HillGenerator : MonoBehaviour { public int x; public int y; public int radius; public float height; }
[CustomEditor (typeof(HillGenerator))]
public class HillGeneratorEditor : Editor
{
public static Vector2 BezierCurve(float t, Vector2 pOne, Vector2 pTwo, Vector2 pThree, Vector2 pFour)
{
return Mathf.Pow (1 - t, 3) * pOne + 3 * Mathf.Pow (1 - t, 2) * t * pTwo + 3 * (1 - t) * t * t * pThree + Mathf.Pow (t, 3) * pFour;
}
public override void OnInspectorGUI()
{
DrawDefaultInspector ();
var script = this.target as HillGenerator;
if (GUILayout.Button ("Generate"))
{
this.CreateHill (script.x, script.y, script.height, script.radius);
}
}
public void CreateHill(int x, int y, float height, int radius)
{
var diameter = radius * 2;
var heightsCenteX = radius;
var heightsCenteY = radius;
var baseX = x - heightsCenteX;
var baseY = y - heightsCenteY;
var script = this.target as HillGenerator;
var terrianData = script.GetComponent<Terrain> ().terrainData;
var heights = terrianData.GetHeights (baseX, baseY, diameter, diameter);
var controlPoint1 = new Vector2 (0.52f, 0.06f);
var controlPoint2 = new Vector2 (0.42f, 0.95f);
for (int a = 0; a < diameter; a++)
{
for (int b = 0; b < diameter; b++)
{
var distanceFromCenter = Mathf.Sqrt (Mathf.Pow ((heightsCenteY - b), 2) + Mathf.Pow ((heightsCenteX - a), 2));
var time = Math.Max (1 - (distanceFromCenter / radius), 0);
var bezierPoint = BezierCurve (time, new Vector2 (0, 0), controlPoint1, controlPoint2, new Vector2 (1, 1));
var pointHeight = bezierPoint.y * height;
if (pointHeight < 0)
pointHeight = 0;
heights [a, b] = pointHeight;
}
}
terrianData.SetHeights (baseX, baseY, heights);
}
}
example: CreateHill(250, 250, 0.3, 100)
To visualise the curve you can use this site: http://www.css3beziercurve.net
controlPoint1 = site.point1 & 2
controlPoint2 = site.point3 & 4
Attach the script to a Terrain
, set up the properties and hit Generate.