Процедурное островное рельеф местности
Изменить: переписал мой вопрос после попытки несколько вещей и сделал его более конкретным.
Привет, так что я создаю мобильную игру RTS с процедурно сгенерированными картами. Я разработал, как создать ландшафт с базовым перлин-шумом, и попытался интегрировать https://gamedev.stackexchange.com/questions/54276/a-simple-method-to-create-island-map-mask метод создания острова процедурно. Это результат пока:
Изображение ниже от http://www-cs-students.stanford.edu/~amitp/game-programming/polygon-map-generation/ показывает тип местности, которая мне нужна. Учебник там отличный, но будет слишком интенсивным, поэтому пост.
Я хочу, чтобы остров произвольной формы с перлиновым шумом, окруженный водой.
редактировать: Базовый Perlin terrain gen работает сейчас =)
Вот мой код Скрипт, прикрепленный к нулю с кнопкой для активации Begin ():
using UnityEngine;
using System.Collections;
using System.Runtime.InteropServices;
public class Gen_Perlin : MonoBehaviour {
public float Tiling = 0.5f;
private bool active = false;
public int mapHeight = 10;
public void Begin()
{
if (active == false) {
TerrainData terrainData = new TerrainData ();
const int size = 513;
terrainData.heightmapResolution = size;
terrainData.size = new Vector3 (2000, mapHeight, 2000);
terrainData.heightmapResolution = 513;
terrainData.baseMapResolution = 1024;
terrainData.SetDetailResolution (1024, 1024);
Terrain.CreateTerrainGameObject (terrainData);
GameObject obj = GameObject.Find ("Terrain");
obj.transform.parent = this.transform;
if (obj.GetComponent<Terrain> ()) {
GenerateHeights (obj.GetComponent<Terrain> (), Tiling);
}
} else {
GameObject obj = GameObject.Find ("Terrain");
if (obj.GetComponent<Terrain> ()) {
GenerateHeights (obj.GetComponent<Terrain> (), Tiling);
}
}
}
public void GenerateHeights(Terrain terrain, float tileSize)
{
Debug.Log ("Start_Height_Gen");
float[,] heights = new float[terrain.terrainData.heightmapWidth, terrain.terrainData.heightmapHeight];
for (int i = 0; i < terrain.terrainData.heightmapWidth; i++)
{
for (int k = 0; k < terrain.terrainData.heightmapHeight; k++)
{
heights[i, k] = 0.25f + Mathf.PerlinNoise(((float)i / (float)terrain.terrainData.heightmapWidth) * tileSize, ((float)k / (float)terrain.terrainData.heightmapHeight) * tileSize);
heights[i, k] *= makeMask( terrain.terrainData.heightmapWidth, terrain.terrainData.heightmapHeight, i, k, heights[i, k] );
}
}
terrain.terrainData.SetHeights(0, 0, heights);
}
public static float makeMask( int width, int height, int posX, int posY, float oldValue ) {
int minVal = ( ( ( height + width ) / 2 ) / 100 * 2 );
int maxVal = ( ( ( height + width ) / 2 ) / 100 * 10 );
if( getDistanceToEdge( posX, posY, width, height ) <= minVal ) {
return 0;
} else if( getDistanceToEdge( posX, posY, width, height ) >= maxVal ) {
return oldValue;
} else {
float factor = getFactor( getDistanceToEdge( posX, posY, width, height ), minVal, maxVal );
return oldValue * factor;
}
}
private static float getFactor( int val, int min, int max ) {
int full = max - min;
int part = val - min;
float factor = (float)part / (float)full;
return factor;
}
public static int getDistanceToEdge( int x, int y, int width, int height ) {
int[] distances = new int[]{ y, x, ( width - x ), ( height - y ) };
int min = distances[ 0 ];
foreach( var val in distances ) {
if( val < min ) {
min = val;
}
}
return min;
}
}
1 ответ
Да уж. В данной статье используется очень сложный метод.
Лучший способ сделать это - взять функцию, которая представляет форму вашего базового острова, со значениями высоты от 0 до 1. Для типа острова на картинке вам в основном нужно что-то, что плавно поднимается с краев, и плавно возвращается к нулю там, где вы хотите озер.
Теперь вы либо добавляете эту поверхность к своей базовой фрактальной поверхности (если вы хотите сохранить заостренность на малых высотах), либо умножаете ее (если хотите, чтобы более низкие высоты были плавными). Затем вы определяете высоту, ниже которой находится вода.
Вот мой очень быстрый способ сделать это, визуализированный с помощью Terragen:
Я использовал функцию, которая поднимается кольцом от края карты к середине к середине, а затем снова опускается, чтобы соответствовать форме, аналогичной той, что была в статье. На практике вы можете использовать это только для получения формы острова, а затем вырезать участок местности, соответствующий контуру, и закопать все остальное.
Я использовал свой собственный генератор фрактальных ландшафтов, как описано здесь:https://fractal-landscapes.co.uk/ для базового фрактала.
Вот код C#, изменяющий ландшафт:
public void MakeRingIsland()
{
this.Normalize(32768);
var ld2 = (double) linearDimension / 2;
var ld4 = 4 / (double) linearDimension;
for (var y = 0u; y < linearDimension; y++)
{
var yMul = y * linearDimension;
for (var x = 0u; x < linearDimension; x++)
{
var yCoord = (y - ld2) * ld4;
var xCoord = (x - ld2) * ld4;
var dist = Math.Sqrt(xCoord * xCoord + yCoord * yCoord);
var htMul = dist > 2 ? 0 :
(dist < 1 ?
dist + dist - dist * dist :
1 - (dist - 1) * (dist - 1));
var height = samples[x + yMul];
samples[x + yMul] = (int) (height + htMul * 32768);
}
}
}
Изображение, которое вы показываете, взято из статьи, описывающей, как его генерировать