趣味的なIT・ネットの話題

Unityでワンソースなシステム開発:ローカル変数の内容がリセットされる現象・基本の基本でミスってました

Unityを始めました。独特の流儀が多いのですが、普段組み込みばかりやっていると新鮮ですね。オブジェクトのキューが組み込み型にあるという時点で感動しています。

さてプログラムを作成していて、あるメソッドで値を代入したクラスのローカル変数が、他のメソッドから参照できない、というか見れるけれども初期値になっていて代入の結果が反映されていないという問題が発生しました。何じゃそりゃと悩んでいたのですが、原因は極基本的な所にありました。

バグの解析の最中にその変数をStaticにすると他のメソッドからも参照できたのですが、「ローカル変数なんだからstaticにしようがしまいが関係無いはずでしょ」とうんうん悩んでました。ただこれも分かってみると納得。

作業としては、Unityでユーティリティアプリを作ろうとしていて、画面の描画されるオブジェクトとは関係無く一定のロジックを回したいので、HierarchyにEmpty Objectを作り、そこにC#スクリプト(Main.cs)をアタッチしてロジックを作っていました。このスクリプト(クラス)のあるメソッドでローカル変数を設定したら、Main.csの同じクラス内のローカル変数から見られないという状態になっていたのです。

原因は、GUIに設定したボタンへのスクリプトのアタッチでした。UIのボタンが押されたときに簡単に処理できるように、そのボタンにもMain.csをアタッチしていました。こうするとボタンのクリックイベントからMain.csのメソッドを呼べるので簡単だろうという判断からです。が、これが間違いの始まりでした。オブジェクトにアタッチされたクラスは、それぞれのアタッチ先のオブジェクト毎にインスタンス化されるので、Empty Objectとボタンにそれぞれアタッチされたスクリプトで、別のインスタンスが生成されていることが分かりました。まだGameObjectの仕組みをあまり理解できていないのですが、概ねそんなところだと理解しています。

試しに新しいプロジェクトで以下のスクリプトをEmptyとボタン2個にそれぞれアタッチしたところ、それぞれのインスタンス他それぞれStart()を実行し、自分のアタッチ先のGameObject名を出力しました。staticな変数が2個目のインスタンスでは初期化されないことも確認でき、2個目のインスタンスのstart()ではその前のインスタンスで設定された内容が表示されます。

インスタンスが3つあるのでUpdateも別々に回っていて、それぞれがDebug.logを実行しています。publicにしたプロパティはインスペクター上でそれぞれ違う値を設定でき、その値が動作に反映されました。

using UnityEngine;
using System.Collections;

public class Main : MonoBehaviour {

	string name_holder = "";
	static string name_holder_static = "";

	// Use this for initialization
	void Start () {
		Debug.LogFormat ("before assignment: My name static is{0}", name_holder_static);
		Debug.LogFormat ("before assignment: My name is{0}", name_holder);
		name_holder = gameObject.name.ToString ();
		name_holder_static = name_holder;
		Debug.LogFormat ("after assignment: My name static is{0}", name_holder_static);
		Debug.LogFormat ("after assignment: My name is{0}", name_holder);
	}

	int count =  0;

	public int max = 0;

	// Update is called once per frame
	void Update () {
		count++;
		if (count > max) {
			Debug.LogFormat ("Update: My name is{0}", name_holder);
			count = 0;
		}
	}
}

当初やりたかった「ボタンを押してEmptyのメソッド実行」はボタンに別のスクリプトを実行させ、その中でEmptyのオブジェクトを取得させないといけないのですね。

===================

さっそく追記

スクリプトのメソッドの実行のためのスクリプトを別に書くというのはどうにも面倒だなと思っていたら、インスペクタのOn Clickに、「実行したいスクリプトがアタッチされているオブジェクト」が選択できることが分かりました。選択するとアタッチしているスクリプトのpublicメソッドが選択できるようになってます。

 


Facebooktwitterpinterestlinkedinmail
納得したらすぐにシェア!