【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の地面のテクスチャを設定してみます。

 

23 件のコメント

  • I’m no longer sure where you’re getting your information, however good topic.
    I needs to spend a while finding out more or understanding more.
    Thank you for excellent info I was on the lookout for this information for my mission.

  • Hello just wanted to give you a quick heads up. The text in your article seem to be
    running off the screen in Internet explorer. I’m not sure if this is a
    format issue or something to do with browser compatibility
    but I figured I’d post to let you know. The design and style look great though!
    Hope you get the problem solved soon. Kudos

  • Oh my goodness! Awesome article dude! Thank you so much,
    However I am having issues with your RSS. I don’t understand the reason why I cannot join it.
    Is there anybody else getting similar RSS issues? Anybody who knows the solution will you
    kindly respond? Thanx!!

  • I’ve been browsing online more than 3 hours these days, yet I
    by no means found any attention-grabbing article like yours.
    It is lovely value enough for me. In my view, if all site owners and bloggers made
    just right content material as you probably did, the web will be a lot more useful than ever before.

  • Today, I went to the beach front with my kids. I found a sea shell and gave it
    to my 4 year old daughter and said “You can hear the ocean if you put this to your ear.” She put the shell to her ear and screamed.
    There was a hermit crab inside and it pinched her ear.

    She never wants to go back! LoL I know this is entirely off
    topic but I had to tell someone!

  • Please let me know if you’re looking for a article writer for your site.
    You have some really great posts and I believe I would be a good asset.

    If you ever want to take some of the load off, I’d absolutely love to write some content for your blog in exchange for a link back to mine.

    Please send me an email if interested. Kudos!

  • Great post. I was checking constantly this blog and I am impressed!

    Very helpful info specially the last part 🙂 I care for such info a lot.
    I was looking for this particular info for a very long
    time. Thank you and best of luck.

  • コメントを残す

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