7

Unity Game Engine でゲームを開発してきましたが、ページ化された地形を無限の世界 (最近では一般的なテーマ) に使用できるようになることを願っています。私の地形ジェネレーターは、パーリン ノイズのみを使用します。しかし、現時点では、バグによって開発が大幅に遅れています。ページの端が一致せず、近くにもありません。問題は、地形を生成するコードのどこか、またはページの生成が完了するたびに実行される別のコードのどこかにある可能性があります。しかし、ユニティ関数の呼び出しに問題があるわけではないので、ユニティに慣れていなくても助けていただけます。

考えられる原因... 1) 各ページの間違った場所でサンプリングされたパーリン ノイズ、数学の失敗。2) 乱数ジェネレーターは、ページの生成の間に再シードされています。3) ページ生成の間にパーリン ノイズが再シードされます。4) ? 知らない。

注: 他のすべてのコードを見て、番号 2 と 3 をほぼ除外しました。私はジェネレーターを 1 回ずつシードするだけで、Unity はジェネレーターの実行の間に Random を再シードすることはありません。

実際、これを簡単にするデバッグの方法を説明できる場合は、アドバイスしてください。長期的には、それは私をはるかに助けるでしょう.

Unity3D を知らなくてもいいように、ページごとに 1 回実行するコードを次に示します。完全なコメントが付いています。

 /* x and y are the indices of the tile being loaded. The game maintains a
    square of pages loaded, where the number of pages per side is equal to
    loadSquares (see below). x and y are the indices of the page to generate
    within that square. */
    public void generate(int x, int y)
    {
         /* pagePos represents the world x and y coordinates of the bottom left
            corner of the page being generated. This is given by...
              new Vector2((-(float)loadSquares / 2.0f + x + xCoord) * tileSize, 
                          (-(float)loadSquares / 2.0f + y + zCoord) * tileSize);
            This is because the origin tile's center is at 0, 0. xCoord represents
            the tile that the target object is on, which is what the loaded square
            is centered around. tileSize is the length of each side of each tile.
         */
        Vector2 pagePos = getPagePos(x, y);
        //Here I get the number of samples x and y in the heightmap.
        int xlim = td[x,y].heightmapWidth;
        int ylim = td[x,y].heightmapHeight;
            //The actual data
        float[,] array = new float[xlim, ylim];
            //These will represent the minimum and maximum values in this tile.
            //I will need them to convert the data to something unity can use.
        float min = 0.0f;
        float max = 0.0f;
        for(int cx = 0; cx < xlim; cx++)
        {
            for(int cy = 0; cy < ylim; cy++)
            {
                //Here I actually sample the perlin function.
                //Right now it doesn't look like terrain (intentionally, testing).
                array[cx,cy] = sample(
                    new Vector3((float)cx / (float)(xlim - 1) * tileSize + pagePos.x, 
                                (float)cy / (float)(ylim - 1) * tileSize + pagePos.y, 
                    122.79f));
                //On the first iteration, set min and max
                if(cx == 0 && cy == 0)
                {
                    min = array[cx,cy];
                    max = min;
                }
                else
                {
                    //update min and max
                    min = Mathf.Min(min, array[cx,cy]);
                    max = Mathf.Max(max, array[cx,cy]);
                }
            }
        }
        //Set up the Terrain object to receive the data
        float diff = max != min ? max - min : 10.0f;
        tr[x,y].position = new Vector3(pagePos.x, min, pagePos.y);
        td[x,y].size = new Vector3(tileSize, diff, tileSize);
        //Convert the data to fit in the Terrain object
     /* Unity's terrain only accepts values between 0.0f and 1.0f. Therefore,
        I shift the terrain vertically in the code above, and I
        squish the data to fit below.
     */
        for(int cx = 0; cx < xlim; cx++)
        {
            for(int cy = 0; cy < ylim; cy++)
            {
                array[cx,cy] -= min;
                array[cx,cy] /= diff;
            }
        }
        //Set the data in the Terrain object
        td[x,y].SetHeights(0, 0, array);
    }
}

興味深い、おそらく重要な詳細: タイルは、x/z 方向の角に隣接するタイルと適切に接続されています。タイルが同じ x-to-z デルタを持たない場合はいつでも、サンプリング シフトが発生します。下の画像では、右側のタイルは他のタイルから +x と +z です。この関係にあるタイルはすべて正しく接続されています。

ここに画像の説明を入力

zip でアップロードされたプロジェクト ファイルを次に示します。動作しないかどうか教えてください... http://www.filefactory.com/file/4fc75xtd3yzl/n/FPS_zip

地形を見るには、そこから始まらない場合は testScene に切り替えてから Play を押します。GameObject はデータと地形オブジェクトを生成します (scripts/General/ からの RandomTerrain スクリプトが添付されています)。そこからパラメーターをパーリン ノイズに変更できます。現在、最初のパーリン オクターブである o_elevator のみが地形生成でアクティブであることに注意してください。このグリッチを解決する目的で、他のすべてのパブリック perlin オクターブ変数は影響を与えません。

4

1 に答える 1

1

TL; DR:getTargetCoordsを削除して、を使用する必要がありますtr[x,y].position = new Vector3(pagePos.y, 0.0f, pagePos.x)

上記に到達する方法:一度に1次元で作業します。これは、(添付のユニティコードから)これを変えることを意味します:

            array[cx,cy] = sample(
                new Vector3((float)cx * tileSize / (float)(xlim - 1) + pagePos.x, 0.0f,
                            (float)cy * tileSize / (float)(ylim - 1) + pagePos.y));

これに:

            array[cx,cy] = sample(
                new Vector3(
                    (float)cx * tileSize / (float)(xlim - 1) + pagePos.x,
                    0.0f,
                    0.0f
                ) 
            ); 

getPagePosから値を追加していてgetTargetCoords、その関数が関数によって設定された位置を使用していたため、あなたのに何か問題があることにすぐに気付きましたgetPagePos。サークル内のサークル、皆さん。

protected void getTargetCoords()
{
    xCoord = Mathf.RoundToInt(target.position.x / tileSize);
    zCoord = Mathf.RoundToInt(target.position.z / tileSize);
}

それで、その関数とそれへの参照を失いましょう。getPagePos実際、今のところ単純化しましょう。

protected Vector2 getPagePos(int x, int y)
{
    return new Vector2( 0.0f,0.0f);
}

OH MY GOD IS TAWNOS CRAZY ?! ええ、そうですが、それは重要なことではありません。サンプル関数が正しいと仮定すると(私があなたの主張に基づいてあなたに与えた仮定)、単純な値(ゼロ!)を取得し、追加の各仮定を追加して検証することから始める必要があります。

簡略化して、をgetPagePosもう一度見てみましょう(float)cx * tileSize / (float)(xlim - 1) + pagePos.x。何をしようとしているのですか?タイルの原点からオフセットして、現在のサンプルの世界座標点を見つけようとしているようです。それが本当の仮定であることを確認しましょう。ユニットにxLimサンプルが分散してtileSizeいるため、ワールド座標の各ステップサイズはですtileSize / xLim。これに現在の値を掛けるとオフセットが得られるので、このコードは正しく見えます。

次に、単純なタイルオフセットを取得して、すべてが一致することを確認しましょう。ドロップをすべてのタイルの中央に配置するのではなく、単純なグリッドオフセットを実行します(これは後で調整して中央に戻すことができます)。

    return new Vector2( 
        tileSize * (float)x,
        0.0f
    );

これを実行すると、問題がすぐにわかる場合があります。下端は、右端が左端と一致するのではなく、隣のタイルの上端をマップします。深く掘り下げないでください。これは、サンプリング方法の動作方法が原因である可能性があります。わからない。更新された関数は次のとおりです。

protected Vector2 getPagePos(int x, int y)
{
    float halfTile = loadSquares / 2.0f;
    return new Vector2( 
        (-halfTile * tileSize) + (x * tileSize),
        (-halfTile * tileSize) + (y * tileSize)
    );
}

public void generate(int x, int y)
{
    if(x >= loadSquares || x < 0 || y >= loadSquares || y < 0) return;
    t[x,y] = Terrain.CreateTerrainGameObject(new TerrainData()).GetComponent<Terrain>();
    t[x,y].name = "Terrain [" + x + "," + y + "]";
    td[x,y] = t[x,y].terrainData;
    td[x,y].heightmapResolution = resolution;

    tr[x,y] = t[x,y].transform;
    Vector2 pagePos = getPagePos(x, y);
    //Actual data generation happens here.
    int xLim = td[x,y].heightmapWidth;
    int yLim = td[x,y].heightmapHeight;

    float[,] array = new float[xLim, yLim];
    float min = int.MaxValue;
    float max = int.MinValue;
    for(int cx = 0; cx < xLim; cx++)
    {
        for(int cy = 0; cy < yLim; cy++)
        {
            array[cx,cy] = sample(
                new Vector3(
                    (float)cx * (tileSize / (float)(xLim - 1)) + pagePos.x, 
                    0.0f,
                    (float)cy * (tileSize / (float)(yLim - 1)) + pagePos.y
                ) 
            ); 

            if(min > array[cx,cy]) 
                min = array[cx,cy];
            if(max < array[cx,cy]) 
                max = array[cx,cy];

        }
    }

    //Set up the Terrain object to receive the data
    float diff = max != min ? max - min : 10.0f;
    tr[x,y].position = new Vector3(pagePos.y, min, pagePos.x);
    td[x,y].size = new Vector3(tileSize, diff, tileSize);

    //Convert the data to fit in the Terrain object
    for(int cx = 0; cx < xLim; cx++)
    {
        for(int cy = 0; cy < yLim; cy++)
        {
            array[cx,cy] -= min;
            array[cx,cy] /= diff;
        }
    }
    //Set the data in the Terrain object
    td[x,y].SetHeights(0, 0, array);
}
于 2012-07-14T23:37:34.177 に答える