1. ホーム
  2. c++

[解決済み] Perlin Noiseを用いた2D地形生成(タイルベース)

2022-02-14 14:44:34

質問

基本的に「2次元のサイドビュー」的なゲーム(シミュレーション)に取り組んでいるアプリケーションのために、パーリンノイズについて調べています。私はperlin noiseがどのように機能するか完全に理解しているわけではありませんが、一般的なアイデアは持っています。

この記事でコードを見つけました。 パーリンノイズ しかし、土地は想像通りとても平らです。そこで、Perlinのノイズが発生すると考え、上記のサイトにあるコードを使用しましたが、ノイズを固体/空気タイルに変換する方法がよくわかりません。

ご覧の通り、あまりリアルな地形ではありません。

山や丘のような地形にしたいです。

タイル配列を反復して、ノイズを上記に変える方法がよくわかりません。

現在、私はタイル配列(2次元)をこのように反復していますが、最初の画像からわかるように、それはタイルが固体であるかどうかをランダムにするだけで、なだらかな丘や山を作ることはありません。

for(int Y = 0;Y < HEIGHT;++Y) {
    for(int X = 0;X < WIDTH;++X) {
        Tile Tile;
        if(noise(X,Y) < 0) {
            Tile.Type = BLOCK;
        } else {
            Tile.Type = Air;
        }

        // Then I push the Tile on to a vector

また、この16x16の「チャンク」の右側には別のチャンクがあり、このチャンクと一致させる必要があり、その次のチャンクも同様です。チャンクは前と同じであるべきではなく、別々のチャンクでないように見せかけるだけです。

どんなことでもご相談ください。ありがとうございました。

アップデイト

私は、Perlinの関数を、以下のように変更しました。 これ とphresnelが提案したものを実装しました。

    PerlinNoise *test=new PerlinNoise(0.25, 6);    //0.25 is Persistance 6 is Octaves

        float f,h;
        float seed = 804.343;

        for (int X=0; X!=MAP_WIDTH; ++X) {

            f = test->GetNoise((X * ChunkX) * seed);

            h = f * MAP_HEIGHT + (MAP_HEIGHT / 2);

            for (int y=0; y<h; ++y) {
                Tile tempTile;
                tempTile.Tile = TILE_NONE;
                Tiles[X][y] = tempTile;
            }
            for (int y=h; y<MAP_HEIGHT; ++y) {
                Tile tempTile;
                tempTile.TileID = TILE_BLOCK;
                Tiles[X][y] = tempTile;
            }
        }

これを行った後、見た目はかなり良くなりました。 X * ChunkX をそれぞれ ノイズ そうしないと、すべてのチャンクが同じになってしまうので、今は次のようになっています。

ご覧のように、地形は以前よりも私の望むものになっていますが、まだあまり似ていないようです。 リアル 地形があまり滑らかでないのですが、全体的な見た目を滑らかにするために、何かできることはありますか? なだらかな丘 平野

そのノイズ値の下にあるタイルの作成を削除して、そのノイズ値が見やすくなるようにしました。 範囲

ご覧の通り、見た目は問題ありません。ただ、擬似乱数値のように見えないように、値を「平滑化」する必要があります。また、左側にある最初のチャンクがずっと同じ値であることも不思議です。

ありがとうございます。

解決方法は?

あなたの結果は、島、大陸、池、海があるという点では問題ないでしょう。パーリンノイズは、非ランダムな地形を生成するのに役立ちます。

あなたは基本的に、ノイズ関数の(数学的に)無限の解像度を、ブロックの非常に限定された解像度とともに、たった2種類のタイルの離散集合に縮小しています。そう考えると、あなたの結果は問題ないように思えます。

違うことをすると、結果が良くなるかもしれません。例えば、タイルグラフィックの間にラープを入れるなどです。

// Exemplary p-code, fine-tuning required
Color color_at (x, y) {
    Real u = x / width, v = y / height
    Real  f  = min (max(noise(u,v), 0), 1);
    Color c  = water*f + hill*(1-f);
    return c;
}

lerp関数を任意のtile-countに一般化することができます。

シャープエッジのもう一つの可能性は、次のようなものです。 ボロノイ図 (1) . 上記と同じ関数ですが、補間するのではなく、ノイズの値に最も近いタイルを取るということですね。

Color color_at (x, y) {
    Real u = x / width, v = y / height
    Real  f  = min (max(noise(u,v), 0), 1);
    if (f<0.5) return water;
    else return hill;
}

これは、ピクセル単位で行うことを除けば、あなたのものと同じです。これもまた、一般化できるかもしれません。

うまく設定できたら、植物・木の分布、敵の分布、アニメーション(これは1d-noise関数になります)など、あらゆるものにノイズを使うことができます。


コメントについて編集します。

連続した1d-地形。

古典的な2Dジャンプ&ランのようにサイドビューが必要な場合は、1Dノイズ関数を考え、0から画像幅まで繰り返し、各停止点でサンプルを取得します。 f というノイズ関数に変換します。 f をスクリーンスペースに変換すると、上の画素はすべて空の一部となり、下の画素はすべてタイルから取得されます。

for (int x=0; x!=width; ++x) {

    // Get noise value:
    f = noise1d (x/width);
    h = f * height + (height/2);  // scale and center at screen-center

    // Select tile:
    Tile *tile;
    if (h < waterLimit) {
        h    = waterLimit;
        tile = waterTile;
    } else {
        tile = terrainTile;
    }

    // Draw vertical line:
    for (int y=0; y<h; ++y)
        setPixel (x, y, skyColor(x,y));
    for (int y=h; y<height; ++y)
        setPixel (x, y, tileColor(x,y, tile));
}

再び、模範的な擬似コードです。

現在の設定で2d-levelsをフルに活用する。

ノイズ関数は非常に柔軟です。もし青を減らして緑を増やしたいなら、定数項を下げればいいだけです。 if(h < 0) -> blue を実行します。 if(h < -0.25) -> blue .


1: 私は3D地形レンダリングに興味があるので、この方法は試したことがありません( picogen.org )