[Unity初心者Tips]確実に！必要なComponentを入れるRequire ComponentとReset()

Unityでゲームを作っていると、GameObjectにComponentを沢山、ぶらさげていくことになります。この時に、コンポーネント同士でお互いの機能を使うために参照するなど、「互いに必須のコンポーネント」が要るようになります。そうした場合の方法を考えてみましょう。

# コンポーネントを必須化する`Require Component`

UnityEngineで用意された、`Require Component`を使うと、同じGameObjectでアタッチされたコンポーネントをチェックして、それがないとビルドでエラーになるようにできます。

例えば、以下のコードがあったとします。

```c#:TestClassA.cs
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`が失敗することがあります。それを防ぐために、以下の形にします。

```c#:TestClassA.cs
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`が自動的にアタッチされます。
[![https://gyazo.com/93f9c5862fb6502eef72b89f6ed2bb8f](https://i.gyazo.com/93f9c5862fb6502eef72b89f6ed2bb8f.gif)](https://gyazo.com/93f9c5862fb6502eef72b89f6ed2bb8f)

[![https://gyazo.com/4fa4e60f51ecc5c9e3f6fca7328c74f2](https://i.gyazo.com/4fa4e60f51ecc5c9e3f6fca7328c74f2.png)](https://gyazo.com/4fa4e60f51ecc5c9e3f6fca7328c74f2)
また、指定されアタッチされたコンポーネントは外せません。この例の場合では、`TestClassA`を外さないままで`TestClassB`を外そうとすると、エラーになります。
書式は以下になります。
>`[RequireComponent(typeof(追加したいコンポーネント名))]`

#初期化をしてくれる`Reset()`
上記でコンポーネントの追加を自動化できましたが、Start()でGetComponentするまで、参照は入りません。初期化で負荷がかかるので、同様のGameObjectが多い場合には、起動前に代入しておきたいものです。その時にEditor上で実行できるメソッドがReset()です。

前記コードを以下に変更します。

```c#:TestClassA.cs
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()`が実行されます。

[![https://gyazo.com/e3eaebcc90a4b96eb852a367e8a77a8d](https://i.gyazo.com/e3eaebcc90a4b96eb852a367e8a77a8d.gif)](https://gyazo.com/e3eaebcc90a4b96eb852a367e8a77a8d)

[![https://gyazo.com/246ea789115a9bcdde172ee1a3063f3b](https://i.gyazo.com/246ea789115a9bcdde172ee1a3063f3b.png)](https://gyazo.com/246ea789115a9bcdde172ee1a3063f3b)

同じように`TestClassA`だけ追加していますが、今度は`TestClassB`、`TestClassC`が自動的にアタッチされると同時に、参照の代入も実行されます。

# 使い方のあれこれ

- `Require Component`だけの場合、必須にしたいものは`Rigidbody`等でも使えます。特定の初期化する部分を`Reset()`に書くことも可能です。
- 最初のサンプルから入れてあるので触れていませんが、`[SerializeField]`は`Reset()`で設定したい場合は必須です。`RequireComponent`は`[SerializeField]`が無くとも有効です。
- SceneでStaticなどで保証されるものなら、Resetで`find`等を用いて検索することも可能です。

```c#:TestClassA.cs
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同士のアクセスが楽になります。そうした場合に、上記方法を使うと便利ですね。

## 参考

- https://docs.unity3d.com/jp/540/ScriptReference/RequireComponent.html
- https://docs.unity3d.com/jp/560/ScriptReference/MonoBehaviour.Reset.html