【Unity】Reflectionを使って、クラス(コンポーネント)の変数を全てコピーする
UnityではインスペクターでPaste Component As Valueを使えばコンポーネントの値をコピーすることが出来ますが、大量にコンポーネントがあると一つ一つコピーするのは大変です。
また、この変数はコピーしたいけど、この変数はコピーしたくない、といった柔軟な対応も出来ません。
スクリプトから、あるコンポーネントの変数を、別のゲームオブジェクトのコンポーネントに丸ごとコピーする方法が分かったのでメモしておきます。
●コピー対象のクラス(enemy)
using UnityEngine;
public class Enemy : MonoBehaviour
{
public string subName;
public int level;
public float attack;
public float diffence;
public float speed;
[SerializeField]
private string privateString;
}
●enemyをコピーするためのクラス
コピーしたくない変数はfield.Nameで場合分けしています。
using UnityEngine;
using System.Reflection;
public class CopyEnemy : MonoBehaviour
{
public Enemy copySource;
public Enemy[] pasteTarget;
[ContextMenu("Copy All")]
public void CopyAll()
{
foreach (Enemy enemy in pasteTarget)
{
System.Type type = enemy.GetType();
FieldInfo[] fields = type.GetFields(BindingFlags.NonPublic | BindingFlags.Instance);
foreach (FieldInfo field in fields)
{
//コピーしたくない項目はここに書いておく
if (field.Name == "subName") continue;
field.SetValue(enemy, field.GetValue(copySource));
}
}
}
}
CopyAll()を実行することで、copySourceに設定したenemyAの変数を、pasteTargetに設定したenemyB、enemyCにコピーします。
[ContextMenu("Copy All")]を書いているので、インスペクターの右上にあるメニューからCopy Allを選べばCopyAll()を実行できます。
通常はprivateな変数は他のクラスからは取得できませんが、以下のように(BindingFlags.NonPublic | BindingFlags.Instance)をつけることで、privateな変数も取得できるようです。
FieldInfo[] fields = type.GetFields(BindingFlags.NonPublic | BindingFlags.Instance);
一体の敵オブジェクトに設定したステータスを、大量の敵オブジェクトにコピーするときなどにこの方法は使えると思います。
本来はプレハブを活用するのが正しい方法なんでしょうけど、場合によってはこの方法が便利なこともあるかと…
応用として、以下のようにsaveとloadの仕組みを作れば、プレハブを使わずにコンポーネントの変数を外部ファイルとして管理できます。
privateな変数も[SerializeField]をつけておけば保存可能です。
using UnityEngine;
using System.IO;
using System.Reflection;
public class CopyEnemy : MonoBehaviour
{
public Enemy copySource;
public Enemy[] pasteTarget;
[ContextMenu("Copy All")]
public void CopyAll()
{
foreach (Enemy enemy in pasteTarget)
{
System.Type type = enemy.GetType();
FieldInfo[] fields = type.GetFields(BindingFlags.NonPublic | BindingFlags.Instance);
foreach (FieldInfo field in fields)
{
//コピーしたくない項目はここに書いておく
if (field.Name == "subName") continue;
field.SetValue(enemy, field.GetValue(copySource));
}
}
}
[ContextMenu("save all")]
public void SaveAll()
{
foreach (Enemy enemy in pasteTarget)
{
string IDname = enemy.gameObject.name;
string json = JsonUtility.ToJson(enemy);
File.WriteAllText(IDname, json);
}
}
[ContextMenu("load all")]
public void LoadAll()
{
foreach (Enemy enemy in pasteTarget)
{
string IDname = enemy.gameObject.name;
string json = File.ReadAllText(IDname);
JsonUtility.FromJsonOverwrite(json, enemy);
}
}
}