如何使用VContainer实例化预制体并注册挂载的MonoBehaviour
问题描述
项目初期引入IoC容器,使用VContainer时遇到预制体实例化与组件注册的问题:
- 不清楚如何实例化GameObject预制体并注册其上的MonoBehaviour组件
- 尝试使用
RegisterComponentInNewPrefab方法但不符合需求 - 当前代码中
Container属性为空导致报错,代码如下:
public class SimulationLifetimeScope : LifetimeScope { [SerializeField] private GameObject prefab; protected override void Configure(IContainerBuilder builder) { InstallGameObject(builder); InstallPlayerControllers(builder); } private void InstallGameObject(IContainerBuilder builder) { var playerObject = Object.Instantiate(prefab); Container.InjectGameObject(playerObject); } private void InstallPlayerControllers(IContainerBuilder builder) { builder.Register<PlayerController>(Lifetime.Singleton); builder.Register<PlayerFacade>(Lifetime.Singleton); builder.Register<Collectable>(Lifetime.Singleton); } }
补充代码
PlayerController脚本:
public class PlayerController : MonoBehaviour { [SerializeField] private PlayerData playerData; [SerializeField] private Transform collectableParent; private Transform cameraTransform; private Rigidbody rb; private InputControl playerInputActions; private Vector2 leftInputVector= Vector2.zero; private float size; private void Start() { cameraTransform = Camera.main.transform; //Input playerInputActions = new InputControl(); playerInputActions.PlayerBallMovement.Enable(); size = playerData.StartingSize; } void Update() { leftInputVector = playerInputActions.PlayerBallMovement.LeftStickDirection.ReadValue<Vector2>(); } private void FixedUpdate() { Move(); } private void Move() { var dragForce = -rb.velocity * playerData.PlayerDrag; dragForce.y = 0f; var force = new Vector3(leftInputVector.x, 0, leftInputVector.y); //camera stuff //todo move this elsewhere and make nicer var cameraForward = cameraTransform.forward; var cameraRight = cameraTransform.right; cameraForward.y = 0f; cameraRight.y = 0f; cameraForward = cameraForward.normalized; cameraRight = cameraRight.normalized; var totalMovementVector = (force.z * cameraForward) + (force.x * cameraRight) + dragForce; rb.AddForce(totalMovementVector * playerData.PlayerSpeed, ForceMode.Acceleration); } private void OnCollisionEnter(Collision collision) { collision.gameObject.TryGetComponent<Collectable>(out var collectable); var closestHitPoint = collision.collider.ClosestPointOnBounds(gameObject.transform.position); if (collectable == null || !collectable.TryPickedUp(this, closestHitPoint)) return; size += collectable.Size; } }
PlayerFacade脚本(用于测试注入):
public class PlayerFacade : MonoBehaviour { [Inject] private PlayerController PlayerController; private void Start() { if (PlayerController == null) Debug.Log("player controller inject is null on start"); else Debug.Log("player controller inject is not null on start"); } private void Update() { if (PlayerController == null) Debug.Log("player controller null in update loop"); else Debug.Log("player controller not null in update loop"); } }
正确实现方式
核心问题分析
Configure方法执行时容器尚未完成初始化,此时Container属性为空,直接调用Container.InjectGameObject会触发空引用错误。必须通过容器构建器完成预制体注册与实例化的绑定,让VContainer自动处理注入流程。
修正后的代码
public class SimulationLifetimeScope : LifetimeScope { [SerializeField] private GameObject playerPrefab; protected override void Configure(IContainerBuilder builder) { // 注册预制体并自动实例化,同时处理实例上所有带[Inject]字段的注入 builder.RegisterComponentInNewPrefab(playerPrefab, Lifetime.Singleton) .UnderTransform(transform); // 可选:指定实例化后的父物体,整理场景层级 // 注册预制体中的MonoBehaviour组件,供其他脚本注入使用 builder.Register<PlayerController>(Lifetime.Singleton).FromComponentInNewPrefab(playerPrefab); builder.Register<PlayerFacade>(Lifetime.Singleton).FromComponentInNewPrefab(playerPrefab); // 如果Collectable是动态生成的可收集物,不要注册为单例,改为Transient生命周期 // builder.Register<Collectable>(Lifetime.Transient); } }
关键说明
RegisterComponentInNewPrefab的正确用法- 该方法会自动完成预制体实例化,并对实例上所有带有
[Inject]属性的字段执行注入 - 附加
UnderTransform可以指定实例化后的父物体,避免场景层级混乱
- 该方法会自动完成预制体实例化,并对实例上所有带有
组件注册的正确逻辑
- 使用
FromComponentInNewPrefab关联预制体中的组件,确保容器注入的是实例化后的真实组件实例 - 生命周期匹配场景:玩家对象是全局单例,用
Lifetime.Singleton;动态生成的Collectable需用Lifetime.Transient
- 使用
无需手动调用注入
- VContainer会在实例化预制体后自动处理所有注入操作,不需要手动调用
InjectGameObject
- VContainer会在实例化预制体后自动处理所有注入操作,不需要手动调用
额外优化建议
- 移除PlayerFacade中Update循环的日志输出,仅保留Start方法的注入检查即可
- 如果PlayerController需要依赖其他服务(比如InputControl),可以通过
[Inject]字段或构造函数注入,避免在Start中手动实例化
内容的提问来源于stack exchange,提问作者Tycho 9000
相关产品推荐
相关产品推荐

