Unityでフラッピーバードゲームを作っていたら、
このようなエラーが出た。
Member 'PlayerController.isCollided' cannot be accessed with an instance reference; qualify it with a type name instead
PlayerController.isCollidedというメンバーには、
インスタンスの参照を通してアクセスできないよ、
型名(今回はクラス)を通してアクセスしてね
という意味。
PlayerControllerを確認してみると
public static bool isCollided = false;
isCollidedは静的フィールドだったことが分かった。
静的関数はインスタンスを通して
呼び出せないのは知っていた。
しかしそうか、
静的フィールドもだめなのか~と納得がいった…
…が。
PlayerControllerをインスタンス化した覚えがないことに気づく。
いつインスタンス化されたの?
newもInstantiateも書いた記憶がないのに、なぜ?
結論
newしたのを忘れていた。
…ではなく
ゲームを再生すると、UnityエンジンはGameObjectにアタッチしてあるスクリプトのクラスを自動的にインスタンス化するから。
詳細
関係のあるコードのみ以下に示す。
public const string tag_Player = "Player";
PlayerController playerController;
void Start()
{
playerController = GameObject.FindWithTag(Variables.tag_Player)
.GetComponent<PlayerController>();
}
データ型がPlayerControllerクラスのフィールド、playerControllerを定義する。
FindWithTagは静的関数であり、
GameObjectクラスを通して呼び出され、
当該のゲームオブジェクト(インスタンス)を返す。
ここではPlayerタグのついたPlayerという名前の
ゲームオブジェクトへの参照を渡している↓
そのゲームオブジェクトに対し
GetComponent<PlayerController>()
をすると、
そのゲームオブジェクトにアタッチされている
スクリプトのクラスのインスタンスへの参照を返す。
この時、すでにクラスはUnityエンジンによって
インスタンス化されており、
GetComponentはインスタンス化に関与していない。
だからコードを見ているだけでは
クラスのインスタンス化には気づけなかったのだ。
余談
Q. GetComponentでクラスが取得できるのはなぜ?
A. GameObjectにアタッチしてある時点で、
そのスクリプトのクラスはコンポーネントだから。
Q. クラスがコンポーネントってどういうこと?
A. クラスの階層構造はこんな感じで階層構造になっている。
かの有名なMonoBehaviourクラスは
Behaviourクラスに包含され、
Behaviourクラスは
Componentクラスに包含されている。
GameObjectへのアタッチはMonoBehaviourの継承が必須である。
だからアタッチされたスクリプトのクラスは
その時点でComponentクラスに含まれている。
よってGetComponentで取得ができる。
Q. ゲーム再生時に自動でインスタンス化というけど、静的クラスはどうなるの?
A. もちろんされない。
Q. ゲーム再生時にクラスは自動でインスタンス化される…あれ?クラスのnewってどんなときに使ってたんだっけ?
A. Vector3などUnity組み込みクラスなどに使う
===
あと静的クラスはライフサイクルメソッドも呼び出すことができない。
(書いてもエラーにはならない)
コメント歓迎です!
ご指摘をいただけるとうれしいです