0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Unity公式が公開しているプログラミングパターンを読み取ってみた!~その6. Factory~

Last updated at Posted at 2023-02-20

はじめに

Unity公式がプログラミングパターンのデモを説明書と共に全12種公開していたので、1つずつ読み取っていきたいと思います!
英語の説明書でしたので間違った解釈をしているかもしれませんが、ご了承下さい!
最初の5種は、SOLID原則に沿ったデモとなっており、コードのみが公開されています。
残り7種は、Scene付きのデモが入っていますので、ZIPファイルをダウンロードして試してみてください。
今回は、6つ目のFactoryについてです。

環境

OS : Windows10
Unity バージョン : 2021.3.8f1

Factoryパターンとは

Factoryパターンとは、オブジェクトの生成生成に関する詳細を分けてしまうことです。
このパターンを用いる理由は、多くのオブジェクトが生成されながらも実際に必要になるまで実行時に分からないことがよくあるためです。

デモ

実際のデモを見ると理解しやすいので見てみましょう!
game-programming-patterns-demo-main-Factory-Windows_-Mac_-Linux-Unity-2021.3.gif
デモは、マウスでフィールド内をクリックし、クリックした場所から赤 or 黃のボールがランダムで生成されるようになっています。
また、各ボールは生成する際に以下の機能が付属しています。今回は、これらの機能が生成に関する詳細になります。

  • 赤ボール → パーティクルが生成される
  • 黃ボール → 音が鳴る(↑添付している動画に音は入っていません)

このゲームは以下のように作られています。
Hierarchy内にClickToCreateというオブジェクトが存在し、その子にFactoryA, FactoryBというオブジェクトが配置されています。
image.png
ClickToCreateというオブジェクトには、CrickToCreate.csがアタッチされています。
このクラスには、マウスのクリックを検知し、赤 or 黄のボールを生成する処理が書かれています。

public class ClickToCreate : MonoBehaviour
{
    [SerializeField] private LayerMask layerToClick;
    [SerializeField] private Vector3 offset;
    [SerializeField] Factory[] factories;

    private Factory factory;

    private void Update()
    {
        GetProductAtClick();
    }

    private void GetProductAtClick()
    {
        // マウスクリック
        if (Input.GetMouseButtonDown(0))
        {
            // FactoryA or Bランダムで選択
            factory = factories[Random.Range(0, factories.Length)];
            
            Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
            RaycastHit hitInfo;

            if (Physics.Raycast(ray, out hitInfo, Mathf.Infinity, layerToClick) && factory != null)
            {
                // 選択されたfactoryからボールを取得する処理
                factory.GetProduct(hitInfo.point + offset);
            }
        }
    }
}

各Factoryは、抽象クラスFactoryを継承しています。

public abstract class Factory : MonoBehaviour
{
    public abstract IProduct GetProduct(Vector3 position);
}

public class ConcreteFactoryA : Factory
{
    [SerializeField] private ProductA productPrefab;

    public override IProduct GetProduct(Vector3 position)
    {
        GameObject instance = Instantiate(productPrefab.gameObject, position, Quaternion.identity);
        ProductA newProduct = instance.GetComponent<ProductA>();

        newProduct.Initialize();

        return newProduct;
    }
}

public class ConcreteFactoryB : Factory
{
    [SerializeField] private ProductB productPrefab;

    public override IProduct GetProduct(Vector3 position)
    {
        GameObject instance = Instantiate(productPrefab.gameObject, position, Quaternion.identity);
        ProductB newProduct = instance.GetComponent<ProductB>();

        newProduct.Initialize();

        return newProduct;
    }
}

抽象クラスのFactoryには、IProductを返すGetProductメソッドが実装されています。
この部分が、オブジェクトの生成部分であり、実際の生成に関する詳細の部分とは切り離して書かれています。
また、GetProductメソッドは抽象メソッドであるため各製品クラスで具体的に処理が書かれています。
FactoryAでは、ProductAの製造を、FactoryBでは、ProductBの製造を行うようにしています。

public interface IProduct
{
    public string ProductName { get; set; }

    public void Initialize();
}

public class ProductA : MonoBehaviour, IProduct
{
    [SerializeField] private string productName = "ProductA";
    public string ProductName { get => productName; set => productName = value ; }

    private ParticleSystem particleSystem;

    public void Initialize()
    {
        gameObject.name = productName;
        particleSystem = GetComponentInChildren<ParticleSystem>();
        particleSystem?.Stop();
        particleSystem?.Play();
    }
}

public class ProductB : MonoBehaviour, IProduct
{
    [SerializeField] private string productName = "ProductB";
    public string ProductName { get => productName; set => productName = value; }

    private AudioSource audioSource;

    public void Initialize()
    {
        audioSource = GetComponent<AudioSource>();
        audioSource?.Stop();
        audioSource?.Play();
    }
}

各Productクラスには、音が出たりパーティクルが表示されたりと生成に関する詳細が書かれています。

長所と短所

Factoryを使用するに当たり長所と短所が以下に示したものになります。
個人的には短所はそれほど問題ないと感じています。

長所

  • 新しいProductを作ることになっても既存のProductのコードを変更する必要がない
  • 各Productごとにロジックを持つことでコードを短くできる
  • 各FactoryではProductの詳細に関与することなく、各ProductのInitializeだけ

短所

  • 多くのクラスとサブクラスを作成する必要がある

まとめ

Factoryパターンとは、オブジェクトの生成生成に関する詳細を分けてしまうことです。
このパターンを利用すると、既存のコードを変更することなく、コードを短くすることができます。
また、Factory内ではオブジェクトのInitializeのみを行っているので、オブジェクトの詳細に関与しなくても良くなります。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?