やりたいこと
int型のフィールドnum1
,num2
を監視し、値が更新されたときにstring型のフィールドtext
を更新したい。
一般的なやり方
前フレームの値を保持するフィールドを用意し、Update()
で判定・更新を行う
public class SampleComponent : MonoBehaviour
{
// 監視対象
public int num1;
public int num2;
// 変更対象
public string text;
// 前フレームの値
private int prevNum1;
private int prevNum1;
// 初期化
void Start() {
text = $"{num1}-{num2}";
prevNum1 = num1;
prevNum2 = num2;
}
void Update() {
if(num1 != prevNum1 || num2 != prevNum2) {
text = $"{num1}-{num2}";
prevNum1 = num1;
prevNum2 = num2;
}
}
}
解説
Update()
はフレーム毎に実行されるメソッドです。
prevNum1
, prevNum2
はそれぞれnum1
, num2
の前フレームでの値を保持し、それらの値を比較することで値の変更を検知しています。
問題点
- 監視するフィールド1つごとにフィールドが1つ増える
- 監視・更新しないといけないフィールドが多くなると
Update()
が肥大化する
コルーチンを使用した記述方法
public class SampleComponent : MonoBehaviour
{
// 監視対象
public int num1;
public int num2;
// 変更対象
public string text;
void OnEnable() {
StartCoroutine(CoUpdateText());
}
IEnumerator CoUpdateText() {
while(true) {
text = $"{num1}-{num2}";
var state = (num1, num2); // 監視するフィールドの値を1タプルに詰め込む
yield return new WaitWhile(() => state == (num1, num2));
}
}
}
解説
監視・更新処理を全てコルーチン内で完結させるようにしました。
ローカル変数state
は最終更新時点でのフィールドの値を保持しています。
WaitWhile
は指定した処理をフレーム毎に実行し、false
を返すまでコルーチンの進行を待機するためのオブジェクトで、ここではstate
と現在の値のタプルを比較し、値が変更されるまでコルーチンの進行を待機しています。
WaitWhile
を通過した後は、無限ループなので上の更新処理に戻り、再度待機が始まります。
問題点
-
WaitWhile
のnewとクロージャのため、フィールドが更新されるたびにヒープアロケーションが発生する
まとめ
肥大化したUpdate()
を読み直すのは結構だるい