- Home /
Procedural Terrain - Diamond Square Algorithm
Hi,
I've been trying for the last 5 days to produce a simple procedural terrain in unity using the diamond square algorithm.
Unfortunately every example (12 at my last count) that I've found online is too complicated or has some kind of error in Unity. (i note that apparently the 'Java' in Unity is not the same as actual Java code).
My last attempt is from scratch and tries to emulate the example here My code below starts with 4 corners, then finds the middle point and height, followed by the 'sides' (north, east, south, west). After the first iteration it is supposed to 'reset' and find the middle & sides of 4 smaller squares.
As you can see in the picture, the first iteration works ok.
But, I can't get the 'reset' (which moves to the next level of detail) or 'next' (which moves to the next square) to actually change their values and start the iteration again.
Can anyone please help or advise?
Code is in JAVA.
function Start () {
// map size
var res = 65;
var step = (res-1);
var tData = GetComponent(Terrain).terrainData;
tData.heightmapResolution = res;
var heights = new float[res, res];
// bumpyness
var bumpyness = 0.03;
var width = res;
var length = res;
var reset = 0;
var next = 1;
//square
//if(reset == 0){
//heights[x,y] = Random.Range(0.1-bumpyness, 0.1+bumpyness);
//}
if(reset == 0){
heights[0,0] = Random.Range(0.1-bumpyness, 0.1+bumpyness);
heights[step,0] = Random.Range(0.1-bumpyness, 0.1+bumpyness);
heights[0,step] = Random.Range(0.1-bumpyness, 0.1+bumpyness);
heights[step,step] = Random.Range(0.1-bumpyness, 0.1+bumpyness);
}
for(next = 1; step*next < res; next++)
{
var x = step*next;
var y = step*next;
var A = heights[x-step, y-step];
var B = heights[x, y-step];
var C = heights[x-step, y];
var D = heights[x, y];
var Middle = (A+B+C+D)/4 + Random.Range(-bumpyness,bumpyness);
heights[x-step/2, y-step/2] = Middle;
var N = (A+B)/2 + Random.Range(-bumpyness,bumpyness);
heights[x-step/2, y-step] = N;
var E = (B+D)/2 + Random.Range(-bumpyness,bumpyness);
heights[x, y-step/2] = E;
var S = (C+D)/2 + Random.Range(-bumpyness,bumpyness);
heights[x-step/2, y] = S;
var W = (A+C)/2 + Random.Range(-bumpyness,bumpyness);
heights[x-step, y-step/2] = W;
print(next);
}
for(reset = 1; step*next >= res && reset < 3; reset++){
print(reset); step = step/2; x = 0; y = 0; next = 1;
}
tData.SetHeights (0, 0, heights);
}
Please don't say the code is in JAVA, it is clearly not in java. Java, JavaScript, and UnityScript (unity javascript) are all different.
Answer by AlucardJay · Sep 04, 2014 at 08:30 PM
I had a look at the code derived from Notchs Ludum Dare entry, but could not get it to work, so am unable to help with your current code :(
However, here is an implementation using code derived from : http://stackoverflow.com/questions/2755750/diamond-square-algorithm
This generates a new terrain every time it is run, but is really hard to control what type of terrain will be generated as it uses a random value for 'bumpiness'. Press space while running to generate a new terrain :
#pragma strict
public var terrain : Terrain;
public var size : int = 513;
var dataArray : float[,];
function Start()
{
DiamondSquareDataArray();
}
function Update()
{
if ( Input.GetKeyDown( KeyCode.Space ) )
DiamondSquareDataArray();
}
// http://stackoverflow.com/questions/2755750/diamond-square-algorithm
function DiamondSquareDataArray()
{
// declare the data array
dataArray = new float[ size, size ];
// set the 4 corners
dataArray[ 0, 0 ] = 1;
dataArray[ size - 1, 0 ] = 1;
dataArray[ 0, size - 1 ] = 1;
dataArray[ size - 1, size - 1 ] = 1;
var val : float;
var rnd : float;
var h : float = 0.5;
var sideLength : int;
var x : int;
var y : int;
var halfSide : int;
for ( sideLength = size - 1; sideLength >= 2; sideLength /= 2 )
{
halfSide = sideLength / 2;
// square values
for ( x = 0; x < size - 1; x += sideLength )
{
for ( y = 0; y < size - 1; y += sideLength )
{
val = dataArray[ x, y ];
val += dataArray[ x + sideLength, y ];
val += dataArray[ x, y + sideLength ];
val += dataArray[ x + sideLength, y + sideLength ];
val /= 4.0;
// add random
rnd = ( Random.value * 2.0 * h ) - h;
val = Mathf.Clamp01( val + rnd );
dataArray[ x + halfSide, y + halfSide ] = val;
}
}
// diamond values
for ( x = 0; x < size - 1; x += halfSide )
{
for ( y = ( x + halfSide ) % sideLength; y < size - 1; y += sideLength )
{
val = dataArray[ ( x - halfSide + size - 1 ) % ( size - 1 ), y ];
val += dataArray[ ( x + halfSide ) % ( size - 1 ), y ];
val += dataArray[ x, ( y + halfSide ) % ( size - 1 ) ];
val += dataArray[ x, ( y - halfSide + size - 1 ) % ( size - 1 ) ];
val /= 4.0;
// add random
rnd = ( Random.value * 2.0 * h ) - h;
val = Mathf.Clamp01( val + rnd );
dataArray[ x, y ] = val;
if ( x == 0 ) dataArray[ size - 1, y ] = val;
if ( y == 0 ) dataArray[ x, size - 1 ] = val;
}
}
h /= 2.0; // cannot include this in for loop (dont know how in uJS)
}
Debug.Log( "DiamondSquareDataArray completed" );
// Generate Terrain using dataArray as height values
GenerateTerrain();
}
function GenerateTerrain()
{
if ( !terrain )
return;
if ( terrain.terrainData.heightmapResolution != size )
terrain.terrainData.heightmapResolution = size;
terrain.terrainData.SetHeights( 0, 0, dataArray );
}
Alternative :
If you are looking at creating fractal looking terrain, have you considered using Perlin Noise? You have much more control with the terrain generated. Modify the variables in the inspector while running then press space to generate a new terrain :
#pragma strict
public var terrain : Terrain;
public var size : int = 513;
public var offset : Vector2 = Vector2.zero; // where does the sample start?
public var octaves : int = 8; // how many iteration for fractal?
public var frq : float = 100.0; // how smooth/rough? (low = rough, high = smooth)
public var amp : float = 0.5; // how strong? (0.5 is ideal)
var dataArray : float[,];
function Start()
{
PerlinFractalDataArray();
}
function Update()
{
if ( Input.GetKeyDown( KeyCode.Space ) )
PerlinFractalDataArray();
}
function PerlinFractalDataArray()
{
// declare the data array
dataArray = new float[ size, size ];
// variables used in calculations
var noise : float;
var gain : float;
var x : int;
var y : int;
var i : int;
var sample : Vector2;
// generate noise
for ( y = 0; y < size; y ++ )
{
for ( x = 0; x < size; x ++ )
{
noise = 0.0;
gain = 1.0;
for ( i = 0; i < octaves; i ++ )
{
sample.x = offset.x + ( parseFloat( x ) * ( gain / frq ) );
sample.y = offset.y + ( parseFloat( y ) * ( gain / frq ) );
noise += Mathf.PerlinNoise( sample.x, sample.y ) * ( amp / gain );
gain *= 2.0;
}
dataArray[ x, y ] = noise;
}
}
Debug.Log( "PerlinFractalDataArray completed" );
// Generate Terrain using dataArray as height values
GenerateTerrain();
}
function GenerateTerrain()
{
if ( !terrain )
return;
if ( terrain.terrainData.heightmapResolution != size )
terrain.terrainData.heightmapResolution = size;
terrain.terrainData.SetHeights( 0, 0, dataArray );
}
Hi,
Thanks for your help. I managed to get $$anonymous$$e to work in the end. It was an issue with global variables causing the problems. Although I'll have a look at the two you suggested and see how they look.
Your answer
Follow this Question
Related Questions
Multiple Cars not working 1 Answer
Simple 2D procedural galaxy map 2 Answers