【Unity】シューティングゲームのリファクタリング(拡張性を向上)

2023年3月14日

プロジェクトウィンドウのフォルダの構成の例

構成イメージ

実際のプロジェクトウィンドウ

プレファブ

シーン構成

スクリプト

GameDirectorゲームオブジェクト

GameDirector

using UnityEngine;

public class GameDirector : MonoBehaviour
{
    [SerializeField]
    Transform arrowsParent;

    PlayerGenerator playerGenerator;
    ArrowGenerator arrowGenerator;
    GaugeController gaugeController;

    void Awake()
    {
        playerGenerator = GetComponent<PlayerGenerator>();
        arrowGenerator = GetComponent<ArrowGenerator>();
        gaugeController = GetComponent<GaugeController>();

        CreateInstance();
    }

    private void CreateInstance()
    {
        // プレイヤーのインスタンス作成
        GameObject player = playerGenerator.Generate();
        // 矢のインスタンスを繰り返し作成
        arrowGenerator.Generate(arrowsParent, player, gaugeController, 1);
    }
}

このコードは、UnityのゲームオブジェクトにアタッチされたGameDirectorスクリプトを表しています。
このスクリプトは、次の変数を宣言しています。
• arrowsParent :Transform型の変数で、矢を生成する親オブジェクトのTransformコンポーネントを参照します。
• playerGenerator :PlayerGenerator型の変数で、プレイヤーを生成するために使用されます。
• arrowGenerator :ArrowGenerator型の変数で、矢を生成するために使用されます。
• gaugeController :GaugeController型の変数で、ゲージを制御するために使用されます。
そして、Awake()メソッド内で、それぞれの変数を初期化し、CreateInstance()メソッドを呼び出して、プレイヤーと矢を生成します。
CreateInstance()メソッドは、プレイヤーのインスタンスを作成し、矢のインスタンスを繰り返し作成するために、 arrowGenerator.Generate() メソッドを呼び出します。 arrowGenerator.Generate() メソッドには、矢の親オブジェクトのTransformコンポーネント、プレイヤーのGameObject、ゲージコントローラー、矢の生成数が渡されます。
以上のように、このスクリプトは、プレイヤーと矢を生成し、それらを制御するためのゲージコントローラーを初期化します。

PlayerGenerator

using UnityEngine;

public class PlayerGenerator : MonoBehaviour
{
    public GameObject player;

    public GameObject Generate()
    {
        return Instantiate(player);
    }
}

PlayerGenerator クラスには、 player というpublic変数があります。これは、生成するプレイヤーオブジェクトのプレハブ(Prefab)を格納するためのものです。

また、Generate()というメソッドがあります。このメソッドは、 player に設定されたプレハブを元に、新しいプレイヤーオブジェクトを生成して返します。具体的には、 Instantiate() メソッドを呼び出して player プレハブのコピーを作成しています。

このコードは、 PlayerGenerator クラスを使ってプレイヤーオブジェクトを生成するためのものです。他のスクリプトで PlayerGenerator クラスを使用する場合は、 Generate() メソッドを呼び出して新しいプレイヤーオブジェクトを生成できます。

ArrowGenerator

using UnityEngine;

public class ArrowGenerator : MonoBehaviour
{
    public GameObject arrowPrefab;
    GameObject player;
    Transform parent;
    GaugeController gaugeController;

    /// <summary>
    /// 落下させる矢を生成します
    /// </summary>
    /// <param name="parent">矢の生成先の親</param>
    /// <param name="player">プレイヤーのゲームオブジェクト</param>
    /// <param name="gaugeController">ライフの残りを表示するゲージ</param>
    /// <param name="repeatRate">生成間隔</param>
    public void Generate(Transform parent, GameObject player, GaugeController gaugeController, int repeatRate)
    {
        this.parent = parent;
        this.player = player;
        this.gaugeController = gaugeController;

        // GenerateArrowメソッドをrepeatRat秒間隔で繰り返します
        InvokeRepeating(nameof(GenerateArrow), 0, repeatRate);
    }

    void GenerateArrow()
    {
        GameObject arrowObject = Instantiate(arrowPrefab, new Vector3(Random.Range(-6, 7), 7, 0), Quaternion.identity, parent);

        ArrowController arrowController = arrowObject.GetComponent<ArrowController>();

        arrowController.player = player;

        arrowController.OnArrowCollision.AddListener(gaugeController.ViewHp);
    }
}

アタッチされたゲームオブジェクトは、矢を生成するためのメソッド Generate() を公開します。

Generate() メソッドは、親の Transform、プレイヤーの GameObject、ライフを表示するためのゲージを表す GaugeController、そして矢を生成する間隔を表す repeatRate の 4 つの引数を受け取ります。

メソッドは、これらの引数をメンバー変数として保持し、 InvokeRepeating() を使用して GenerateArrow() メソッドを繰り返し呼び出します。

GenerateArrow() メソッドは、ランダムな x 座標を持つ位置に arrowPrefab オブジェクトのインスタンスを生成し、矢を制御する ArrowController を取得し、プレイヤーの GameObject への参照を設定します。また、矢の衝突時に GaugeController の ViewHp メソッドを呼び出すために、 OnArrowCollision イベントに GaugeController の ViewHp メソッドを追加します。

Instantiateメソッドの第4引数は、ヒエラルキーでの親ゲームオブジェクトを登録しています

Quaternion.identityは、回転を表すQuaternionオブジェクトのデフォルト値を表します。具体的には、x、y、z、wのすべての値が0のQuaternionオブジェクトで、回転がない状態を表します。

Unityの3D空間では、オブジェクトの回転はQuaternionオブジェクトで表されます。Quaternion.identityは、回転が必要ない場合に便利で、以下のような場合に使用できます。

  • オブジェクトを作成するときに、回転を0に設定する必要がある場合。
  • オブジェクトを回転する必要がない場合、例えばスケール変換を適用するときに回転を避けたい場合。

Quaternion.identityを使用すると、回転がない状態でオブジェクトを操作でき、コードがより簡潔になります。

Playerゲームオブジェクト

PlayerController

using UnityEngine;
using UnityEngine.UI;

public class PlayerController : MonoBehaviour
{
    // 左ボタン
    Button leftButton;
    // 右ボタン
    Button rightButton;

    // プレイヤーの横の移動速度
    public float playerMoveSpeed = 3.0f;

    void Start()
    {
        leftButton = GameObject.Find("LButton").GetComponent<Button>();
        leftButton.onClick.AddListener(MoveLeft);

        rightButton = GameObject.Find("RButton").GetComponent<Button>();
        rightButton.onClick.AddListener(MoveRight);
    }

    void Update()
    {
        // 左矢印が押された時
        if (Input.GetKey(KeyCode.LeftArrow))
        {
            MoveLeft();
        }

        // 右矢印が押された時
        if (Input.GetKey(KeyCode.RightArrow))
        {
            MoveRight();
        }

        // 左右の移動制限の計算
        float x = transform.position.x;
        float y = transform.position.y;

        transform.position = new Vector2(Mathf.Clamp(x, -8, 8), y);
    }

    public void MoveLeft()
    {
        transform.Translate(-playerMoveSpeed * Time.deltaTime, 0, 0);
    }

    public void MoveRight()
    {
        transform.Translate(playerMoveSpeed * Time.deltaTime, 0, 0);
    }

}

このコードは、Unityのゲームオブジェクトを制御するためのスクリプトです。具体的には、左右に移動するプレイヤーキャラクターを制御します。以下に、コードの各部分を説明します。
Button はUnityのUI機能で、ボタンを表します。leftButton と rightButton は、それぞれ左右の移動を制御するためのボタンです。
public float playerMoveSpeed = 3.0f; は、プレイヤーの横移動速度を示す変数です。初期値は 3.0f に設定されています。
void Start() は、スクリプトが開始されたときに実行されるメソッドです。GameObject.Find と GetComponent を使用して、ボタンオブジェクトを検索し、左右のボタンに対して onClick.AddListener を呼び出して、左右の移動を制御するメソッドを登録しています。
void Update() は、フレームごとに実行されるメソッドです。このメソッドでは、左右のキー入力があった場合に、それぞれ MoveLeft メソッドと MoveRight メソッドを呼び出して、プレイヤーを移動させています。また、プレイヤーの移動制限を Mathf.Clamp を使用して計算して、画面の端に到達した場合には、それ以上移動しないようにしています。
public void MoveLeft() と public void MoveRight() は、それぞれプレイヤーを左右に移動させるメソッドです。transform.Translate を使用して、プレイヤーの座標を移動させています。Time.deltaTime を乗算することで、フレームレートの変化による移動速度の変化を吸収します。

PlayerStatus

using UnityEngine;

// プレイヤーの状態管理
public class PlayerStatus : MonoBehaviour
{
    public int hp = 100;
}

プレイヤーの状態を管理するスクリプトです
public int hp = 100; は、プレイヤーの体力を表す hp 変数を宣言して、初期値を100に設定しています。この変数は、他のスクリプトからアクセスできます

UIオブジェクト

GaugeController

using UnityEngine;
using UnityEngine.UI;   // UIを使うので忘れずに追加

public class GaugeController : MonoBehaviour
{
    Image hpGaugeImage;

    void Start()
    {
        hpGaugeImage = GameObject.Find("hpGauge").GetComponent<Image>();
    }

    // ゲージの円を更新する
    public void ViewHp(PlayerStatus playerStatus)
    {
        hpGaugeImage.fillAmount = playerStatus.hp / 100.0f;
    }
}

ImageクラスのインスタンスであるhpGaugeImageを宣言しています。
Start()メソッドで、hpGaugeImageに、GameObject.Find()メソッドを使用して、hpGaugeという名前のオブジェクトのImageコンポーネントを取得しています。
ViewHp(PlayerStatus playerStatus)メソッドは、引数としてPlayerStatusという型の変数を受け取ります。この目ドッドは、プレイヤーのステータスに応じて、hpGaugeImageの円のゲージの量を更新します。具体的には、fillAmountプロパティを使用して、playerStatusのhpの値を0から1の範囲に変換しています。 hpの値が100の場合、fillAmountは1になり、円のゲージが完全に塗りつぶされます。
このスクリプトは、ゲームオブジェクトにアタッチされることが想定されており、Start()メソッドでhpGaugeオブジェクトのImageコンポーネントを取得します。その後、プレイヤーのステータスに基づいて、円のゲージを更新するためにViewHp(PlayerStatus playerStatus)メソッドが呼び出されます。

クラス図

おまけ

ArrowGenerator

using UnityEngine;

public class ArrowGenerator : MonoBehaviour
{
    public ArrowController arrowPrefab;
    GameObject player;
    Transform parent;
    GaugeController gaugeController;

    /// <summary>
    /// 落下させる矢を生成します
    /// </summary>
    /// <param name="parent">矢の生成先の親</param>
    /// <param name="player">プレイヤーのゲームオブジェクト</param>
    /// <param name="gaugeController">ライフの残りを表示するゲージ</param>
    /// <param name="repeatRate">生成間隔</param>
    public void Generate(Transform parent, GameObject player, GaugeController gaugeController, int repeatRate)
    {
        this.parent = parent;
        this.player = player;
        this.gaugeController = gaugeController;

        // GenerateArrowメソッドをrepeatRat秒間隔で繰り返します
        InvokeRepeating(nameof(GenerateArrow), 0, repeatRate);
    }

    void GenerateArrow()
    {
        ArrowController arrowController = Instantiate(arrowPrefab, new Vector3(Random.Range(-6, 7), 7, 0), Quaternion.identity, parent);

        arrowController.player = player;

        arrowController.OnArrowCollision.AddListener(gaugeController.ViewHp);
    }
}

「arrowPrefab」というpublic変数で、矢のプレハブを指定することができます。
「player」と「parent」というGameObjectとTransformの変数で、矢の生成先の親とプレイヤーのゲームオブジェクトを指定することができます。
「gaugeController」というGaugeControllerの変数で、ライフの残りを表示するゲージを指定することができます。
「Generate」メソッドを使用して、矢を生成する処理を実行します。このメソッドは、「parent」「player」「gaugeController」変数と、「repeatRate」整数値を引数として受け取ります。このメソッドは、「InvokeRepeating」メソッドを使用して、「GenerateArrow」メソッドをrepeatRate秒間隔で繰り返し呼び出します。
「GenerateArrow」メソッドは、矢のプレハブから「ArrowController」のインスタンスを生成し、「Random.Range」メソッドを使用してランダムなx座標を決定して、指定された親オブジェクトの下に配置します。次に、この矢の「player」変数にプレイヤーのゲームオブジェクトを割り当て、「OnArrowCollision」イベントに「gaugeController.ViewHp」メソッドを追加します。これにより、矢がプレイヤーに当たった時にライフの残りを更新することができます。

C#,Unity,デバッグ

Posted by hidepon