Unity商业插件(六)- 课外3- Zenject Tick方法和全局生命周期

经常会碰到这种Tick 方法

经常会碰到这样的方法,继承| 实现来自 ITickable 

        //Tick 方法
       public void Tick()
        {
            if (++_tick % _config.CheckInterval != 0) //实现:每几秒执行一次的循环
                return;

            var cam = _cameraModel.CurrentCamera;
            if (cam == null)
                return;

            GeometryUtility.CalculateFrustumPlanes(cam, _frustumPlanes);
            Vector3 camPos = cam.transform.position;

            var items = _registry.Items;

            //

Unity Zenject ITickable 完整触发原理与执行流程

一、基础概念

1. 核心接口

public interface ITickable
{
    void Tick();
}

配套还有:

  • IFixedTickable:对应 FixedUpdate
  • ILateTickable:对应 LateUpdate

作用:替代 MonoBehaviour 的 Update,实现非 Mono 逻辑解耦、依赖注入、统一生命周期管理

2. 核心管理类:TickManager

Zenject 所有 Tick 逻辑的总调度器,是整个流程的核心,属于场景容器 DiContainer 内置自动绑定单例,无需手动注册。 职责:

  1. 存储所有注册的 ITickable/IFixedTickable/ILateTickable 对象
  2. 在 Unity 原生 Update/FixedUpdate/LateUpdate 驱动下,批量调用所有 Tick 方法
  3. 支持执行优先级、动态增删 Tick 对象

二、底层挂载载体:ZenjectMonoBehaviour(场景自动生成)

  1. 加载 Zenject 场景时,容器会自动创建一个隐藏 GameObject,挂载 SceneKernel(继承 ZenjectMonoBehaviour)
  2. SceneKernel 内部持有 TickManager 引用,监听 Unity 原生生命周期
    void Update()
    {
        _tickManager.Tick();
    }
    void FixedUpdate()
    {
        _tickManager.FixedTick();
    }
    void LateUpdate()
    {
        _tickManager.LateTick();
    }
    

关键:ITickable 不是自己挂脚本,是由一个全局 Mono 统一驱动所有非 Mono 对象


三、完整执行全流程(分 4 大阶段)

阶段 1:对象注册(绑定阶段)

两种注册 ITickable 的方式,最终都会加入 TickManager 的列表:

方式 1:Bind 后自动注册(最常用)
// Installer中绑定
Container.BindInterfacesTo<PlayerLogic>().AsSingle();
  • BindInterfacesTo 会自动识别该类实现了 ITickable
  • 绑定完成后,自动调用 TickManager.AddTickable(实例, 优先级)
方式 2:手动添加(动态运行时新增)
// 运行时拿到TickManager手动注册
var tickMgr = Container.Resolve<TickManager>();
tickMgr.AddTickable(new EnemyLogic(), 100);
存储结构

TickManager 内部维护三个有序列表:

List<TickableInfo> _tickables;        // ITickable
List<TickableInfo> _fixedTickables;  // IFixedTickable
List<TickableInfo> _lateTickables;   // ILateTickable

TickableInfo = 实例对象 + 执行优先级 int,列表会按优先级从小到大排序,数值越小越先执行。

阶段 2:Unity 原生帧驱动(SceneKernel Mono 回调)

每帧 Unity 执行顺序:

  1. FixedUpdate → SceneKernel.FixedUpdate() → TickManager.FixedTick()
  2. Update → SceneKernel.Update() → TickManager.Tick()
  3. LateUpdate → SceneKernel.LateUpdate() → TickManager.LateTick()

阶段 3:TickManager 批量分发(核心触发逻辑)

以普通 ITickable.Tick() 举例,TickManager.Tick() 内部逻辑:

  1. 处理上一帧标记为移除的 Tick 对象(安全移除,防止遍历时删列表报错)
  2. 遍历排序后的 _tickables 列表
  3. 逐个调用 tickInfo.Tickable.Tick()
  4. 执行完成,等待下一帧

伪代码简化实现:

public void Tick()
{
    // 先清理待删除对象
    ProcessRemoveQueue();
    
    // 按优先级顺序遍历所有ITickable
    foreach (var info in _tickables)
    {
        if (info.IsActive)
        {
            info.Tickable.Tick();
        }
    }
}
优先级规则
  • 绑定时指定优先级:
    Container.BindInterfacesTo<GameLoop>().AsSingle().WithArguments(200);
    
  • 默认优先级:0
  • 数字越小,越早执行;负数优先,大数后置
  • 同优先级:注册顺序不确定

阶段 4:销毁 / 移除流程

  1. 对象销毁、容器销毁、手动移除都会触发注销
    _tickManager.RemoveTickable(tickObj);
    
  2. 不会立刻从列表删除,加入移除队列,当前帧遍历结束后统一清理,避免遍历中集合修改报错
  3. 场景卸载时,SceneKernel 销毁,TickManager 清空所有列表,停止所有 Tick

四、关键特性与底层细节

1. 和 Mono Update 本质区别

维度MonoBehaviour UpdateZenject ITickable
驱动载体每个脚本自身 Mono全局唯一 SceneKernel 统一驱动
对象类型必须挂载 GameObject纯 C# 非 Mono 类也可使用
依赖管理无原生注入,需手动 GetComponent完全由 DI 容器管理依赖
执行排序依赖脚本挂载顺序 / Execution Order自定义数值优先级,可控
批量性能大量 Mono 有 GC 开销单 Mono 遍历列表,更少 GC

2. 线程与执行时机

  • 完全在主线程执行,和 Update 同步,不能异步
  • ITickable = Update 时机;IFixedTickable = FixedUpdate(不受帧率影响,固定时间步);ILateTickable = LateUpdate(渲染前最后逻辑)

3. 激活 / 暂停控制

TickableInfo 包含 IsActive 标记:

  • 调用 tickManager.PauseTickable(obj) 会标记为不执行,不删除
  • 适合临时暂停逻辑(如游戏暂停),无需反复 Add/Remove

4. 生命周期绑定自动解绑

通过 BindInterfaces 绑定的对象,当对象被容器销毁时,Zenject 会自动调用 RemoveTickable,无需手动清理,防止空引用报错。

5. 多容器隔离

  • ProjectContext(全局容器)、SceneContext(场景容器)各自拥有独立 TickManager
  • 全局注册的 ITickable 跨场景常驻;场景内注册的切换场景自动销毁停止 Tick,互不干扰

五、完整时序链路总结

Unity引擎帧循环
    ↓
SceneKernel(Mono).Update()
    ↓
TickManager.Tick()
    ↓
1. 清理待删除Tick对象队列
2. 按优先级升序遍历全部ITickable实例
    ↓
循环调用每个实例的 Tick() 方法
    ↓
本帧Tick执行完毕,等待下一帧Update再次触发

FixedTick / LateTick 流程完全一致,仅触发入口为 FixedUpdate/LateUpdate,对应不同接口。

六、常见踩坑底层原因

  1. Tick 不执行
    • 类没有通过BindInterfaces绑定,只是 Bind <类名>(不会自动识别 ITickable)
    • 对象提前被销毁,已从 Tick 列表移除
    • 调用了 PauseTickable 暂停执行
  2. 遍历时报集合修改异常 Zenject 设计了延迟删除队列,只要不用反射强行直接操作内部列表,不会报错;禁止在 Tick 内部直接 Remove
  3. 执行顺序混乱 未指定优先级,默认 0,同优先级注册顺序不保证,复杂业务必须手动传入优先级数值排

课内,生命周期,解释链接

Unity高性能依赖注入框架Extenject(Zenject)-----进阶教程 - 灰信网(软件开发博客聚合)

完整生命周期如上,只是

ProjectContext

SceneContext

这和普通英文翻译和理解不同,ProjectContext 是在SceneContext 下触发的,因为Unity必然是以Scene 场景为基础单位

Fixed 修复循环引用问题recycle dependence(可能能修复)

https://www.youtube.com/watch?v=v22y-gNnk8I

Zneject +Editor 例子

ZenjectEditorWindowの紹介 | VirtualCast Blog

课外,实现 MenuBar (Group by 页签) Zenject框架是否OK?

研究中。。。

 创建

显示Label

点击事件

//参考代码,不能直接拷贝,依赖多个自定义类       
        var item = GameObject.Instantiate(m_Toggle3,m_ToggleRoot,true);
        item.transform.position += new Vector3(210, 0);
        
        
        var binder = item.GetComponent<UIDataBinder>();
        var col = new CommonDataCollection();
        col["label"] = "储物柜";
        binder.Args = col;

 CommndBar

Billboard

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

avi9111

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值