Unity - スクリプトでTerrainに草を生やす
前回Terrainに自動で木を植えたので下草も生やしたいなと思って調べたが、やっぱり情報があんまりない感じだったので書く。
環境
- Unity: 2020.3.26f1 Personal
- Universal RP: 10.8.1
できあがりイメージ
考え方
木の時とだいたい同じ。
こちらはTerrainの座標に対応する配列にフラグ値を詰めていく形。
- 生成する草のPrefabをDetailPrototypeにセットする
- Terrain上の座標に対応する二次元配列(
int[,]
)に、草を生やすかどうかのフラグ値を詰めていく0
… 生やさない1
以上 … 生やす
- 詰め終わった二次元配列をTerrainDataにセットすると草が生成される
サンプルスクリプト
以下のスクリプトをTerrainオブジェクトにくっつけ、Inspectorで草のTexture2Dを設定すればPlayした時に自動的に草を生成する。
TerrainGrassGenerator .cs
using System.Linq; using UnityEngine; public class TerrainGrassGenerator : MonoBehaviour { public Texture2D _grassTexture; [Range(0, 100)] public int _density = 50; void Start() { Terrain terrain = GetComponent<Terrain>(); GenerateTerrainGrass(terrain); } private void GenerateTerrainGrass(Terrain terrain) { var terrainData = terrain.terrainData; // prototypeTextureはnullでもエラーにならなくて分かりづらいので例外投げとく if (_grassTexture == null) { throw new MissingReferenceException("コンポーネントに草が付いてないよ!"); } // すべての草の元ネタ terrainData.detailPrototypes = new DetailPrototype[] { new DetailPrototype() { prototypeTexture = _grassTexture, renderMode = DetailRenderMode.GrassBillboard } }; // デフォルトは1024のはず var detailResolution = terrainData.detailResolution; var noise = new CreationNoiseGener(detailResolution, _density); // 草の生成状態を数値で表す四角形配列 / 1以上の値が入っている座標に草が生える var detailMap = new int[detailResolution, detailResolution]; var detailPoints = Enumerable.Range(0, detailResolution).ToList(); detailPoints.AsParallel().ForAll(x => { detailPoints.Where(z => noise.ShouldCreate(x, z)) .ToList().ForEach(z => { detailMap[x, z] = 1; }); }); terrainData.SetDetailLayer(0, 0, 0, detailMap); } /// <summary> /// 各座標にオブジェクトを生成するかランダム判定する用ユーティリティ /// </summary> private class CreationNoiseGener { private int[] _randAtPoint; private int _threshold; private int _resolution; public CreationNoiseGener(int resolution, int density) { _threshold = 100 - density; _resolution = resolution; var ints = GenerateRandomInts(resolution * resolution); _randAtPoint = ints.Select(e => e * 100 / byte.MaxValue).ToArray(); } public bool ShouldCreate(int x, int z) { return _threshold < _randAtPoint[x * _resolution + z]; } private int[] GenerateRandomInts(int size) { var bytes = new byte[size]; new System.Random().NextBytes(bytes); var ints = new int[size]; bytes.CopyTo(ints, 0); return ints; } } }
参考リンク
Placing grass on terrain in script on a certain height - Unity Answers
Removing grass from a terrain in Unity | Independent Software