下記画像のようにTerrain経由で生成した木や草オブジェクトをリアルタイムで差し替える方法の紹介です。
紅葉など季節の移り変わりなどに利用できると思います。
今回は「夏の風景から秋の風景に変更する処理」を例に説明します。
夏(差し替え前) | 秋(差し替え後) |
---|---|
木オブジェクトの差し替え
夏用と秋用の木を用意
夏用の木と秋用の紅葉した木のプレハブを準備し、Terrainのインスペクタ > 樹木ペイントタブに移動し、準備した2種類の木を追加します。
木を差し替えるスクリプトの作成
最下部にある関数ChangeTreeColor
に差し替え処理を記載しています。
/// <summary>
/// 季節毎の風景を演出する機能を実装する
/// </summary>
public class SeasonVisualizar : MonoBehaviour
{
/// <summary>
/// 変更対象のTerrain
/// </summary>
[SerializeField] private Terrain terrain;
/// <summary>
/// シーン上に配置してある木のリスト
/// </summary>
private TreeInstance[] currentTreeList ;
/// <summary>
/// 季節用の定数( 0 = 春, 1 = 秋)
/// </summary>
private const int SPRING = 0, FALL = 1;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
// マウスクリックで木を紅葉したものに差し替える
if (Input.GetMouseButtonDown(0)) {
ChangeTreeColor(season: FALL);
}
}
/// <summary>
/// 木のモデルを入れ替え、風景を変える
/// </summary>
/// <param name="season">変更予定の季節番号</param>
public void ChangeTreeColor(int season) {
currentTreeList = new TreeInstance[terrain.terrainData.treeInstances.Length];
System.Array.Copy(terrain.terrainData.treeInstances, currentTreeList,
terrain.terrainData.treeInstances.Length);
for (int i = 0; i < terrain.terrainData.treeInstanceCount; i++) {
currentTreeList[i].prototypeIndex = season;
}
terrain.terrainData.treeInstances = currentTreeList;
}
}
関数ChangeTreeColor
について
-
terrain.terrainData.treeInstances
はTerrain上に配置されたそれぞれの木の情報がTreeInstance
の配列として格納されています。 -
TreeInstance
はprototypeIndexには下記画像のように、Terrainに設定済みの木オブジェクトの内、参照している木のインデックスが格納されています。 -
treeIntances
のプロパティを直接変更しても変化しなかったため、現在のtreeIntancesのコピーを作成→コピーの値を変更→変更済みコピー配列でオリジナル配列を上書きという形をとっています。
草オブジェクトの差し替え
夏用と秋用の草を準備
草を差し替えるコードの作成
先ほどのスクリプトに草を差し替えるコードを追記します。
/// <summary>
/// 季節毎の風景を演出する機能を実装する
/// </summary>
public class SeasonVisualizar : MonoBehaviour
{
/// <summary>
/// 変更対象のTerrain
/// </summary>
[SerializeField] private Terrain terrain;
/// <summary>
/// シーン上に配置してある木のリスト
/// </summary>
private TreeInstance[] currentTreeList ;
/* 追記 */
/// <summary>
/// シーン上に配置してある草のリスト
/// </summary>
private DetailPrototype[] currentGrass;
/// <summary>
/// 季節ごとの草オブジェクト
/// </summary>
[SerializeField] private GameObject[] grassVariant;
/// <summary>
/// 季節用の定数( 0 = 春, 1 = 秋)
/// </summary>
private const int SPRING = 0, FALL = 1;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
if (Input.GetMouseButtonDown(0)) {
ChangeTreeColor(season: FALL);
/* 追記 */
ChangeGrassColor(season: FALL);
}
}
/// <summary>
/// 木のモデルを入れ替え、風景を変える
/// </summary>
/// <param name="season">変更予定の季節番号</param>
public void ChangeTreeColor(int season) {
currentTreeList = new TreeInstance[terrain.terrainData.treeInstances.Length];
System.Array.Copy(terrain.terrainData.treeInstances, currentTreeList, terrain.terrainData.treeInstances.Length);
for (int i = 0; i < terrain.terrainData.treeInstanceCount; i++) {
currentTreeList[i].prototypeIndex = season;
}
terrain.terrainData.treeInstances = currentTreeList;
}
/// <summary>
/// 草オブジェクトを入れ替え、風景を変える
/// </summary>
/// <param name="season">変更予定の季節番号</param>
public void ChangeGrassColor(int season) {
currentGrass = new DetailPrototype[terrain.terrainData.detailPrototypes.Length];
System.Array.Copy(terrain.terrainData.detailPrototypes, currentGrass, terrain.terrainData.detailPrototypes.Length);
for (int i = 0; i < terrain.terrainData.detailPrototypes.Length; i++) {
currentGrass[i].prototype = grassVariant[season];
}
terrain.terrainData.detailPrototypes = currentGrass;
}
}
スクリプト作成後、先ほど準備した草プレハブをこのスクリプトのGrassVariant
に設定します。(要素0: 夏、要素1: 秋)
関数ChangeGrassColor
について
-
Terrain.terrainData.detailPrototypes
にTerrainに配置されたそれぞれの草の情報がDetailPrototype
の配列として格納されています。 - 草の場合は
DetailPrototype.prototype
へオブジェクトを代入することで草の差し替えが可能になっています。- 今回の場合は
DetailPrototype.prototype
へ事前に用意した秋用の草プレハブを代入することでモデルを入れ替えます。
- 今回の場合は
- 木と同様、一度detailPrototypesのコピーを作成し、最終的にコピーでオリジナルを上書きしています。
注意点
この方法で差し替えを行うと、Unity実行終了後も差し替え状態が保持されたままになります。
そのままでは気になる場合はもとに戻すような関数を追加するか、差し替え状態を戻すEditor拡張を準備するといいと思います。
下記スクリプトはSeasonVisualizarコンポーネントのインスペクタ上に"Reset"ボタンを追加し、ボタン押下で差し替えをもとに戻すEditor拡張スクリプトです。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;
#endif
#if UNITY_EDITOR
[CustomEditor(typeof(SeasonVisualizar))]
public class SeasonVisualizarEditor : Editor {
/// <summary>
/// 季節用の定数( 0 = 春, 1 = 秋)
/// </summary>
private const int SPRING = 0, FALL = 1;
public override void OnInspectorGUI() {
base.OnInspectorGUI();
SeasonVisualizar sv = target as SeasonVisualizar;
if (GUILayout.Button("Reset")) {
sv.ChangeTreeColor(season: SPRING);
sv.ChangeGrassColor(season: SPRING);
}
}
}
#endif