游戏人生
About Me
  • 你好
  • Math
    • Number
      • Float IEEE754对确定性的影响
      • Pairing Function及其用途
    • Vector and Matrix
      • TRS基础概念
      • LossyScale深入分析
    • Quatenion
      • FromToRotation实现细节
    • Lerp and Curve
      • Slerp球形插值
      • Bezier Curve为什么重要
      • Interpolation和Extrapolation实现细节
  • Programming
    • C#
      • 学习资料
      • C# struct灵魂拷问
      • CIL的世界:call和callvirt
      • .NET装箱拆箱机制
      • .NET垃圾回收机制
    • Go
      • 基础特性
      • 如何正确的判空interface
      • 如何用interface模拟多态
      • 如何定制json序列化
      • 如何安全在循环中删除元素
      • 如何安全关闭channel
      • 如何集成c++库(cgo+swig)
      • 如何性能测试(benchmark, pprof)
    • Lua
      • 基础特性
  • General Game Development
    • Game Engine
      • 学习资料
      • 关于游戏引擎的认知
    • Networking
      • 帧同步
      • 状态同步
      • 物理同步
    • Physics
      • PhysX基本概念
      • PhysX增加Scale支持
      • PhysX场景查询
      • PhysX碰撞检测
      • PhysX刚体动力学
      • PhysX角色控制器
      • PhysX接入项目工程
      • 物理同步
      • 物理破坏
    • Design Pattern
      • 常用设计模式
      • MVP 架构模式
      • ECS 架构模式
  • Unity
    • Runtime
      • Unity拥抱CoreCLR
      • 浅析Mono内存管理
    • UGUI
      • 浅析UGUI渲染机制
      • 浅析UGUI文本优化
      • 介绍若干UGUI实用技巧
    • Resource Management
      • 浅析Unity堆内存的分类和管理方式
      • 深入Unity资源
      • 深入Unity序列化
      • 深入Assetbundle机制
    • Async
      • 深入Unity协程
      • 介绍若干Unity协程实用技巧
      • 异步动作队列
    • Hot Reload
      • Unity+Xlua
      • Xlua Examples学习(一)
      • Xlua Examples学习(二)
    • Editor Extension
    • Performance
      • 浅析Unity Profiler
      • 介绍一个Overdraw分析工具
  • Platform
    • WebGL
  • Real-world Project
    • Souce Engine
    • DOOM3 BFG
Powered by GitBook
On this page
  1. Unity
  2. Async

异步动作队列

本篇介绍一个小组件(只有两个类ActionSequence和ActionSequenceManager),用于在Unity脚本中方便的串行执行命令,命令支持包括匿名函数、范型委托、Unity协程和Wait对象、自定义迭代器等类型等。

举个简单的调用例子:

public class MyClass
{
    private bool _isPlayingAni;

    void Func()
    {
        ActionSequenceManager.Create()
            .Then(()=> _isPlayingAni = true;)
            .Then(new WaitForSeconds(2f))
            .Then(()=> _isPlayingAni = false;)
            .Run();

        //不会阻塞后面的代码
    }
}

对比一下原来的写法:

public class MyClass : Monobehaviour
{
    private bool _isPlayingAni;

    void Func()
    {
        StartCoroutine(MyCoro());

        //不会阻塞后面的代码
    }

    IEnumerator MyCoro()
    {
        _isPlayingAni = true;
        yield return new WaitForSeconds(2f);
        _isPlayingAni = false;
    }
}

可以看到,用类似Promise的写法,使代码变得更紧凑,并且普通c#类也可以调用Unity的各类WaitForXXX事件,而不需要继承Monobehaviour。 在实际使用中,好处远不止于此。

首先,通过将命令传入队列然后串行执行的方式,是一种解耦,解耦了命令和命令之间的衔接逻辑。当需求发生改动的时候,原来的写法要把整个函数重写并且Debug,而新的写法只要修改传入的命令即可,节省了笔者大量时间。这种把命令当作数据或配置的方法,可以在代码不重新编译的情况下,动态执行不同的命令。这种灵活性正是前面介绍过的“命令模式”带来的。这种思想在项目多个模块中均有应用。

其次,功能扩展方便。Then函数通过重载,可以接收丰富的参数类型。而且除了Run,Stop,还有可选的Callback接口等。经过一段时间的迭代,基本能够覆盖开发需求。Actionsequence的“并行”、嵌套都能够支持。

下面再介绍下ActionSequenceManager编写中的要点。这个类负责管理每一个Actionsequence的生命周期,因此是单例。为了节省开销(这部分存在开销是因为要从Mono调用Native的一些函数),对每个Actionsequence对象通过对象池来取用(将 reuse 选项打开)。要注意,一旦获取到这个Actionsequence,就要设立标志位,而不能等到执行Run方法后才设置。因为你不知道是否在同一帧内有多少个获取请求,绝不能让两个请求获取到同一个Actionsequence。

目前使用中要注意的一点是:当一个游戏对象调用了这个组件并运行命令中,因为别的原因被直接销毁了(Native层被销毁,c#这里不能马上感知),如果正在运行的命令需要读写该游戏对象的Native数据,会触发异常。为了避免这种情况,使用者可以选择在OnDestroy()中将自己持有的、正在运行的Actionsequence手动调用其Stop()方法。

Previous介绍若干Unity协程实用技巧NextHot Reload

Last updated 5 years ago

本篇介绍的该组件源码地址,会根据实际使用进行更新迭代。

见这里