Когда попались на глаза первые упоминания об этом алгоритме (Diamond-Square), заинтересовало. Алгоритм начинает работу с двумерного массива, затем, из четырех начальных значений, случайным образом генерирует карту высот, упорядоченную в виде сетки из точек так, чтобы весь массив (массив описывает условную карту поверхности) была покрыта квадратами. Строить «бесконечные» ландшафты островов, гор, рек и морей — ценная возможность при создании игрушек. В рунете одна из самых первых заметок, которая находится по запросу, это заметка Дениса Ольшина на хабре —
Алгоритм «diamond-square» для построения фрактальных ландшафтов (автору спасибо!). Поскольку итоговая демка не доступна, нашел отличную джаваскритовую реализацию Diamond-Square на jsfiddle. Не знакомясь с самим алгоритмом, поменяйте в фидле параметр smooth c 0.9 на 0.4 и нажмите «Run» в верхней левой части страницы сервиса: Diamond-Square превратит пустыню в островной ландшафт! Теперь вы точно чувствуете что хотите реализовать этот алгоритм на PHP? Тогда вперед!
П.с. разобраться в нём все же придётся до построения, потому найдите время — может пригодится.
PHP-код Diamond-Square
Код Diamond-Square на гитхабе. При переписывании кода на PHP, стояла задача внести минимальное количество изменений в исходный алгоритм. Теперь разобравшись с вариантом создания ландшафта на одном языке, будет проще рассмотреть вариант на другом. С другой стороны, есть потенциал по изменению скрипта: переделка кода на ООП, задание других цветов для карты высот, использование алгоритмов аппроксимации изображений (сглаживание углов, использование спрайтов).
<?php
$size = 8;
$zoom = 5;
$smooth = .9;
$dim = '3d';
$size = pow(2, $size) + 1;
$matrix = [];
$length = $size*$size;
for($i = 0; $i < $length; $i++) $matrix[] = 0;
$width = $height = $size * $zoom;
header ('Content-Type: image/png');
$im = imagecreatetruecolor ( $width , $height );
function toI($x, $y) {
global $size;
return $x + $y * $size;
}
function toXY($i) {
return [
"x" => floor($i / $size),
"y" => $i % $size
];
}
function genColor($w) {
global $im;
$w = ($w - 0.7) * 1700;
if ($w < 0) return imagecolorallocate($im, 117,212,242);
if ($w < 200) return imagecolorallocate($im, 180,198,20);
if ($w < 500) return imagecolorallocate($im, 199,210,26);
if ($w < 1000) return imagecolorallocate($im, 254,231,115);
if ($w < 2000) return imagecolorallocate($im, 249,209,49);
if ($w < 3000) return imagecolorallocate($im, 236,168,32);
if ($w < 4000) return imagecolorallocate($im, 176,141,119);
if ($w < 5000) return imagecolorallocate($im, 199,176,162);
if ($w < 6000) return imagecolorallocate($im, 224,208,195);
return imagecolorallocate($im, 255, 255, 255);
}
function drawPoint($x, $y, $s, $step) {
global $im,$matrix,$smooth,$zoom;
$points = [];
if ($step == 'square') {
if (is_numeric($matrix[toI($x-$s, $y-$s)]))
array_push($points,$matrix[toI($x-$s, $y-$s)]);
if (is_numeric($matrix[toI($x+$s, $y-$s)]))
array_push($points,$matrix[toI($x+$s, $y-$s)]);
if (is_numeric($matrix[toI($x-$s, $y+$s)]))
array_push($points,$matrix[toI($x-$s, $y+$s)]);
if (is_numeric($matrix[toI($x+$s, $y+$s)]))
array_push($points,$matrix[toI($x+$s, $y+$s)]);
} else {
if (is_numeric($matrix[toI($x-$s, $y)]))
array_push($points,$matrix[toI($x-$s, $y)]);
if (is_numeric($matrix[toI($x+$s, $y)]))
array_push($points,$matrix[toI($x+$s, $y)]);
if (is_numeric($matrix[toI($x, $y-$s)]))
array_push($points,$matrix[toI($x, $y-$s)]);
if (is_numeric($matrix[toI($x, $y+$s)]))
array_push($points,$matrix[toI($x, $y+$s)]);
}
$sum = array_sum($points);
if($points)
$avg = $sum / count($points);
else
$avg = 0;
$matrix[toI($x, $y)] = $weight = $avg + rand(0, $s*$smooth*100)/100;
$color = genColor($weight);
if ($dim == '2d') {
imagefilledrectangle ( $im , $x * $zoom, $y * $zoom, ($x+1) * $zoom, ($y+1) * $zoom , $color );
} else {
imagefilledrectangle ( $im , $x * $zoom, $y * $zoom, $x * $zoom+($x+1) * $zoom, $y * $zoom+($y+1) * $zoom , $color );
}
}
$s = $size;
while($s > 1)
{
$s = ceil($s / 2);
for($x=0; $x<$size; $x+=$s*2) {
for($y=0; $y<$size; $y+=$s*2) {
drawPoint($x+$s, $y+$s, $s, 'square');
}
}
for($x=0; $x<$size; $x+=$s*2) {
for($y=0; $y<$size; $y+=$s*2) {
drawPoint($x+$s, $y, $s, 'diamond');
drawPoint($x+$s, $y+$s*2, $s, 'diamond');
drawPoint($x, $y+$s, $s, 'diamond');
drawPoint($x+$s*2, $y+$s, $s, 'diamond');
}
}
}
imagepng($im);
imagedestroy($im);