1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

VContainer で動的に GameObject を生成する

Last updated at Posted at 2024-11-07

はじめに

VContainer の入門記事は結構あるのですが、動的に GameObject を生成する方法がイマイチよく分からなかったので、サンプルを作成しました。

ソースコード

以下に記載しているプログラムは GitHub にも公開しています。

ファクトリメソッドを使用する方法を記載していましたが、もっと簡単に書ける方法がありましたので、修正しました。

具体的には IObjectResolver をインジェクトすることで、ファクトリメソッドを介さずに、直接依存注入する形式です。

GameLifetimeScope

using UnityEngine;
using VContainer;
using VContainer.Unity;

public class GameLifetimeScope : LifetimeScope
{
    [SerializeField]
    EnemySpawner EnemySpawner;

    protected override void Configure(IContainerBuilder builder)
    {
        // Enemy に依存注入する Timer クラス
        builder.RegisterComponentOnNewGameObject<Timer>(Lifetime.Singleton);
        // 実際に敵を生成するクラス
        builder.RegisterComponentInNewPrefab(EnemySpawner, Lifetime.Singleton);
        builder.RegisterComponentInHierarchy<EnemySpawnTrigger>();

        // ヒエラルキーに配置済みの Enemy クラスに依存注入
        foreach (var enemy in FindObjectsByType<Enemy>(FindObjectsSortMode.None)) {
            builder.RegisterBuildCallback(resolver => {
                resolver.Inject(enemy);
            });
        }
    }
}

EnemySpawner

using UnityEngine;
using VContainer;

public class EnemySpawner : MonoBehaviour
{
    [Inject]
    IObjectResolver Resolver;

    [SerializeField]
    Enemy EnemyPrefab;

    public Enemy Generate(string ID)
    {
        Debug.Log($"Generate ID: {ID}");

        // 今回は Instantiate を呼び出しているが、
        // ObjectPool などで、インスタンスを生成して返すことも可能。
        // 例) var enemy = ObjectPool.Get(ID).GetCompnent<Enemy>();
        var enemy = Instantiate(EnemyPrefab);

        // 動的生成されたインスタンスに対して依存注入
        Resolver.Inject(enemy);

        return enemy;
    }
}

Enemy

using UnityEngine;
using VContainer;

public class Enemy : MonoBehaviour
{
    [Inject]
    Timer timer;

    [SerializeField]
    float Duration = 10f;

    float lifetime;

    void Update()
    {
        if ((lifetime += Time.deltaTime) >= Duration) {
            Destroy(gameObject);
        }

        if (timer.Timeout) {
            Destroy(gameObject);
        }
    }
}

EnemySpawnTrigger

using System;
using UnityEngine;
using UnityEngine.UI;
using VContainer;
using Random = UnityEngine.Random;

public class EnemySpawnTrigger : MonoBehaviour
{
    [SerializeField]
    Button EnemySpawnButton;

    [Inject]
    EnemySpawner EnemySpawner;

    [Inject]
    Timer timer;

    void Start()
        => EnemySpawnButton.onClick.AddListener(OnClicked);

    void Update()
    {
        if (timer.Timeout) {
            EnemySpawnButton.onClick.RemoveListener(OnClicked);
        }
    }

    void OnClicked()
    {
        var ID = Guid.NewGuid().ToString();
        var enemy = EnemySpawner.Generate(ID);

        enemy.transform.position = new(Random.Range(0f, 320f), Random.Range(0f, 180f), 0f);
    }
}

Timer

using UnityEngine;

public class Timer : MonoBehaviour
{
    float TimeoutSeconds = 10f;

    public bool Timeout => !enabled;

    void Update()
    {
        if ((TimeoutSeconds -= Time.deltaTime) <= 0) {
            Debug.Log("Timeout");
            enabled = false;
        }
    }
}
古いコード ### GameLifetimeScope
using UnityEngine;
using VContainer;
using VContainer.Unity;

public class GameLifetimeScope : LifetimeScope
{
    [SerializeField]
    EnemyGenerator EnemyGenerator;

    protected override void Configure(IContainerBuilder builder)
    {
        builder.RegisterComponentOnNewGameObject<Timer>(Lifetime.Singleton);
        builder.RegisterComponentInNewPrefab(EnemyGenerator, Lifetime.Singleton);
        builder.RegisterComponentInHierarchy<EnemySpawner>();
        builder.RegisterFactory<string, Enemy>(container => (ID) => {
            return container.Resolve<EnemyGenerator>().Generate(container, ID);
        }, Lifetime.Scoped);
    }
}

Timer

using UnityEngine;

public class Timer : MonoBehaviour
{
    float TimeoutSeconds = 10f;

    public bool Timeout => !enabled;

    void Update()
    {
        if ((TimeoutSeconds -= Time.deltaTime) <= 0) {
            Debug.Log("Timeout");
            enabled = false;
        }
    }
}

Enemy

using UnityEngine;
using VContainer;

public class Enemy : MonoBehaviour
{
    [Inject]
    Timer timer;

    [SerializeField]
    float Duration = 10f;

    float lifetime;

    void Update()
    {
        if ((lifetime += Time.deltaTime) >= Duration) {
            Destroy(gameObject);
        }

        if (timer.Timeout) {
            Destroy(gameObject);
        }
    }
}

EnemyGenerator

using UnityEngine;
using VContainer;

public class EnemyGenerator : MonoBehaviour
{
    [SerializeField]
    GameObject EnemyPrefab;

    public Enemy Generate(IObjectResolver resolver, string ID)
    {
        Debug.Log($"Generate ID: {ID}");

        // 今回は Instantiate を呼び出しているが、
        // ObjectPool などで、インスタンスを生成して返すことも可能。
        // 例) var enemy = ObjectPool.Get(ID).GetCompnent<Enemy>();
        var enemy = Instantiate(EnemyPrefab).GetComponent<Enemy>();

        // 動的生成されたインスタンスに対して DI 注入
        resolver.Inject(enemy);

        return enemy;
    }
}

EnemySpawnTrigger

using System;
using UnityEngine;
using UnityEngine.UI;
using VContainer;
using Random = UnityEngine.Random;

public class EnemySpawnTrigger : MonoBehaviour
{
    [SerializeField]
    Button EnemySpawnButton;

    /// <summary>
    /// RegisterFactory で登録されたファクトリメソッドを依存注入する
    /// </summary>
    [Inject]
    Func<string, Enemy> GenerateEnemy;

    [Inject]
    Timer timer;

    void Start()
        => EnemySpawnButton.onClick.AddListener(OnClicked);

    void Update()
    {
        if (timer.Timeout) {
            EnemySpawnButton.onClick.RemoveListener(OnClicked);
        }
    }

    void OnClicked()
    {
        var ID = Guid.NewGuid().ToString();
        var enemy = GenerateEnemy(ID);

        enemy.transform.position = new(Random.Range(0f, 320f), Random.Range(0f, 180f), 0f);
    }
}
1
1
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
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?