Unityでゲームを作っていると、GameObjectにComponentを沢山、ぶらさげていくことになります。この時に、コンポーネント同士でお互いの機能を使うために参照するなど、「互いに必須のコンポーネント」が要るようになります。そうした場合の方法を考えてみましょう。
コンポーネントを必須化するRequire Component
UnityEngineで用意された、Require Component
を使うと、同じGameObjectでアタッチされたコンポーネントをチェックして、それがないとビルドでエラーになるようにできます。
例えば、以下のコードがあったとします。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class TestClassA : MonoBehaviour {
[SerializeField]
TestClassB testClassB;
[SerializeField]
TestClassC testClassC;
private void Start()
{
testClassB = GetComponent<TestClassB>();
testClassC = GetComponent<TestClassC>();
}
}
TestClassA
というコンポーネントから、TestClassB
,TestClassC
を参照しているとします。ただし、GameObjectにアタッチされているかは判りませんから、場合によってはGetComponent
が失敗することがあります。それを防ぐために、以下の形にします。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//必要なコンポーネントを定義
[RequireComponent(typeof(TestClassB))]
[RequireComponent(typeof(TestClassC))]
public class TestClassA : MonoBehaviour {
[SerializeField]
TestClassB testClassB;
[SerializeField]
TestClassC testClassC;
private void Start()
{
testClassB = GetComponent<TestClassB>();
testClassC = GetComponent<TestClassC>();
}
}
TestClassA
をアタッチすると、Require Component
で指定されたコンポーネントTestClassB
、TestClassC
が自動的にアタッチされます。
また、指定されアタッチされたコンポーネントは外せません。この例の場合では、TestClassA
を外さないままでTestClassB
を外そうとすると、エラーになります。
書式は以下になります。
[RequireComponent(typeof(追加したいコンポーネント名))]
#初期化をしてくれるReset()
上記でコンポーネントの追加を自動化できましたが、Start()でGetComponentするまで、参照は入りません。初期化で負荷がかかるので、同様のGameObjectが多い場合には、起動前に代入しておきたいものです。その時にEditor上で実行できるメソッドがReset()です。
前記コードを以下に変更します。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//必要なコンポーネントを定義
[RequireComponent(typeof(TestClassB))]
[RequireComponent(typeof(TestClassC))]
public class TestClassA : MonoBehaviour {
[SerializeField]
TestClassB testClassB;
[SerializeField]
TestClassC testClassC;
private void Reset()
{
testClassB = GetComponent<TestClassB>();
testClassC = GetComponent<TestClassC>();
}
}
これを実行すると、コンポーネント追加やギアアイコンからResetしたときに、Reset()
が実行されます。
同じようにTestClassA
だけ追加していますが、今度はTestClassB
、TestClassC
が自動的にアタッチされると同時に、参照の代入も実行されます。
使い方のあれこれ
-
Require Component
だけの場合、必須にしたいものはRigidbody
等でも使えます。特定の初期化する部分をReset()
に書くことも可能です。 - 最初のサンプルから入れてあるので触れていませんが、
[SerializeField]
はReset()
で設定したい場合は必須です。RequireComponent
は[SerializeField]
が無くとも有効です。 - SceneでStaticなどで保証されるものなら、Resetで
find
等を用いて検索することも可能です。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[RequireComponent(typeof(TestClassB))]
[RequireComponent(typeof(TestClassC))]
[RequireComponent(typeof(Rigidbody))]
public class TestClassA : MonoBehaviour
{
[SerializeField]
TestClassB testClassB;
[SerializeField]
TestClassC testClassC;
[SerializeField]
Rigidbody rigidBody;
[SerializeField]
GameController gameController;
[SerializeField]
TestEnemy testEnemy;
private void Reset()
{
testClassB = GetComponent<TestClassB>();
testClassC = GetComponent<TestClassC>();
rigidBody = GetComponent<Rigidbody>();
gameController = FindObjectOfType<GameController>();
if (gameController == null) {
Debug.Log("ERROR!Gamecontroller not found");
}
}
}
必須になるものをGameController
にいろいろ記述して参照できるようにしておくと、Scene内での相互参照したいGameObject同士のアクセスが楽になります。そうした場合に、上記方法を使うと便利ですね。