【Unity C# スクリプトでTerrain生成 2 】Terrain の高さを設定

Unity C# スクリプトでTerrain生成 という題目で、シリーズで説明します。

今回は、Terrainの高さを設定してみます。通常、Terrainで地形を作るときは、Terrainのインスペクターのツールを使うと思います。

ここでは、ツールを使わずにスクリプトのみで高さを設定します

スクリプトだけでアイキャッチ画像のような山を作ります。

前回の続きから行います。前回の記事は以下を参考にしてください。

【Unity C# スクリプトでTerrain生成 1 】Terrainのサイズ設定

2020.05.27
開発環境
  • Windows10
  • Unity 2019.3.7f1
  • Microsoft Visual Studio Community2019
  • .NET Framework 4.8

この記事は、スクリプトマニュアルを参考にしています。

ヘイトマップで高さを設定する

高さを設定するためには、terrain オブジェクトのヘイトマップ( heightmap ) を使用します。

簡単なコードでヘイトマップの説明をします。このコードは、(2,1) の高さを10にするものです。

public class TerrainScript : MonoBehaviour
{
    // Start is called before the first frame update
    void Start()
    {
        // 1-1. オブジェクトを取得
        Terrain terrain = GameObject.Find("Terrain").GetComponent<Terrain>();

        // 1-2. サイズを設定する
        terrain.terrainData.size = new Vector3(400, 100, 300);

        // 2-1. ヘイトマップの解像度を設定する
        terrain.terrainData.heightmapResolution = 257;

        // 2-2. ヘイトマップを取得する
        float[,] heightmap = terrain.terrainData.GetHeights(0, 0, 257, 257);

        // 2-3. (2,1) の高さを10にする
        int x1 = 2 * 256 / 400;
        int z1 = 1 * 256 / 300;
        heightmap[z1, x1] = 10F / 100;

        // 2-4. ヘイトマップを設定する
        terrain.terrainData.SetHeightsDelayLOD(0, 0, heightmap);

    }
}

2-1. ヘイトマップの解像度を設定する

terrain.terrainData.heightmapResolution でヘイトマップの解像度を設定します。

今回は257としたので、x軸、z軸に対して257 点の高さを設定できます

解像度は、32×2^x + 1 ( x=0・・・7)の値となるようにします。

解像度が大きくなればなるほど、地形を細かく設定できます。が、その分処理が遅くなります。

インスペクターで、以下の値を設定することと同等なことをやっています。

2-2. ヘイトマップを取得する

terrain.terrainData.GetHeights でヘイトマップを取得します。ヘイトマップは、float 型、サイズ [ 解像度×解像度 ] の配列です。

GetHeights のパラメータは以下の通りです。

xBase ハイトマップのサンプルを取得する最初の x インデックス
yBase ハイトマップのサンプルを取得する最初の y インデックス
width ハイトマップの x 軸に沿って取得するサンプルの数
height ハイトマップの y 軸に沿って取得するサンプルの数

引用元 : スクリプトリファレンス

今回は、フルサイズのヘイトマップを取得しています。この関数でヘイトマップを複製すると思われます。サイズを減らした方が負荷が減りそうです。

2-3. (2,1) の高さを10にする

int x1 = 2 * 256 / 400;

int z1 = 1 * 256 / 300;

Terrain は、x軸幅 400、z軸幅 300 としていますが、解像度は257 です。解像度に合わせる必要があります。

heightmap[z1, x1] = 10F / 100;

ヘイトマップの値は、0~1のfloat 値を設定します。Terrainの高さに対する割合値となります。

heightマップの1次インデックスはz軸の方向の値、2次要素はx軸の方向の値を設定するようです。

2-4. ヘイトマップを設定する

SetHeightsDelayLOD でヘイトマップを設定します。

xBase ハイトマップのサンプルを設定する最初の x インデックス
yBase ハイトマップのサンプルを設定する最初の y インデックス
heights 設定するハイトマップサンプルの配列 (値は 0 から 1 の範囲、配列のインデックスとして [x,y])

引用元 : スクリプトリファレンス

今回は、フルサイズを設定します。

このスクリプトを実行すると、以下のような地形を確認できます。

(2,1) の場所の高さが0となりました。

Terrain 全体の高さを0にする

スクリプトは以下の関数となります。

// Terrainの高さを0にする
// r:解像度
private void ClearHeightMap( Terrain t, int r)
{
    float[,] heightmap = t.terrainData.GetHeights(0, 0, r, r);
    for ( int i = 0; i < r; i++)
    {
        for (int j = 0; j < r; j++)
        {
            heightmap[j, i] = 0;
        }
    }
    t.terrainData.SetHeightsDelayLOD(0, 0, heightmap);
}

heightmapをフルサイズで取得し、すべての点の高さを0としています。

平らな山をつくる

最後に平らな山を作ってみます。

コードを以下のように修正します。

public class TerrainScript : MonoBehaviour
{
    // Start is called before the first frame update
    void Start()
    {
        // 1-1. オブジェクトを取得
        Terrain terrain = GameObject.Find("Terrain").GetComponent<Terrain>();

        // 1-2. サイズを設定する
        terrain.terrainData.size = new Vector3(400, 100, 300);

        // 2-1. ヘイトマップの解像度を設定する
        terrain.terrainData.heightmapResolution = 257;

        // ★★★ 追加 2-1.5. Terrainの高さを0にする
        ClearHeightMap(terrain, 257);

        // 2-2. ヘイトマップを取得する
        float[,] heightmap = terrain.terrainData.GetHeights(0, 0, 257, 257);

        // 2-3. (2,1) の高さを10にする
        int x1 = 2 * 256 / 400;
        int z1 = 1 * 256 / 300;
        heightmap[z1, x1] = 10F / 100;

        // 2-4. ヘイトマップを設定する
        terrain.terrainData.SetHeightsDelayLOD(0, 0, heightmap);

        // ★★★ 追加 2-5. (20,10) を開始点として、山を設定する
        float[,] heightmap2 = new float[20, 10]
        {
            {0.2F, 0.2F, 0.2F, 0.2F, 0.2F, 0.2F, 0.2F, 0.2F, 0.2F, 0.2F},
            {0.2F, 0.2F, 0.2F, 0.2F, 0.2F, 0.2F, 0.2F, 0.2F, 0.2F, 0.2F},
            {0.2F, 0.2F, 0.2F, 0.2F, 0.2F, 0.2F, 0.2F, 0.2F, 0.2F, 0.2F},
            {0.2F, 0.2F, 0.2F, 0.2F, 0.2F, 0.2F, 0.2F, 0.2F, 0.2F, 0.2F},
            {0.2F, 0.2F, 0.2F, 0.2F, 0.2F, 0.2F, 0.2F, 0.2F, 0.2F, 0.2F},
            {0.2F, 0.2F, 0.2F, 0.2F, 0.2F, 0.2F, 0.2F, 0.2F, 0.2F, 0.2F},
            {0.2F, 0.2F, 0.2F, 0.2F, 0.2F, 0.2F, 0.2F, 0.2F, 0.2F, 0.2F},
            {0.2F, 0.2F, 0.2F, 0.2F, 0.2F, 0.2F, 0.2F, 0.2F, 0.2F, 0.2F},
            {0.2F, 0.2F, 0.2F, 0.2F, 0.2F, 0.2F, 0.2F, 0.2F, 0.2F, 0.2F},
            {0.2F, 0.2F, 0.2F, 0.2F, 0.2F, 0.2F, 0.2F, 0.2F, 0.2F, 0.2F},
            {0.2F, 0.2F, 0.2F, 0.2F, 0.2F, 0.2F, 0.2F, 0.2F, 0.2F, 0.2F},
            {0.2F, 0.2F, 0.2F, 0.2F, 0.2F, 0.2F, 0.2F, 0.2F, 0.2F, 0.2F},
            {0.2F, 0.2F, 0.2F, 0.2F, 0.2F, 0.2F, 0.2F, 0.2F, 0.2F, 0.2F},
            {0.2F, 0.2F, 0.2F, 0.2F, 0.2F, 0.2F, 0.2F, 0.2F, 0.2F, 0.2F},
            {0.2F, 0.2F, 0.2F, 0.2F, 0.2F, 0.2F, 0.2F, 0.2F, 0.2F, 0.2F},
            {0.2F, 0.2F, 0.2F, 0.2F, 0.2F, 0.2F, 0.2F, 0.2F, 0.2F, 0.2F},
            {0.2F, 0.2F, 0.2F, 0.2F, 0.2F, 0.2F, 0.2F, 0.2F, 0.2F, 0.2F},
            {0.2F, 0.2F, 0.2F, 0.2F, 0.2F, 0.2F, 0.2F, 0.2F, 0.2F, 0.2F},
            {0.2F, 0.2F, 0.2F, 0.2F, 0.2F, 0.2F, 0.2F, 0.2F, 0.2F, 0.2F},
            {0.2F, 0.2F, 0.2F, 0.2F, 0.2F, 0.2F, 0.2F, 0.2F, 0.2F, 0.2F},
        };

        terrain.terrainData.SetHeightsDelayLOD(20 * 256 / 400, 10 * 256 / 300, heightmap2);
    }

    // Update is called once per frame
    void Update()
    {
        
    }

    // Terrainの高さを0にする
    // r:解像度
    private void ClearHeightMap( Terrain t, int r)
    {
        float[,] heightmap = t.terrainData.GetHeights(0, 0, r, r);
        for ( int i = 0; i < r; i++)
        {
            for (int j = 0; j < r; j++)
            {
                heightmap[j, i] = 0;
            }
        }
        t.terrainData.SetHeightsDelayLOD(0, 0, heightmap);
    }
}

 

2-1.5. Terrainの高さを0にする

前章で説明した関数を使います。

2-5. (20,10) を開始点として、山を設定する

heightmap は20×10 の配列としてを定義します。z軸方向 20、x軸方向 10 の解像度分の幅に対し、高さを0.2 としています。

つまり、

x軸の幅 10×400 / 256 = 15
y軸の幅 0.2 × 100 = 20
z軸の幅 20×300 / 256 = 35

の山が出来上がります。

terrain.terrainData.SetHeightsDelayLOD で開始位置とヘイトマップを設定します。

実行すると以下のような山が出来上がります。

まとめ

いかがだったでしょうか。

今回は、スクリプトでTerrain の高さを設定する方法を説明しました。

次回は、スクリプトでTerrainの地面のテクスチャを設定してみます。

 

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です