[PHP] Generating Random Terrain Fractal

Discuss game development design and post your game ideas

Post » Fri Jul 27, 2012 1:49 pm

PHP script (and eventually Construct 2) that generates fractal terrain using Diamond Square Algorithm. The three links below show (in order), implementation inside Construct 2, Fractal Terrain with Gradient, Raw Fractal


In Action Inside Construct
Terrain Example
Raw Example

[CODE]<?

$DATA_SIZE = 513;
$SEED = 0.0;
$data = array();
$maxY = -1E32;
$minY = 1E32;
mt_srand($DATA_SIZE);
$RAND_MAX = mt_getrandmax();
$data[0][0] = $data[0][$DATA_SIZE-1] = $data[$DATA_SIZE-1][0] = $data[$DATA_SIZE-1][$DATA_SIZE-1] = $SEED;
$h = 200.0;

for ($sideLength = $DATA_SIZE-1; $sideLength >= 2; $sideLength /= 2, $h /= 2.0)     {
          $halfSide = $sideLength/2;
          for($x=0; $x<$DATA_SIZE-1;$x+=$sideLength) {
              for($y=0; $y<$DATA_SIZE-1; $y+=$sideLength) {

                    //x,y is upper left corner of the square
                    //calculate average of existing corners
                    $avg = $data[$x][$y] +                     //top left
                    $data[$x+$sideLength][$y]   +                    //top right
                    $data[$x][$y+$sideLength]   +                     //lower left
                    $data[$x+$sideLength][$y+$sideLength];      //lower right
          
                    $avg = $avg / 4.0;

                    //center is average plus random offset in the range (-h, h)
                    $offset = (-$h) + rand() * ($h - (-$h)) / $RAND_MAX;
                    $data[$x+$halfSide][$y+$halfSide] = $avg + $offset;

              } //for y
          } // for x
          
          //Generate the diamond values
          //Since diamonds are staggered, we only move x by half side
          //NOTE: if the data shouldn't wrap the x < DATA_SIZE and y < DATA_SIZE
          for ($x=0; $x<$DATA_SIZE; $x+=$halfSide) {
                    for ($y=($x+$halfSide)%$sideLength; $y<$DATA_SIZE; $y+=$sideLength) {
     
                         //x,y is center of diamond
                         //we must use mod and add DATA_SIZE for subtraction
                         //so that we can wrap around the array to find the corners
     
                         $avg =
                         $data[($x-$halfSide+$DATA_SIZE)%$DATA_SIZE][$y] +     //left of center
                         $data[($x+$halfSide)%$DATA_SIZE][$y]                    +     //right of center
                         $data[$x][($y+$halfSide)%$DATA_SIZE]                    +     //below center
                         $data[$x][($y-$halfSide+$DATA_SIZE)%$DATA_SIZE];     //above center
     
                         $avg = $avg / 4.0;
     
                         //new value = average plus random offset
                         //calc random value in the range (-h,+h)
                         $offset = (-$h) + rand() * ($h - (-$h)) / $RAND_MAX;
                         $avg = $avg + $offset;
                    
                         //update value for center of diamond
                         $data[$x][$y] = $avg;
     
                         //wrap values on the edges
                         //remove this and adjust loop condition above
                         //for non-wrapping values
                         //if ($x == 0) $data[$DATA_SIZE-1][$y] = $avg;
                         //if ($y == 0) $data[$x][$DATA_SIZE-1] = $avg;
                    } //for y
              } //for x
          } //for sideLength
     
     
          //Calculate minY and maxY values
     for ($i = 0; $i<$DATA_SIZE-1; $i++){
          for($j=0; $j<$DATA_SIZE-1; $j++) {
              if ($data[$i][$j] > $maxY){
                    $maxY = $data[$i][$j];
              }
              if ($data[$i][$j] < $minY){
                    $minY = $data[$i][$j];
              }
          }
     }
     
     createTerrain();
     
     // start of functions
     function createTerrain(){
        global $DATA_SIZE, $data, $maxY, $minY;
          $px = 0;
          $py = 0;
          $pz = 0;
          $pallete = array();
          // I use a 256px wide image gradient here to represent the colors for elevation
          $im = imagecreatefrompng("map.png");
          for($a = 0;$a<= 256;$a++){
              $rgb = imagecolorat($im, $a+1, 1);
              $r = ($rgb >> 16) & 0xFF;
              $g = ($rgb >> 8) & 0xFF;
              $b = $rgb & 0xFF;
              $pallete[$a][r] = $r;
              $pallete[$a][g] = $g;
              $pallete[$a] = $b;
          }
          $gd = imagecreatetruecolor($DATA_SIZE, $DATA_SIZE);
          for($x=0; $x < $DATA_SIZE-1; $x++){
               for($y=0; $y < $DATA_SIZE-1; $y++){
                            //populate the point struct
                        $px = $x;
                        $py = $data[$x][$y]; // color || height
                        $pz = $y;
                            //change range to 0..1
                            $py = ($py - $minY) / ($maxY - $minY);
                            $py = floor($py * 255);
                                   $c[0] = $pallete[$py][r];
                                   $c[1] = $pallete[$py][g];
                                   $c[2] = $pallete[$py];
                            $color = imagecolorallocate($gd, $c[0], $c[1], $c[2]);
                            imagesetpixel($gd, round($y),round($x), $color);
                     }
          }
          header('Content-Type: image/png');
          imagepng($gd);
          imagedestroy($gd);
      

}

function html2rgb($color){
    if ($color[0] == '#')
        $color = substr($color, 1);

    if (strlen($color) == 6)
        list($r, $g, $b) = array($color[0].$color[1],
                                 $color[2].$color[3],
                                 $color[4].$color[5]);
    elseif (strlen($color) == 3)
        list($r, $g, $b) = array($color[0].$color[0], $color[1].$color[1], $color[2].$color[2]);
    else
        return false;

    $r = hexdec($r); $g = hexdec($g); $b = hexdec($b);

    return array($r, $g, $b);
}

?>[/CODE]digitalsmeg2012-07-27 17:07:43
B
12
G
1
Posts: 19
Reputation: 1,066

Post » Sat Jul 28, 2012 1:01 am

well that's very interesting!

Think you could bias certain terrain areas to create stuff like biomes and other terrain features?
B
35
S
8
G
8
Posts: 532
Reputation: 6,868

Post » Sat Jul 28, 2012 1:09 pm

[QUOTE=Fimbul]Think you could bias certain terrain areas to create stuff like biomes and other terrain features?[/QUOTE]

I could.
B
12
G
1
Posts: 19
Reputation: 1,066


Return to Game Development, Design & Ideas

Who is online

Users browsing this forum: No registered users and 0 guests