You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用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);
    }
}

关键说明

  1. RegisterComponentInNewPrefab的正确用法

    • 该方法会自动完成预制体实例化,并对实例上所有带有[Inject]属性的字段执行注入
    • 附加UnderTransform可以指定实例化后的父物体,避免场景层级混乱
  2. 组件注册的正确逻辑

    • 使用FromComponentInNewPrefab关联预制体中的组件,确保容器注入的是实例化后的真实组件实例
    • 生命周期匹配场景:玩家对象是全局单例,用Lifetime.Singleton;动态生成的Collectable需用Lifetime.Transient
  3. 无需手动调用注入

    • VContainer会在实例化预制体后自动处理所有注入操作,不需要手动调用InjectGameObject

额外优化建议

  • 移除PlayerFacade中Update循环的日志输出,仅保留Start方法的注入检查即可
  • 如果PlayerController需要依赖其他服务(比如InputControl),可以通过[Inject]字段或构造函数注入,避免在Start中手动实例化

内容的提问来源于stack exchange,提问作者Tycho 9000

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.09 07:40:33