はじめに
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;
}
}
}
古いコード
### GameLifetimeScopeusing 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);
}
}