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

Extenject架构:如何正确实现ProjectContext访问SceneContext服务?

Extenject(Zenject)跨上下文状态访问规范方案

针对ProjectContext中GameStateMachine的LoadGameState需要访问SceneContext内PlayerChooseService的场景,以下是符合Zenject架构规范的实现方案:

方案1:接口抽象+跨上下文实例注册(适合需要持续访问服务的场景)

  • 定义抽象接口IPlayerChoiceProvider,仅暴露LoadGameState所需的方法:
    public interface IPlayerChoiceProvider
    {
        List<string> GetSelectedResourcePaths();
        void ClearSelectedResources();
    }
    
  • 让PlayerChooseService实现该接口:
    public class PlayerChooseService : IPlayerChoiceProvider
    {
        private List<string> _selectedResources = new List<string>();
        
        public List<string> GetSelectedResourcePaths() => _selectedResources;
        public void ClearSelectedResources() => _selectedResources.Clear();
        
        // 其他业务方法保持私有或内部可见
    }
    
  • 在SceneContext的Installer中,将服务实例注册到ProjectContext的全局容器(确保场景卸载时清理):
    public class SceneInstaller : MonoInstaller
    {
        public override void InstallBindings()
        {
            var playerChoiceService = Container.Bind<PlayerChooseService>().AsSingle().NonLazy().Instance;
            // 将实例注册到ProjectContext的全局容器
            ProjectContext.Instance.Container.Bind<IPlayerChoiceProvider>()
                .FromInstance(playerChoiceService)
                .AsCached()
                .WhenInjectedInto<LoadGameState>();
            
            // 场景卸载时解绑,避免内存泄漏
            Container.Bind<SceneUninstallHandler>().AsSingle().NonLazy();
        }
    }
    
    public class SceneUninstallHandler : IInitializable, IDisposable
    {
        private readonly IContainer _projectContainer;
        private readonly IPlayerChoiceProvider _playerChoiceProvider;
        
        public SceneUninstallHandler(IContainer projectContainer, IPlayerChoiceProvider playerChoiceProvider)
        {
            _projectContainer = projectContainer;
            _playerChoiceProvider = playerChoiceProvider;
        }
        
        public void Initialize() {}
        
        public void Dispose()
        {
            _projectContainer.Unbind<IPlayerChoiceProvider>(_playerChoiceProvider);
        }
    }
    
  • 在LoadGameState中直接注入接口实例:
    public class LoadGameState : GameState
    {
        [Inject] private IPlayerChoiceProvider _playerChoiceProvider;
        
        public override void Enter()
        {
            var selectedPaths = _playerChoiceProvider.GetSelectedResourcePaths();
            // 执行资源加载逻辑
        }
    }
    

方案2:SignalBus数据传递(适合一次性数据传递场景)

  • 定义携带玩家选择数据的信号:
    public class PlayerChoiceConfirmedSignal : Signal
    {
        public List<string> SelectedResourcePaths;
    }
    
  • 在ProjectContext的Installer中绑定SignalBus:
    public class ProjectInstaller : MonoInstaller
    {
        public override void InstallBindings()
        {
            Container.Bind<SignalBus>().AsSingle();
            Container.DeclareSignal<PlayerChoiceConfirmedSignal>();
        }
    }
    
  • 在PlayerChooseService中发送信号(玩家选择完成时):
    public class PlayerChooseService
    {
        [Inject] private SignalBus _signalBus;
        private List<string> _selectedResources = new List<string>();
        
        public void OnPlayerConfirmedChoice()
        {
            _signalBus.Fire(new PlayerChoiceConfirmedSignal
            {
                SelectedResourcePaths = _selectedResources
            });
        }
    }
    
  • 在LoadGameState中订阅信号并缓存数据:
    public class LoadGameState : GameState, IDisposable
    {
        [Inject] private SignalBus _signalBus;
        private List<string> _cachedSelectedPaths;
        private SignalSubscription _subscription;
        
        public override void Enter()
        {
            _subscription = _signalBus.Subscribe<PlayerChoiceConfirmedSignal>(OnChoiceConfirmed);
            // 如果信号已发送,直接使用缓存数据执行加载
            if (_cachedSelectedPaths != null)
            {
                LoadSelectedResources(_cachedSelectedPaths);
            }
        }
        
        private void OnChoiceConfirmed(PlayerChoiceConfirmedSignal signal)
        {
            _cachedSelectedPaths = signal.SelectedResourcePaths;
            LoadSelectedResources(_cachedSelectedPaths);
        }
        
        private void LoadSelectedResources(List<string> paths)
        {
            // 资源加载逻辑
        }
        
        public void Dispose()
        {
            _subscription?.Dispose();
        }
    }
    

方案3:状态初始化注入(适合场景与状态强绑定的场景)

  • 在LoadGameState中添加初始化方法:
    public class LoadGameState : GameState
    {
        private IPlayerChoiceProvider _playerChoiceProvider;
        
        public void Initialize(IPlayerChoiceProvider choiceProvider)
        {
            _playerChoiceProvider = choiceProvider;
        }
        
        public override void Enter()
        {
            var selectedPaths = _playerChoiceProvider.GetSelectedResourcePaths();
            // 加载逻辑
        }
    }
    
  • 在SceneContext的Installer中获取状态实例并注入依赖:
    public class SceneInstaller : MonoInstaller
    {
        public override void InstallBindings()
        {
            var playerChoiceService = Container.Bind<PlayerChooseService>().AsSingle().NonLazy().Instance;
            // 从ProjectContext获取GameStateMachine及LoadGameState
            var gameStateMachine = ProjectContext.Instance.Container.Resolve<GameStateMachine>();
            var loadGameState = gameStateMachine.GetState<LoadGameState>();
            loadGameState.Initialize(playerChoiceService);
        }
    }
    

规范要点

  • 优先使用SignalBus方案,完全解耦上下文,避免跨上下文的实例依赖,符合Zenject的模块化设计原则
  • 若需持续访问服务,必须用接口抽象,禁止直接依赖SceneContext中的具体服务类型
  • 所有跨上下文的实例引用必须在场景卸载时清理,防止内存泄漏
  • 全局上下文(ProjectContext)不应持有场景上下文(SceneContext)的任何类型引用,保持全局逻辑的独立性

内容的提问来源于stack exchange,提问作者Vladyslav Yeromenko

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 07:16:17