程序岛地形生成

Procedural Island Terrain Generation

编辑:在尝试了一些事情后重写了我的问题并使其更具体。

你好,我正在创建一个 带有程序生成地图的移动 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/ 的图片显示了我想要的那种地形。那里的教程很棒,但过于密集,因此 post.

我想要带有 Perlin 噪声的随机形状岛屿,该岛屿被水包围。

编辑:基本的 Perlin 地形生成现在可以使用 =)

这是我的代码。附加到 null 的脚本,带有激活 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;
}

}

您显示的图像来自article描述如何生成它

是的。有问题的文章使用了waaay复杂的方法。

最好的方法是采用一个表示基本岛屿形状的函数,高度值介于 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);
        }
    }
}