10.Playable源码浅析

10.Playable源码浅析


10.1 为什么要学习 Playable 源码

看源码是为了吃透 Playable 的运行规则:图怎么被驱动、节点与输出怎么调度、逻辑落在哪一层
分层阅读时以 C# 侧为主,核心执行仍在 C++ 黑盒层(需要引擎源码),这里不展开 C++ 黑盒层。


10.2 Playable源码分层概述

  • C++ 黑盒层:评估、遍历、数据流、Job 等核心逻辑在引擎内部,C# 只能通过句柄触达。
  • Handle 层PlayableHandle / PlayableOutputHandle 承担真正控制;前者管 time/state/port/graph/IsValid,后者负责绑定源、权重与通知,并触发遍历与输出
  • 包装层Playable / PlayableOutput / ScriptPlayable<T> / PlayableBehaviour 是 C# 入口;节点由 API 创建并连到图上,输出层只是包装,真正控制在 PlayableOutputHandle
  • 数据层FrameData 是评估阶段生成的帧上下文(PlayEvaluate 都会产生),传入 PrepareFrame/ProcessFrameFrameRate 负责时间基准与帧率换算。
  • 驱动与图层PlayableDirector 持有并驱动 Graph;Graph 的创建入口是 PlayableGraph.Create,负责连接/评估/销毁,是调度核心。
  • 资源层PlayableAsset 描述“播什么”和“有哪些输出”;PlayableBinding / ScriptPlayableBinding 是输出绑定的描述与创建入口;TimelineAsset 是其中一种实现。
flowchart TD
    A[资源层
PlayableAsset / TimelineAsset
PlayableBinding / ScriptPlayableBinding] -->|CreatePlayable| B[驱动层
PlayableDirector] B -->|创建与持有| C[图层
PlayableGraph] C -->|生成节点包装| P[包装层
Playable / ScriptPlayable] C -->|生成输出包装| O[输出层
PlayableOutput / ScriptPlayableOutput] P -->|GetHandle 获取节点句柄| H[节点句柄层
PlayableHandle] O -->|GetHandle 获取输出句柄| OH[输出句柄层
PlayableOutputHandle] H -->|设置时间 状态 端口 图关系| D[引擎核心层
C++ 黑盒评估与遍历] OH -->|绑定源节点 设置权重 推送通知| D D -->|每帧生成上下文| E[数据层
FrameData] E -->|回调输入| F[行为层
PlayableBehaviour] F -->|写回状态与权重| H H -->|节点结果更新| D OH -->|驱动目标输出| G[具体目标
Animator AudioSource RenderTexture] G -->|反馈到引擎| D P -->|封装为节点入口| H O -->|封装为输出入口| OH

10.3 Playables类图与类型索引

完整类图

classDiagram
  direction LR

  %% 关系 管理与聚合
  PlayableGraph o-- Playable : 管理 包含 一对多
  PlayableGraph o-- PlayableOutput : 管理 包含 一对多
  Playable o-- PlayableHandle : 聚合 持有句柄
  PlayableOutput o-- PlayableOutputHandle : 聚合 持有句柄
  ScriptPlayable_T o-- PlayableHandle : 聚合 持有句柄
  ScriptPlayableOutput o-- PlayableOutputHandle : 聚合 持有句柄

  %% 关系 实现与继承
  IPlayable <|.. Playable : 实现
  IPlayable <|.. ScriptPlayable_T : 实现
  IPlayableOutput <|.. PlayableOutput : 实现
  IPlayableOutput <|.. ScriptPlayableOutput : 实现
  IPlayableBehaviour <|.. PlayableBehaviour : 实现
  IPlayableAsset <|.. PlayableAsset : 实现
  INotification <|.. Notification : 实现

  %% 关系 引用与依赖
  PlayableOutput --> Playable : SetSourcePlayable 绑定源节点
  PlayableOutputHandle --> PlayableHandle : GetSourcePlayable 取源句柄
  PlayableBinding --> PlayableOutput : CreateOutput 创建输出
  ScriptPlayableBinding ..> PlayableBinding : 依赖 创建绑定
  ScriptPlayable_T --> PlayableBehaviour : 包装 持有行为
  INotificationReceiver ..> INotification : 依赖 通知类型
  INotificationReceiver ..> Playable : 依赖 通知源
  PlayableOutputExtensions ..> IPlayableOutput : 扩展 输出
  PlayableExtensions ..> IPlayable : 扩展 节点

  %% 关系 组件驱动
  PlayableDirector --> PlayableAsset : 驱动 播放资产
  PlayableDirector --> PlayableGraph : 持有 播放图

  %% 关系 枚举与结构体使用
  PlayableHandle --> PlayState : 使用 播放状态
  FrameData --> PlayState : 使用 有效状态
  FrameData --> FrameData_EvaluationType : 使用 评估类型
  PlayableHandle --> DirectorWrapMode : 使用 时间包裹
  PlayableGraph --> DirectorUpdateMode : 使用 更新时间
  PlayableExtensions --> PlayableTraversalMode : 使用 遍历模式
  PlayableBinding --> DataStreamType : 使用 数据流类型

  %% 新增关系 FrameRate 与工具类依赖
  PlayableGraph ..> FrameRate : 依赖 匹配帧率
  PlayableDirector ..> FrameRate : 依赖 Play帧率
  AnimationPlayableUtilities ..> PlayableGraph : 依赖 创建图
  AnimationPlayableUtilities ..> Playable : 依赖 连接播放

  %% 类型与重要方法
  class PlayableGraph {
    结构体 播放图容器
    创建连接节点并驱动评估与销毁
    Create 创建图()
    Play 播放图()
    Stop 停止图()
    Evaluate 手动评估()
    Connect 连接节点()
  }

  class Playable {
    结构体 通用节点
    封装PlayableHandle并提供统一节点入口
    GetHandle 获取句柄()
    IsPlayableOfType 类型判断()
  }

  class PlayableHandle {
    结构体 底层句柄
    控制时间状态输入输出等核心数据
    IsValid 是否有效()
    GetGraph 所属图()
    Play 播放()
    Pause 暂停()
    SetTime 设置时间()
  }

  class ScriptPlayable_T {
    泛型结构体 脚本节点
    把PlayableBehaviour包装为可播放节点
    Create 创建脚本节点()
    GetBehaviour 获取行为实例()
  }

  class PlayableBehaviour {
    抽象类 行为回调基类
    提供节点生命周期与帧处理钩子
    OnPlayableCreate 创建回调()
    PrepareFrame 帧前准备()
    ProcessFrame 帧处理()
  }

  class IPlayableBehaviour {
    接口 行为回调接口
    规范图与节点的生命周期与帧回调
    OnGraphStart 图开始()
    OnGraphStop 图停止()
    OnBehaviourPlay 行为播放()
    OnBehaviourPause 行为暂停()
    PrepareFrame 帧前准备()
    ProcessFrame 帧处理()
  }

  class PlayableOutput {
    结构体 通用输出端
    封装PlayableOutputHandle并连接输出目标
    GetHandle 获取输出句柄()
  }

  class PlayableOutputHandle {
    结构体 输出句柄
    绑定源节点控制权重并派发通知
    SetSourcePlayable 设置源节点()
    GetWeight 获取权重()
    SetWeight 设置权重()
    PushNotification 推送通知()
  }

  class ScriptPlayableOutput {
    结构体 脚本输出端
    用于创建脚本自定义输出通道
    Create 创建脚本输出()
    GetHandle 获取输出句柄()
  }

  class PlayableBinding {
    结构体 输出绑定描述
    描述输出目标类型并提供创建输出入口
    streamName 输出名称
    outputTargetType 目标类型
    CreateOutput 创建输出()
  }

  class PlayableAsset {
    抽象类 可播放资产
    生成图节点并提供默认输出
    CreatePlayable 创建节点()
    duration 时长
    outputs 输出集合
  }

  class IPlayableAsset {
    接口 资产接口
    规范创建节点与输出描述的统一契约
    CreatePlayable 创建节点()
    duration 时长
    outputs 输出集合
  }

  class PlayableExtensions {
    静态类 节点扩展
    封装播放暂停时间连接等常用操作
    Play 播放()
    Pause 暂停()
    SetTime 设置时间()
    ConnectInput 连接输入()
  }

  class PlayableOutputExtensions {
    静态类 输出扩展
    提供源节点绑定权重设置与通知功能
    SetSourcePlayable 设置源节点()
    GetWeight 获取权重()
    SetWeight 设置权重()
    PushNotification 推送通知()
    GetSourceInputPort 旧接口()
    SetSourceInputPort 旧接口()
    SetSourceOutputPort 旧接口()
  }

  class Notification {
    类 默认通知
    以PropertyName作为通知标识的默认实现
    id 通知ID
  }

  class INotification {
    接口 通知接口
    用于统一通知标识并支持通知系统
    id 通知ID
  }

  class INotificationReceiver {
    接口 通知接收器
    接收输出端派发的通知事件
    OnNotify 收到通知()
  }

  class FrameData {
    结构体 帧上下文
    携带时间权重状态与输出等运行信息
    deltaTime 帧间隔
    weight 权重
    evaluationType 评估类型
    effectivePlayState 有效状态
  }

  class FrameRate {
    结构体 帧率
    描述图时间步进与帧率换算
    IsValid 是否有效()
    rate 帧率值
  }

  class PlayableDirector {
    类 驱动组件
    驱动PlayableAsset并管理PlayableGraph播放
    Play 播放()
    Pause 暂停()
    Stop 停止()
    Evaluate 评估()
  }

  class AnimationPlayableUtilities {
    静态类 动画工具
    提供快速创建动画图与输出的便捷方法
    Play 快速播放()
    PlayClip 播放剪辑()
    PlayMixer 播放混合()
  }

  class IPlayable {
    接口 节点接口
    统一暴露获取PlayableHandle的入口
    GetHandle 获取句柄()
  }

  class IPlayableOutput {
    接口 输出接口
    统一暴露获取PlayableOutputHandle的入口
    GetHandle 获取输出句柄()
  }

  %% 枚举 作用和含义
  class PlayState {
    枚举 播放状态
    描述节点运行阶段与有效状态
    Paused 暂停
    Playing 播放中
    Delayed 延迟已废弃
  }
  class DirectorWrapMode {
    枚举 时间包裹模式
    指定到末尾时如何处理时间
    Hold 停留末尾
    Loop 循环
    None 不包裹
  }
  class DirectorUpdateMode {
    枚举 更新时间模式
    指定图时间由何种时钟驱动
    DSPClock 音频时钟
    GameTime 游戏时间
    UnscaledGameTime 不受缩放
    Manual 手动
  }
  class PlayableTraversalMode {
    枚举 遍历模式
    决定节点输入计算的方式
    Mix 混合
    Passthrough 直通
  }
  class DataStreamType {
    枚举 数据流类型
    旧版流类型标识用于兼容
    Animation 动画
    Audio 音频
    Texture 纹理
    None 无
  }
  class FrameData_EvaluationType {
    枚举 评估类型
    区分手动评估与播放评估来源
    Evaluate 手动评估
    Playback 播放评估
  }

核心类型分成类图

classDiagram
  direction LR

  %% C++ 黑盒层(引擎内部)
  namespace 引擎黑盒层 {
    class NativePlayableSystem {
      C++实现 Playables内核
      评估/遍历/数据流/Job
    }
  }

  %% Handle 层(底层能力)
  namespace 句柄层 {
    class PlayableHandle {
      结构体 底层句柄
      IsValid 是否有效()
      GetGraph 所属图()
      SetTime 设置时间()
      Play/Pause 播放控制()
    }
    class PlayableOutputHandle {
      结构体 输出句柄
      SetSourcePlayable 绑定源节点()
      SetWeight 设置权重()
      PushNotification 推送通知()
    }
  }

  %% 包装层(对外 API)
  namespace 包装层 {
    class IPlayable {
      接口 节点入口
      GetHandle 获取句柄()
    }
    class IPlayableOutput {
      接口 输出入口
      GetHandle 获取句柄()
    }
    class Playable {
      结构体 节点包装
      GetHandle 获取句柄()
      SetTime 设置时间()
    }
    class PlayableOutput {
      结构体 输出包装
      SetSourcePlayable 绑定源节点()
      SetWeight 设置权重()
    }
    class ScriptPlayable_T {
      泛型结构体 脚本节点
      Create 创建节点()
      GetBehaviour 取行为()
    }
    class PlayableBehaviour {
      抽象类 行为基类
      PrepareFrame 帧前准备()
      ProcessFrame 帧处理()
    }
    class PlayState {
      枚举 播放状态
      Paused/Playing
    }
  }

  %% 驱动与图层(调度)
  namespace 驱动层 {
    class PlayableGraph {
      结构体 播放图
      Create 创建图()
      Connect 连接节点()
      Play/Evaluate 驱动评估()
      Destroy 释放图()
    }
    class PlayableDirector {
      类 驱动组件
      Play/Evaluate 驱动图()
    }
    class PlayableAsset {
      抽象类 资源
      CreatePlayable 创建节点()
      outputs/duration 输出与时长()
    }
    class PlayableBinding {
      结构体 绑定描述
      CreateOutput 创建输出()
      streamName/outputTargetType 目标描述()
    }
  }

  %% 数据层(帧上下文)
  namespace 数据层 {
    class FrameData {
      结构体 帧上下文
      评估快照(由引擎生成)
      effectivePlayState 有效状态
      evaluationType 评估类型
      weight 权重
    }
  }

  %% 关系(核心链路)
  NativePlayableSystem ..> PlayableHandle : 内部驱动状态
  NativePlayableSystem ..> PlayableOutputHandle : 内部驱动输出
  NativePlayableSystem ..> PlayableGraph : 引擎评估与遍历

  PlayableGraph o-- Playable : 管理/连接节点
  PlayableGraph o-- PlayableOutput : 管理输出

  Playable o-- PlayableHandle : 句柄承载状态
  PlayableOutput o-- PlayableOutputHandle : 句柄承载输出

  IPlayable <|.. Playable : 统一入口
  IPlayable <|.. ScriptPlayable_T : 实现
  IPlayableOutput <|.. PlayableOutput : 统一入口

  ScriptPlayable_T --> PlayableBehaviour : 包装逻辑
  FrameData ..> PlayableBehaviour : 回调上下文
  PlayableHandle --> PlayState : 提供播放状态

  PlayableOutput --> Playable : SetSourcePlayable 绑定源节点
  PlayableBinding ..> PlayableOutput : CreateOutput 创建输出
  PlayableAsset ..> PlayableGraph : CreatePlayable 创建节点

  PlayableDirector --> PlayableAsset : 驱动资产
  PlayableDirector --> PlayableGraph : 持有/驱动图

Playables命名空间类型说明

接口

  • IPlayable:所有可播放节点的统一接口,暴露 GetHandle() 以获取底层句柄,从而让不同类型的节点可以用同一种方式被图管理和连接。
  • IPlayableOutput:所有输出端的统一接口,暴露 GetHandle(),用于将图的结果连接到具体输出目标(如 Animator 或 Audio)。
  • IPlayableBehaviour:播放回调接口,定义图开始/结束、节点创建/销毁、帧处理等生命周期钩子,是自定义行为的核心入口。
  • IPlayableAsset:可播放资源接口,规定如何创建节点、输出哪些绑定以及提供时长,PlayableAsset 的所有子类都需遵守。
  • INotification:通知接口,提供唯一 id 标识,用于在播放过程中发送与识别事件。
  • INotificationReceiver:通知接收器接口,实现 OnNotify 来接收 PlayableOutput 推送的通知事件。

  • PlayableAsset:可播放资源基类(ScriptableObject),用于在播放图中生成节点,并提供输出绑定和时长信息。
  • PlayableBehaviour:可播放行为基类,提供默认空实现的生命周期回调,方便用户继承实现自定义帧处理逻辑。
  • Notification:默认通知实现,仅封装 PropertyName 作为事件标识,适合简单通知场景。
  • PlayableExtensionsIPlayable 的扩展工具类,封装播放、暂停、设置时间、连接输入等常用操作,简化 API 使用。
  • PlayableOutputExtensionsIPlayableOutput 的扩展工具类,封装源节点绑定、权重设置、通知推送等常用操作,同时保留旧接口兼容。
  • ScriptPlayableBinding:创建脚本输出绑定的工具类,用于把脚本输出类型注册到图的绑定系统中。
  • PlayableDirector:驱动组件,负责管理 PlayableAssetPlayableGraph 的创建、播放、暂停与停止,通常挂在 GameObject 上控制播放。
  • AnimationPlayableUtilities:动画图工具类,提供便捷方法快速创建动画图并连接到 Animator,适合快速播放片段或混合器。

结构体

  • Playable:通用可播放节点结构体,包装 PlayableHandle,提供统一节点入口与基本类型判断能力。
  • PlayableHandle:底层句柄结构体,控制节点的时间、状态、输入输出等核心数据,是 Playables 系统的核心控制对象。
  • PlayableGraph:播放图容器结构体,负责创建节点、连接关系、评估播放并管理输出生命周期。
  • PlayableOutput:通用输出端结构体,包装 PlayableOutputHandle,用于把图的结果连接到目标输出。
  • PlayableOutputHandle:输出句柄结构体,控制源节点绑定、权重与通知推送,是输出端的核心控制对象。
  • ScriptPlayable<T>:泛型结构体,把 PlayableBehaviour 行为包装成可播放节点,支持脚本驱动的播放逻辑。
  • ScriptPlayableOutput:脚本输出端结构体,用于创建脚本类型输出通道并连接图。
  • PlayableBinding:输出绑定描述结构体,包含输出名称、目标类型与创建输出的方法,用于统一输出描述。
  • FrameData:帧上下文结构体,携带帧时间、权重、状态与输出信息,用于帧回调逻辑。
  • FrameRate:帧率结构体,用于描述图的时间步进与帧率换算,支持不同时间驱动模式。

枚举

  • PlayState:播放状态枚举,描述节点当前运行阶段(Paused 暂停 / Playing 播放中 / Delayed 延迟已废弃)。
  • DirectorWrapMode:时间包裹模式枚举,描述播放到末尾时如何处理(Hold 停留 / Loop 循环 / None 不处理)。
  • DirectorUpdateMode:更新时间模式枚举,描述图使用何种时钟驱动(DSPClock / GameTime / UnscaledGameTime / Manual)。
  • PlayableTraversalMode:遍历模式枚举,决定节点输入计算方式(Mix 混合 / Passthrough 直通)。
  • DataStreamType:数据流类型枚举,旧版兼容用途(Animation / Audio / Texture / None)。
  • FrameData.EvaluationType:评估类型枚举,区分手动评估与播放评估来源(Evaluate / Playback)。

10.4 Handle 层

Handle 层是 C# 侧能直接触达的最底层入口。Playable / PlayableOutput 只是轻量包装,真正的状态、时间、端口、权重与通知都落在句柄上。调用链基本固定:
上层 API → Handle → bindings(C++ FreeFunction)→ 引擎内部评估

只要看到 extern / FreeFunction,基本就进入 C++ 黑盒;上层语义最终都落在句柄调用上。

交互链路

  • 向下(C++ 黑盒):Handle 的 extern / FreeFunction 直接调用引擎实现,负责评估、遍历、数据流与作业调度。
  • 向上(包装层)Playable / PlayableOutput 把常用操作转发到 Handle,保证 API 易用。

PlayableHandle:节点状态的唯一承载

职责:时间、状态、端口、图关系、输入权重。
谁调用它Playable(API 层)把 Play() / Pause() / SetTime() / 端口管理等转发到 PlayableHandle
它调用谁PlayableHandleBindings(C++ FreeFunction),交给引擎黑盒执行。
脚本行为实例ScriptPlayable<T>.GetBehaviour() 最终会从 PlayableHandle 里取回绑定的 PlayableBehaviour 实例(通过 handle 的 object/script instance 通道返回)。
换句话说:Playable/ScriptPlayable 是壳,Behaviour 才是脚本逻辑对象;Behaviour 被“挂”在 Handle 上。

重要方法

  • 有效性与类型IsValid() / IsNull() / GetPlayableType()
  • 播放控制Play() / Pause() / GetPlayState() / GetSpeed() / SetSpeed()
  • 时间与时序GetTime() / SetTime() / GetDuration() / SetDuration()
  • 端口与连接GetInputCount() / SetInputCount() / GetOutputCount() / SetOutputCount()
  • 输入权重SetInputWeightFromIndex() / GetInputWeightFromIndex()
  • 图关系GetGraph() / GetInputHandle() / GetOutputHandle()

Playable 的互相调用关系

  • Playable 持有 PlayableHandle
  • PlayableGetHandle() 只是回传句柄;
  • Playable 的播放/时间/权重操作最终都走 PlayableHandle
  • PlayableHandle.Destroy() 反向通过 PlayableGraph.DestroyPlayable(new Playable(this)) 完成节点销毁。
classDiagram
  direction LR

  class IPlayable {
    接口 节点入口
    GetHandle() 获取句柄
  }
  class Playable {
    结构体 节点包装
    m_Handle 底层句柄
    GetHandle() 获取句柄
    Play()/Pause() 播放控制
    SetTime() 设置时间
  }
  class ScriptPlayable_T {
    泛型结构体 脚本节点
    m_Handle 底层句柄
    Create() 创建节点
    GetBehaviour() 取行为
  }
  class PlayableHandle {
    结构体 节点句柄
    m_Handle 指针
    m_Version 版本
    IsValid() 是否有效
    GetPlayState() 播放状态
    Get/SetTime() 时间读写
    Get/SetDuration() 时长读写
    Get/SetInputCount() 输入端口数
    Get/SetOutputCount() 输出端口数
    SetInputWeight() 输入权重
    GetGraph() 获取所属图
  }
  class PlayableGraph {
    结构体 播放图
    DestroyPlayable() 销毁一个节点
  }
  class 引擎黑盒 {
    C++内核
    FreeFunction入口
    评估/遍历/数据流
  }

  IPlayable <|.. Playable : 实现
  IPlayable <|.. ScriptPlayable_T : 实现
  IPlayable ..> PlayableHandle : GetHandle 获取句柄
  Playable "1" o-- "1" PlayableHandle : 持有/转发
  ScriptPlayable_T "1" o-- "1" PlayableHandle : 持有
  PlayableHandle ..> 引擎黑盒 : FreeFunction调用
  PlayableHandle --> PlayableGraph : GetGraph 取所属图
  PlayableGraph "1" --> "many" Playable : 管理/销毁节点
  PlayableGraph "1" --> "many" ScriptPlayable_T : 管理/销毁节点

PlayableOutputHandle:输出链路的控制点

职责:绑定源节点、控制权重、推送通知、触发遍历与输出。
谁调用它PlayableOutput 及派生输出(如 AnimationPlayableOutput)。
它调用谁PlayableOutputHandleBindings(C++ FreeFunction),由引擎执行真实输出与遍历。

重要方法

  • 绑定与遍历SetSourcePlayable() / GetSourcePlayable() / GetSourceOutputPort()
  • 权重GetWeight() / SetWeight()
  • 通知PushNotification() / AddNotificationReceiver() / RemoveNotificationReceiver()
  • 输出目标SetReferenceObject() / GetReferenceObject()(派生输出会把 target 写到这里)

PlayableOutputHandlePlayableOutput 的关系

  • PlayableOutput 持有 PlayableOutputHandle
  • PlayableOutput.SetSourcePlayable() / SetWeight() 直接转发到句柄;
  • 具体输出类型(动画/音频/纹理)在自身 SetTarget 时,最终写到 OutputHandle 的引用对象。

PlayableOutputHandlePlayableHandle 的关系

  • PlayableOutputHandleSetSourcePlayable(PlayableHandle, port) 指定一个“数据源节点”,可以理解成把输出口接到图上的某个节点
  • 评估时就沿着这条线取结果,没连上的子树不会被计算,也不会写回目标组件。换句话说,绑定源节点就是告诉输出“我只关心这一路”
classDiagram
  direction LR

  class IPlayableOutput {
    接口 输出入口
    GetHandle() 获取句柄
  }
  class PlayableOutput {
    结构体 输出包装
    m_Handle 底层句柄
    SetSourcePlayable() 绑定源节点
    SetWeight() 设置权重
  }
  class ScriptPlayableOutput {
    结构体 脚本输出
    m_Handle 底层句柄
    Create() 创建输出
    GetHandle() 获取句柄
  }
  class PlayableOutputHandle {
    结构体 输出句柄
    m_Handle 指针
    m_Version 版本
    SetSourcePlayable() 绑定源句柄
    GetSourcePlayable() 取源句柄
    GetSourceOutputPort() 取源端口
    Get/SetWeight() 读写权重
    PushNotification() 推送通知
    Set/GetReferenceObject() 目标引用
  }
  class PlayableGraph {
    结构体 播放图
    CreateScriptOutputInternal() 创建脚本输出
    CreatePlayableHandle() 创建句柄
    Connect() 连接节点
  }
  class PlayableHandle {
    结构体 节点句柄
  }
  class 引擎黑盒 {
    C++内核
    FreeFunction入口
    触发遍历/输出
  }

  IPlayableOutput <|.. PlayableOutput : 实现
  IPlayableOutput <|.. ScriptPlayableOutput : 实现
  IPlayableOutput --> PlayableOutputHandle : GetHandle 获取句柄
  PlayableOutput "1" o-- "1" PlayableOutputHandle : 持有/转发
  ScriptPlayableOutput "1" o-- "1" PlayableOutputHandle : 持有
  PlayableGraph "1" --> "many" PlayableOutputHandle : 创建/管理句柄
  PlayableOutputHandle --> PlayableHandle : 绑定源节点
  PlayableOutputHandle ..> 引擎黑盒 : FreeFunction调用

10.5 包装层

包装层的作用就是:把句柄能力整理成更顺手的 C# API
读这层重点看两件事:哪些调用最终落到 Handle,Graph/Output 的入口怎么串起来。

交互链路

  • 图的创建Playable.Create() 内部用 PlayableGraph.CreatePlayableHandle() 拿到句柄,然后交回 Playable
  • 节点操作PlayableExtensionsPlay()/Pause()/SetTime()/SetInputWeight() 最终都走 GetHandle()
  • 输出链路PlayableOutputExtensions.SetSourcePlayable() 实际把源节点句柄和端口交给 PlayableOutputHandle,由输出触发遍历与写回。
  • 行为回调ScriptPlayable<T>PlayableBehaviour 绑定到节点,评估阶段由引擎回调到 PrepareFrame/ProcessFrame

IPlayable:节点的统一入口

定位:可播放节点的统一接口,只做一件事——暴露句柄
方法脉络GetHandle() 是唯一入口,扩展方法最终都会把调用落到 PlayableHandle
和驱动层的关系:Graph 只认句柄,接口起到“统一入口”的作用。
和数据层的关系:接口本身不接触 FrameData,评估阶段由引擎带着句柄进入回调链。

常见 IPlayable 实现很多,这里只列最核心的几类

classDiagram
  direction LR

  class IPlayable {
    接口 可播放入口
    GetHandle() 获取句柄
  }

  class Playable {
    结构体 通用节点
    m_Handle 底层句柄
    Create() 创建节点
  }
  class ScriptPlayable_T {
    泛型结构体 脚本节点
    m_Handle 底层句柄
    Create() 创建节点
    GetBehaviour() 取行为
  }
  class AnimationClipPlayable {
    结构体 动画剪辑节点
    m_Handle 底层句柄
    Create() 创建节点
    Get/SetAnimationClip() 取/设剪辑
  }
  class AnimationMixerPlayable {
    结构体 混合节点
    m_Handle 底层句柄
    Create() 创建节点
  }
  class AnimationLayerMixerPlayable {
    结构体 分层混合节点
    m_Handle 底层句柄
    Create() 创建节点
  }
  class AudioClipPlayable {
    结构体 音频剪辑节点
    m_Handle 底层句柄
    Create() 创建节点
    Get/SetClip() 取/设剪辑
  }
  class PlayableHandle {
    结构体 节点句柄
    m_Handle 指针
    m_Version 版本
  }

  IPlayable <|.. Playable : 实现
  IPlayable <|.. ScriptPlayable_T : 实现
  IPlayable <|.. AnimationClipPlayable : 实现
  IPlayable <|.. AnimationMixerPlayable : 实现
  IPlayable <|.. AnimationLayerMixerPlayable : 实现
  IPlayable <|.. AudioClipPlayable : 实现
  IPlayable ..> PlayableHandle : GetHandle 获取句柄
  Playable "1" o-- "1" PlayableHandle : 持有
  ScriptPlayable_T "1" o-- "1" PlayableHandle : 持有
  AnimationClipPlayable "1" o-- "1" PlayableHandle : 持有
  AnimationMixerPlayable "1" o-- "1" PlayableHandle : 持有
  AnimationLayerMixerPlayable "1" o-- "1" PlayableHandle : 持有
  AudioClipPlayable "1" o-- "1" PlayableHandle : 持有
  PlayableGraph "1" --> "many" IPlayable : 管理节点
  PlayableOutput --> IPlayable : 绑定源节点

Playable:节点侧的壳

定位:通用节点包装,核心是持有 PlayableHandle
方法脉络:本体方法负责“创建与取句柄”,扩展方法负责“播放、时间、端口、连接、权重”等常用操作。
和驱动层的关系:节点由 PlayableGraph.CreatePlayableHandle() 生成并纳入图管理;连接与权重最终落在 Graph/Handle。
和数据层的关系:不直接持有 FrameData,评估阶段才会进入回调链。

Playable 本身可以看成一个多输入、多输出的有向节点,核心运行状态都挂在句柄上(speed/time/duration/isDone/PlayState 之类)。这层读源码时,重点不是“类型有多少”,而是端口、连接、权重、遍历这四件事。

classDiagram
  direction LR

  class IPlayable {
    接口 节点入口
    GetHandle() 获取句柄
  }
  class Playable {
    结构体 节点包装
    m_Handle 底层句柄
    Create() 创建节点
    GetHandle() 获取句柄
    IsPlayableOfType() 类型判断
    IsNull()/IsValid() 句柄判定
    Destroy() 销毁节点
    Play()/Pause() 播放控制
    Get/SetTime() 时间读写
    Get/SetDuration() 时长读写
    Get/SetInputCount() 输入端口数
    Get/SetOutputCount() 输出端口数
    GetInput()/GetOutput() 取端口节点
    SetInputWeight()/GetInputWeight() 权重读写
    ConnectInput()/DisconnectInput() 连接管理
    AddInput() 追加输入
    Get/SetTraversalMode() 遍历模式
  }
  class ScriptPlayable_T {
    泛型结构体 脚本节点
    m_Handle 底层句柄
    Create() 创建节点
    GetBehaviour() 取行为
  }
  class PlayableHandle {
    结构体 节点句柄
    m_Handle 指针
    m_Version 版本
  }
  class PlayableGraph {
    结构体 播放图
    m_Handle 指针
    m_Version 版本
    CreatePlayableHandle() 创建句柄
    Connect/Play/Evaluate() 图操作
  }
  class PlayableAsset {
    抽象类 资源入口
    CreatePlayable() 生成节点
  }

  IPlayable <|.. Playable : 实现
  IPlayable <|.. ScriptPlayable_T : 实现
  ScriptPlayable_T ..> Playable : 隐式转为Playable
  Playable ..> ScriptPlayable_T : 显式转换
  IPlayable ..> PlayableHandle : GetHandle 获取句柄
  Playable "1" o-- "1" PlayableHandle : 持有
  ScriptPlayable_T "1" o-- "1" PlayableHandle : 持有
  PlayableGraph "1" --> "many" Playable : 管理节点
  PlayableGraph "1" --> "many" ScriptPlayable_T : 管理节点
  PlayableAsset ..> Playable : CreatePlayable 返回节点

节点属性/方法(端口与连接)

  • input/output port 是连接锚点:连接时始终是“source 输出端口 → destination 输入端口”。
  • 端口必须先存在:输入/输出数量不足会报错,需要 SetInputCount() / SetOutputCount(),或者 Create(inputCount) 时一次给足。
  • 同一端口不能重复连接:一个 input/output 只能连一条线,重复连接会被判定为非法拓扑。
  • 不允许形成环:Playables 图是 DAG,出现回路会导致评估遍历失效。
  • 连接入口只有两个PlayableGraph.Connect(...)Playable.ConnectInput(...),本质都是对 Graph/Handle 的转发。
  • Clip 相关方法GetClip/SetClip 不属于通用 Playable,而是具体类型各自提供(如 AnimationClipPlayableAudioClipPlayableVideoClipPlayable 等),命名也可能是 GetAnimationClip/SetAnimationClipGetCamera 等。
  • Playable 是 struct,具体类型(如 AnimationClipPlayableScriptPlayable)靠显/隐式转换统一到 PlayableHandle 这一条入口;资源型节点的访问方法由具体类型自己定义。
// source: 上游节点(输出端口提供数据)
// destination: 下游节点(输入端口接收数据)
PlayableGraph.Connect(...)

bool Connect<U, V>(  
  U source,              // 上游节点
  int sourceOutputPort,  // 上游输出端口索引
  V destination,         // 下游节点
  int destinationInputPort)  // 下游输入端口索引
  where U : struct, IPlayable  
  where V : struct, IPlayable

允许的结构(有向无环):

不允许的结构(回路):

权重与混合

  • 输入权重默认是 0,Mixer/Layer 这类节点的输出基本都取决于这组权重。
  • 连接时可以指定 weight,也可以后续用 SetInputWeight() 调整,最终由 Handle 记录并参与评估。

运行属性/方法(时间与状态)

  • PlayState 表示运行状态(Playing/Paused)。
  • Speed/SetTime/SetDuration/IsDone 控制或反映节点的时间推进与完成状态。
  • Destroy() 会通过所属 Graph 销毁节点,IsValid/IsNull 用来判定句柄有效性。

TraversalMode(遍历方式)

  • Mix:对所有输入按权重混合。
  • Passthrough:只取“对应端口”的输入,理解成“直通某一路”。

可以把每个 PlayableOutput 理解成一棵子树的根。一张图里可能有多个输出口,每个输出口都像“从根往上拉数据”的入口。
PlayableOutput 通过 SetSourcePlayable 绑定源节点,并带一个 sourceOutputPort 的概念。
在 Passthrough 模式里可以理解成:
一个中间节点有 input0input1 两路输入,你同时创建了 output0output1 两个输出口;
output0 发起评估就只取 input0,切到 output1 就只取 input1,这就是“输出口序号对应输入口序号”的含义。

  1. Passthrough 时只走一条输入
    中间节点是 Passthrough,输出口一旦发起评估,就只会取某一个 input 的数据,其它输入不参与。

  2. 切换输入口的方式
    这里“切换输入口”不是去改节点的 input,而是切换哪个输出口在驱动评估
    同一张图里创建多个 PlayableOutput,或调整输出的创建顺序/启用顺序,就会改变“谁来发起遍历”。
    评估入口一变,Passthrough 直通的输入口自然随之变化。

  3. Mix 会把多路输入一起算
    切到 Mix 后,多条输入都会参与,权重决定最终合成结果。

  4. 源码注释的原意
    注释明确写的是“输出连接到第 n 个 input 时,只传播第 n 个输入”。
    也就是说设计意图是:sourceOutputPort ↔ input 端口序号一一对应。

  5. 实测现象:更像“按输出顺序取输入”
    从可视化结果看,直通的输入口与 PlayableOutput 的顺序有对应关系:
    第 0 个输出 → 取 input 0;第 1 个输出 → 取 input 1;依此类推。
    这意味着在实际使用中,直通输入更多由 PlayableOutput 的创建/评估顺序决定,而不是 SetSourcePlayable(playable, port) 里的 port

通俗一点说:输出口的“编号”不是你传的 sourceOutputPort,而是它在 Graph 里排第几个
所以你传 30/50/100 没有改变“第几个输出”的事实,直通输入自然不会变。

下面这段代码能验证这一点(重点看注释):

void ValidateTraversalMode()
{
    DestroyGraph(graph);
    graph = PlayableGraph.Create("TraversalModeGraph");

    // 这三个输出的“顺序”分别是 0、1、2
    ScriptPlayableOutput output0 = ScriptPlayableOutput.Create(graph, "customOutput0");
    ScriptPlayableOutput output1 = ScriptPlayableOutput.Create(graph, "customOutput1");
    ScriptPlayableOutput output2 = ScriptPlayableOutput.Create(graph, "customOutput2");

    var mixer = ScriptPlayable<CustomMixerBehaviour>.Create(graph);
    mixer.SetTraversalMode(PlayableTraversalMode.Passthrough); // 直通模式
    mixer.SetInputCount(10); // 不足会报 Connecting invalid input

    // 把不同节点接到不同 input 上
    mixer.ConnectInput(0, playableA, 0, 1);
    mixer.ConnectInput(1, playableB, 0, 1);
    mixer.ConnectInput(2, playableC, 0, 1);

    // 这里的 port 看起来很大,但实测不影响“直通哪一路”
    output0.SetSourcePlayable(mixer, 30);
    output1.SetSourcePlayable(mixer, 50);
    output2.SetSourcePlayable(mixer, 100);

    // 直通关系更像是:output0→input0,output1→input1,output2→input2
    graph.Play();
}
  1. 权重查找逻辑里的佐证
    Animation 权重查找里也能看到“按 outputPort 找输入”的意图:
    设计上仍把 sourceOutputPort 当作输出端口索引来用。
  • TraversalMode.Passthrough 的工程结论
  • 结论:直通哪一路输入,取决于 PlayableOutput 在 Graph 里的创建/评估顺序SetSourcePlayable 的 port 目前不可靠。
  • 建议:
    • 不要把 sourceOutputPort 当作输入索引。
    • 只在结构固定、输出数量可控时使用 Passthrough。
    • 复杂图优先用 Mix + 权重控制。

IPlayableOutput:输出的统一入口

定位:可播放输出的统一接口,只负责暴露输出句柄
方法脉络GetHandle() 是唯一入口,扩展方法最终都会落到 PlayableOutputHandle
和驱动层的关系:输出是评估入口,Graph 通过句柄识别并调度输出。
和数据层的关系:接口本身不接触 FrameData,只是输出句柄的桥梁。

常见 IPlayableOutput 实现也很多,这里只列最核心的几类

classDiagram
  direction LR

  class IPlayableOutput {
    接口 输出入口
    GetHandle() 获取句柄
  }

  class PlayableOutput {
    结构体 通用输出
    m_Handle 底层句柄
    GetHandle() 获取句柄
  }
  class ScriptPlayableOutput {
    结构体 脚本输出
    m_Handle 底层句柄
    Create() 创建输出
  }
  class AnimationPlayableOutput {
    结构体 动画输出
    m_Handle 底层句柄
    Create() 创建输出
  }
  class AudioPlayableOutput {
    结构体 音频输出
    m_Handle 底层句柄
    Create() 创建输出
  }
  class TexturePlayableOutput {
    结构体 纹理输出
    m_Handle 底层句柄
    Create() 创建输出
  }
  class PlayableOutputHandle {
    结构体 输出句柄
    m_Handle 指针
    m_Version 版本
  }
  class PlayableGraph {
    结构体 播放图
    m_Handle 指针
    m_Version 版本
    CreateScriptOutputInternal() 创建脚本输出
  }

  IPlayableOutput <|.. PlayableOutput : 实现
  IPlayableOutput <|.. ScriptPlayableOutput : 实现
  IPlayableOutput <|.. AnimationPlayableOutput : 实现
  IPlayableOutput <|.. AudioPlayableOutput : 实现
  IPlayableOutput <|.. TexturePlayableOutput : 实现
  IPlayableOutput --> PlayableOutputHandle : GetHandle 获取句柄
  PlayableOutput "1" o-- "1" PlayableOutputHandle : 持有
  ScriptPlayableOutput "1" o-- "1" PlayableOutputHandle : 持有
  AnimationPlayableOutput "1" o-- "1" PlayableOutputHandle : 持有
  AudioPlayableOutput "1" o-- "1" PlayableOutputHandle : 持有
  TexturePlayableOutput "1" o-- "1" PlayableOutputHandle : 持有
  ScriptPlayableOutput ..> PlayableOutput : 可隐式转为通用输出
  PlayableOutput ..> ScriptPlayableOutput : 需显式转换
  PlayableGraph "1" --> "many" IPlayableOutput : 管理输出

PlayableOutput:输出侧的壳

定位:通用输出包装,核心仍是 PlayableOutputHandle
方法脉络:本体负责取句柄与类型判断,扩展方法负责绑定源、权重、通知与引用对象。
和驱动层的关系:输出是图的评估入口;没连输出就不会触发遍历,图也不会“跑”。
和数据层的关系:输出侧不直接处理 FrameData,只是把数据交给目标对象消费。

classDiagram
  direction LR

  class IPlayableOutput {
    接口 输出入口
    GetHandle() 获取句柄
  }

  class PlayableOutput {
    结构体 输出包装
    m_Handle 底层句柄
    GetHandle() 获取句柄
    IsPlayableOutputOfType() 类型判断
    IsOutputNull()/IsOutputValid() 句柄判定
    Get/SetSourcePlayable() 绑定源节点
    GetSourceOutputPort() 源端口
    Get/SetWeight() 权重读写
    Get/SetReferenceObject() 目标引用
  }
  class ScriptPlayableOutput {
    结构体 脚本输出
    m_Handle 底层句柄
    Create() 创建输出
  }
  class PlayableBinding {
    结构体 输出绑定
    streamName 输出名
    sourceObject 源对象
    outputTargetType 目标类型
    createOutputMethod 创建回调
    CreateOutput() 创建输出
  }
  class PlayableOutputHandle {
    结构体 输出句柄
    m_Handle 指针
    m_Version 版本
  }
  class PlayableGraph {
    结构体 播放图
    m_Handle 指针
    m_Version 版本
  }
  class FrameData {
    结构体 帧上下文
  }

  IPlayableOutput <|.. PlayableOutput : 实现
  IPlayableOutput <|.. ScriptPlayableOutput : 实现
  IPlayableOutput --> PlayableOutputHandle : GetHandle 获取句柄
  PlayableBinding ..> PlayableOutput : CreateOutput 创建输出
  ScriptPlayableOutput ..> PlayableOutput : 可隐式转为通用输出
  PlayableOutput ..> ScriptPlayableOutput : 需显式转换
  PlayableOutput "1" o-- "1" PlayableOutputHandle : 持有
  ScriptPlayableOutput "1" o-- "1" PlayableOutputHandle : 持有
  PlayableGraph "1" --> "many" PlayableOutput : 管理输出
  PlayableGraph "1" --> "many" ScriptPlayableOutput : 管理输出
  PlayableOutput ..> FrameData : 触发评估产生上下文
  • SourcePlayable / SourceOutputPort:输出连接的源节点与端口,通过 SetSourcePlayable(Playable value, int port) 绑定。
  • ReferenceObject:通常对应 Track/Binding 的引用对象。
  • Weight:输出权重,默认 1,SetWeight(float) 可调。
  • Target:只在具体输出类型里出现(Animation/Audio/Texture 等),表示真正接受数据的目标对象。
  • 约束(更准确):每个 PlayableOutput 只能绑定一个 SourcePlayable(通过 SetSourcePlayable 指定)。
  • 补充:同一个 Playable 是否允许被多个 PlayableOutput 同时作为 source,取决于输出类型与引擎内部实现;工程上如果需要“一源多路输出”,建议用明确的扇出结构(例如增加中间节点/显式复制链路),避免依赖不稳定的隐式行为。

ScriptPlayable_T:把行为挂到节点上

定位:带行为的节点包装,仍实现 IPlayable
方法脉络Create() 创建句柄并绑定脚本实例;GetBehaviour() 取回行为对象。
ScriptPlayable 的核心是把脚本行为挂到句柄上

  • Create()CreateHandle(),里面先 SetInputCount(),最后 SetScriptInstance();这一步会触发 IPlayableBehaviour.OnPlayableCreate()
  • 传模板走克隆流程,不传就直接 new(或 ScriptableObject.CreateInstance)。
  • GetBehaviour() 实际通过 PlayableHandle.GetObject<T>() 拿到脚本实例。

结果是行为与节点解耦:节点由 PlayableHandle 托管,行为只是挂在句柄上的实例。

classDiagram
  direction LR

  class IPlayable {
    接口 节点入口
    GetHandle() 获取句柄
  }
  class ScriptPlayable_T {
    泛型结构体 脚本节点
    m_Handle 底层句柄
    Create() 创建节点
    GetBehaviour() 取行为
    GetHandle() 获取句柄
  }
  class PlayableBehaviour {
    抽象类 行为基类
  }
  class PlayableHandle {
    结构体 节点句柄
    m_Handle 指针
    m_Version 版本
  }

  IPlayable <|.. ScriptPlayable_T : 实现
  ScriptPlayable_T --> PlayableBehaviour : 包装行为
  ScriptPlayable_T "1" o-- "1" PlayableHandle : 持有
  IPlayable --> PlayableHandle : GetHandle 获取句柄

IPlayableBehaviour:行为回调接口

定位:脚本行为的回调协议,定义节点生命周期与每帧评估时机。
方法脉络:图级(OnGraphStart/Stop)、节点级(OnPlayableCreate/Destroy)、播放级(OnBehaviourPlay/Pause)、帧级(PrepareFrame/ProcessFrame)。
调用方式:接口本身不负责触发,回调由引擎评估流程在合适时机注入。

classDiagram
  direction LR

  class IPlayableBehaviour {
    接口 行为回调
    OnGraphStart/Stop
    OnPlayableCreate/Destroy
    OnBehaviourPlay/Pause
    PrepareFrame/ProcessFrame
  }
  class PlayableBehaviour {
    抽象类 行为基类
  }
  class ScriptPlayable_T {
    泛型结构体 脚本节点
    GetBehaviour() 取行为
  }
  class PlayableHandle {
    结构体 节点句柄
    SetScriptInstance() 绑定行为
  }
  class FrameData {
    结构体 帧上下文
  }

  IPlayableBehaviour <|.. PlayableBehaviour : 实现
  ScriptPlayable_T --> PlayableHandle : 绑定实例
  PlayableHandle --> IPlayableBehaviour : GetObject 取行为
  FrameData ..> IPlayableBehaviour : 评估回调上下文

这张图把回调顺序按引擎评估链路拉直:
Create 绑定实例 → Play 触发图级回调 → 每帧评估进入 PrepareFrame/ProcessFrameStop/Destroy 收尾。
只列主干流程,方便对照源码里的触发点。

flowchart TD
  A["创建 ScriptPlayable
OnPlayableCreate()"] subgraph B[Graph Play 触发] direction TB C["OnGraphStart()"] D{"PlayState"} E["OnBehaviourPlay()"] F["OnBehaviourPause()"] C --> D D -->|isPlaying| E D -->|isPaused| F end subgraph G[Graph Play 后每帧执行] direction TB H["前序遍历:PrepareFrame()"] I["后序遍历:ProcessFrame()"] H --> I end subgraph J[Graph Stop 触发] direction TB K["OnBehaviourPause()"] L["OnGraphStop()"] K --> L end subgraph M[Graph Destroy 触发] direction TB N["OnPlayableDestroy()"] end A --> C E --> H F --> H I --> K L --> N

PlayableBehaviour:行为回调基类

定位:行为基类,用于继承扩展;提供一套默认空实现,按需覆写。
关键回调:图级(OnGraphStart/Stop)、节点级(OnPlayableCreate/Destroy)、播放级(OnBehaviourPlay/Pause)、帧级(PrepareData / PrepareFrame / ProcessFrame)。
补充:实现 ICloneable,默认 MemberwiseClone()OnBehaviourDelay 已废弃。
和驱动层的关系:回调触发由引擎评估流程决定,不由 C# 主动调用。

classDiagram
  direction LR

  class IPlayableBehaviour {
    接口 行为回调
    OnGraphStart/Stop
    OnPlayableCreate/Destroy
    OnBehaviourPlay/Pause
    PrepareData/PrepareFrame/ProcessFrame
  }
  class PlayableBehaviour {
    抽象类 行为基类
    Clone() 默认克隆
  }
  class ScriptPlayable_T {
    泛型结构体 脚本节点
    Create() 创建节点
    GetBehaviour() 取行为
  }
  class FrameData {
    结构体 帧上下文
  }

  IPlayableBehaviour <|.. PlayableBehaviour : 实现
  PlayableBehaviour <|-- ScriptPlayable_T : 作为行为基类
  FrameData ..> IPlayableBehaviour : 评估回调上下文

10.6 数据层

数据层的两个关键结构是 FrameDataFrameRate
前者承载评估结果,后者提供时间基准;都来自评估流程,但用途并不相同。

FrameData:评估快照

FrameData评估阶段生成的帧快照,不用于长期保存。
当图被评估(PlayEvaluate),C++ 侧生成并填充 FrameData,再交给回调使用。

它承载的内容

  • 一帧的上下文快照:deltaTime / weight / effectiveWeight / effectiveSpeed / flags 等。
  • 评估类型与状态由 flags 推导:evaluationTypeseekOccurredtimeLoopedtimeHeld
  • output 记录本次评估的输出端,可用来区分输出来源。

它从哪里来

  • 由引擎评估流程在 C++ 侧生成并填充,然后注入到 IPlayableBehaviour 的回调里。
  • OnBehaviourPlay/PausePrepareFrameProcessFrame 中拿到的都是这次评估快照。

读源码时的关注点

  • FrameDatastruct,字段多为 internal,对外暴露只读属性,强调“只读上下文”。
  • 它不做计算,只承载结果;结果来自 C++ 评估
classDiagram
  direction LR

  class 引擎黑盒 {
    C++评估
    填充FrameData
  }
  class FrameData {
    结构体 帧上下文
    frameId 帧编号
    deltaTime 时间步长
    weight 权重
    effectiveWeight 有效权重
    effectiveParentSpeed 父节点速度
    effectiveSpeed 有效速度
    evaluationType 评估类型
    seekOccurred 是否跳转
    timeLooped 是否循环
    timeHeld 是否保持
    output 输出端
    effectivePlayState 有效播放状态
  }
  class IPlayableBehaviour {
    接口 行为回调
    OnBehaviourPlay/Pause
    PrepareFrame/ProcessFrame
  }

  引擎黑盒 ..> FrameData : 评估生成
  FrameData ..> IPlayableBehaviour : 回调输入

FrameRate:时间基准

FrameRate 负责“时间如何映射为帧”。它不是评估上下文,而是时间刻度
用来表达 24/30/60fps 以及 29.97 这类掉帧模式。

它提供的能力

  • 预置帧率常量(k_24Fps / k_30Fps / k_60Fps 等)。
  • ratedropFrame 用于表示实际帧率与掉帧模式。
  • 比较与格式化接口,便于时间换算与显示。
classDiagram
  direction LR

  class FrameRate {
    结构体 帧率基准
    m_Rate 内部帧率
    rate 实际帧率
    dropFrame 是否掉帧
    IsValid 是否有效
  }
  class PlayableGraph {
    结构体 播放图
    m_Handle 指针
    m_Version 版本
  }

  PlayableGraph ..> FrameRate : 依赖时间基准

10.7 驱动与图层

这一层只看两件事:谁驱动图,以及图如何创建/连接/评估/销毁。核心类型是 PlayableDirectorPlayableGraph

PlayableGraph:图的创建与调度

定位:图容器,负责创建节点、连接关系、评估与销毁。
方法脉络Create 建图,Connect/Disconnect 管连接,Play/Stop/Evaluate 管评估,Destroy 管释放。
和句柄层的关系:连接最终落在 PlayableHandle/PlayableOutputHandle,Graph 是调度入口。
和驱动层的关系PlayableDirector 会持有 Graph;Graph 也可以通过 GetResolver() 找到当前的驱动宿主(返回 IExposedPropertyTable,如果是Timeline一般返回的就是PlayableDirector)。

classDiagram
  direction LR

  class PlayableGraph {
    结构体 播放图
    m_Handle 指针
    m_Version 版本
    Create/Create(string) 创建图
    Connect/Disconnect 连接管理
    Play/Stop/Evaluate 驱动评估
    Destroy 释放图
    GetRootPlayable/GetOutput 查询节点
    GetResolver 取驱动宿主
  }
  class IPlayable {
    接口 节点入口
    GetHandle() 获取句柄
  }
  class IPlayableOutput {
    接口 输出入口
    GetHandle() 获取句柄
  }
  class IExposedPropertyTable {
    接口 绑定表
  }
  class PlayableHandle {
    结构体 节点句柄
  }
  class PlayableOutputHandle {
    结构体 输出句柄
  }
  class DirectorUpdateMode {
    枚举 更新时间模式
  }

  PlayableGraph "1" --> "many" IPlayable : 管理节点
  PlayableGraph "1" --> "many" IPlayableOutput : 管理输出
  IPlayable --> PlayableHandle : GetHandle
  IPlayableOutput --> PlayableOutputHandle : GetHandle
  PlayableGraph ..> IExposedPropertyTable : GetResolver
  PlayableGraph ..> DirectorUpdateMode : 时间驱动模式

PlayableDirector:图的驱动入口

定位:组件级驱动器,把 PlayableAsset 转成 PlayableGraph 并驱动播放。
方法脉络Play/Stop/Pause/Evaluate 是控制入口,playableAsset/playableGraph 是两端挂载点。
绑定关系SetGenericBinding/GetGenericBinding 管输出绑定,RebindPlayableGraphOutputs 负责重绑。
时间驱动timeUpdateMode 决定用 GameTime/Unscaled/Manual/DSPClock;playOnAwake 决定是否自动开播。

classDiagram
  direction LR

  class PlayableDirector {
    类 驱动组件
    Play/Pause/Stop/Evaluate
    playableAsset 资产入口
    playableGraph 图句柄
    time 当前时间
    initialTime 初始时间
    duration 资产时长
    timeUpdateMode 时间模式
    Set/GetGenericBinding 绑定关系
    RebuildGraph 重建图
    playOnAwake 自动播放
  }
  class PlayableAsset {
    抽象类 资源
    CreatePlayable 创建节点
    outputs 输出集合
  }
  class PlayableGraph {
    结构体 播放图
  }
  class PlayableBinding {
    结构体 输出绑定
  }
  class DirectorWrapMode {
    枚举 包裹模式
  }
  class DirectorUpdateMode {
    枚举 更新时间模式
  }
  class IExposedPropertyTable {
    接口 绑定表
  }

  PlayableDirector --> PlayableAsset : 驱动资产
  PlayableDirector --> PlayableGraph : 持有/驱动图
  PlayableAsset "1" o-- "many" PlayableBinding : outputs
  PlayableDirector ..> DirectorWrapMode : 播放包裹
  PlayableDirector ..> DirectorUpdateMode : 时间驱动
  PlayableDirector ..> PlayableBinding : 绑定输出
  PlayableDirector <|.. IExposedPropertyTable : 绑定表实现

Play 会不会构建图

  • Play() 会确保当前资产对应的图被创建并可评估;如果图还没建好,会触发构建流程(等价于先建图再播放)。
  • 已存在的图则直接进入播放。

几个容易混淆的点

  • PlayableAsset.outputs 是一组 PlayableBinding,每个 binding 描述一个输出端如何创建。
  • IExposedPropertyTable 可以理解成 “绑定表”:key → 具体对象(比如 Animator/AudioSource)。
    Director 实现了它,Graph 用它来把输出端的 ReferenceObject 解析成真正的 target。

RebuildGraph 做了什么

  1. 读取 PlayableAsset,触发 CreatePlayable 构建根节点。
  2. 资产返回的节点树交给 PlayableGraph 管理,节点间连接落到 PlayableHandle
  3. 遍历 PlayableAsset.outputs,按 PlayableBinding.CreateOutput 创建输出端,并 SetSourcePlayable 绑定根节点。
  4. 输出目标的绑定值来自 IExposedPropertyTable(也就是 Director 的绑定表)。
  5. 最终生成一个可评估的 Graph,等待 Play/Evaluate 驱动。
flowchart TD
  A[PlayableDirector.RebuildGraph] --> B[PlayableAsset.CreatePlayable]
  B --> C[PlayableGraph 生成节点树]
  C --> D[PlayableBinding.CreateOutput 创建输出]
  D --> E[PlayableOutput.SetSourcePlayable 绑定根节点]
  E --> F[IExposedPropertyTable 解析输出绑定]

10.8 资源层

资源层只关心两件事:播什么往哪里输出。对应的核心类型就是 PlayableAssetPlayableBinding

PlayableAsset:播什么

定位:资源入口,负责描述“这一段播放内容是什么”,并提供 CreatePlayable 构建节点树。
方法脉络CreatePlayable 是关键入口;outputs 返回一组 PlayableBindingduration 默认走 PlayableBinding.DefaultDuration
和驱动层的关系PlayableDirector 读取 PlayableAsset,触发 CreatePlayable,再交给 PlayableGraph 管理。

这层更像“静态配置”:运行时不会直接操作资源本体,而是用它来生成图里的节点。
Timeline 只是 PlayableAsset 的一种实现(不展开),但思路相同:资产描述结构,Graph 负责运行。
Timeline 的资源文件扩展名是 .playable,序列化后就是 TimelineAsset,它本质上继承自 PlayableAsset

classDiagram
  direction LR

  class PlayableAsset {
    抽象类 资源入口
    CreatePlayable 创建节点
    outputs 输出集合
    duration 时长
  }
  class PlayableDirector {
    类 驱动组件
    playableAsset 资产入口
    playableGraph 图句柄
  }
  class PlayableGraph {
    结构体 播放图
  }
  class PlayableBinding {
    结构体 输出绑定
    CreateOutput 创建输出
  }

  PlayableDirector --> PlayableAsset : 驱动资产
  PlayableDirector --> PlayableGraph : 持有/驱动图
  PlayableAsset ..> PlayableGraph : CreatePlayable 生成节点
  PlayableAsset "1" o-- "many" PlayableBinding : outputs

PlayableBinding:往哪里输出

定位:输出绑定描述,记录“输出名 + 目标类型 + 如何创建输出”。
方法脉络CreateOutput() 在建图时被调用,调用内部 CreateOutputMethod 创建具体 PlayableOutput
和驱动层的关系PlayableDirector 读取 PlayableAsset.outputs,把每个 binding 转成输出端,并用绑定表解析目标对象。

简单理解:

  • PlayableAsset 提供一组 PlayableBinding(输出清单);
  • PlayableBinding 负责把“清单项”变成真正的输出端;
  • PlayableDirector 再把输出端绑定到场景对象。
classDiagram
  direction LR

  class PlayableAsset {
    抽象类 资源入口
    CreatePlayable 创建节点
    outputs 输出集合
    duration 时长
  }
  class PlayableBinding {
    结构体 输出绑定
    streamName 输出名
    sourceObject 源对象
    outputTargetType 目标类型
    createOutputMethod 创建回调
    CreateOutput 创建输出
  }
  class PlayableDirector {
    类 驱动组件
    Set/GetGenericBinding 绑定关系
    playableAsset 资产入口
    playableGraph 图句柄
  }
  class PlayableGraph {
    结构体 播放图
  }
  class PlayableOutput {
    结构体 输出包装
  }
  class IExposedPropertyTable {
    接口 绑定表
  }

  PlayableAsset "1" o-- "many" PlayableBinding : outputs
  PlayableBinding ..> PlayableOutput : CreateOutput
  PlayableDirector --> PlayableBinding : 读取输出清单
  PlayableDirector --> PlayableGraph : 持有/驱动图
  PlayableDirector <|.. IExposedPropertyTable : 绑定表实现

10.9 使用Playables类图与类型索引

使用Playables的类图

classDiagram
  direction LR

  %% Playables 命名空间
  namespace UnityEnginePlayables {
    class PlayableGraph {
      结构体 播放图容器
      创建连接节点并驱动评估与销毁
      Create 创建图
      Play 播放图
      Stop 停止图
      Evaluate 手动评估
      Connect 连接节点
    }

    class Playable {
      结构体 通用节点
      封装PlayableHandle并提供统一节点入口
      GetHandle 获取句柄
      IsPlayableOfType 类型判断
    }

    class PlayableHandle {
      结构体 底层句柄
      控制时间状态输入输出等核心数据
      IsValid 是否有效
      GetGraph 所属图
      Play 播放
      Pause 暂停
      SetTime 设置时间
    }

    class PlayableOutput {
      结构体 通用输出端
      封装PlayableOutputHandle并连接输出目标
      GetHandle 获取输出句柄
    }

    class PlayableOutputHandle {
      结构体 输出句柄
      绑定源节点控制权重并派发通知
      SetSourcePlayable 设置源节点
      GetWeight 获取权重
      SetWeight 设置权重
      PushNotification 推送通知
    }

    class PlayableBinding {
      结构体 输出绑定描述
      描述输出目标类型并提供创建输出入口
      streamName 输出名称
      outputTargetType 目标类型
      CreateOutput 创建输出
    }

    class PlayableAsset {
      抽象类 可播放资产
      生成图节点并提供默认输出
      CreatePlayable 创建节点
      duration 时长
      outputs 输出集合
    }

    class PlayableDirector {
      类 驱动组件
      驱动PlayableAsset并管理PlayableGraph播放
      Play 播放
      Pause 暂停
      Stop 停止
      Evaluate 评估
    }

    class IPlayable {
      接口 节点接口
      统一暴露获取PlayableHandle的入口
      GetHandle 获取句柄
    }

    class IPlayableOutput {
      接口 输出接口
      统一暴露获取PlayableOutputHandle的入口
      GetHandle 获取输出句柄
    }

    class PlayState {
      枚举 播放状态
      描述节点运行阶段与有效状态
      Paused 暂停
      Playing 播放中
      Delayed 延迟已废弃
    }
  }

  %% 动画命名空间
  namespace UnityEngineAnimations {
    class AnimationClipPlayable {
      结构体 动画片段节点
      播放单个动画剪辑并可作为图输入
      Create 创建节点
      GetHandle 获取句柄
    }
    class AnimationMixerPlayable {
      结构体 动画混合节点
      混合多个动画输入形成最终动画
      Create 创建节点
      GetHandle 获取句柄
    }
    class AnimationLayerMixerPlayable {
      结构体 动画层混合节点
      支持层级混合与遮罩控制
      Create 创建节点
      GetHandle 获取句柄
    }
    class AnimationOffsetPlayable {
      结构体 动画偏移节点
      对动画应用位移或旋转偏移
      Create 创建节点
      GetHandle 获取句柄
    }
    class AnimationPosePlayable {
      结构体 动画姿态节点
      用于提供或固定某一姿势
      Create 创建节点
      GetHandle 获取句柄
    }
    class AnimationRemoveScalePlayable {
      结构体 移除缩放节点
      在动画中去除缩放影响
      Create 创建节点
      GetHandle 获取句柄
    }
    class AnimationMotionXToDeltaPlayable {
      结构体 根运动转换节点
      将根运动转换为位移增量
      Create 创建节点
      GetHandle 获取句柄
    }
    class AnimationScriptPlayable {
      结构体 动画作业节点
      绑定动画作业进行自定义处理
      Create 创建节点
      GetHandle 获取句柄
    }
    class AnimatorControllerPlayable {
      结构体 控制器节点
      驱动AnimatorController作为图节点
      Create 创建节点
      GetHandle 获取句柄
    }
    class AnimationPlayableOutput {
      结构体 动画输出端
      把动画图输出连接到Animator
      Create 创建输出
      SetTarget 设置目标Animator
      GetHandle 获取输出句柄
    }
    class AnimationPlayableGraphExtensions {
      静态类 动画图扩展
      提供创建动画输出等便捷入口
      CreateAnimationOutput 创建输出
    }
    class AnimationPlayableExtensions {
      静态类 动画节点扩展
      提供动画节点常用操作
      SetApplyFootIK 设置脚部IK
    }
    class AnimationPlayableOutputExtensions {
      静态类 动画输出扩展
      提供动画输出端的辅助方法
      GetAnimationStreamSource 获取源
    }
    class AnimationPlayableBinding {
      静态类 动画绑定
      创建动画输出绑定描述
      Create 创建绑定
    }
    class IAnimationJobPlayable {
      接口 动画作业接口
      定义动画作业可播放节点
      GetJobData 获取作业数据
    }
  }

  %% 音频命名空间
  namespace UnityEngineAudio {
    class AudioClipPlayable {
      结构体 音频片段节点
      播放音频剪辑并可设置延迟
      Create 创建节点
      GetHandle 获取句柄
    }
    class AudioMixerPlayable {
      结构体 音频混合节点
      混合多个音频输入
      Create 创建节点
      GetHandle 获取句柄
    }
    class AudioPlayableOutput {
      结构体 音频输出端
      把音频图输出连接到AudioSource
      Create 创建输出
      SetTarget 设置目标AudioSource
      GetHandle 获取输出句柄
    }
    class AudioPlayableBinding {
      静态类 音频绑定
      创建音频输出绑定描述
      Create 创建绑定
    }
    class AudioPlayableGraphExtensions {
      静态类 音频图扩展
      提供创建音频输出等入口
      CreateAudioOutput 创建输出
    }
  }

  %% 视频命名空间
  namespace UnityEngineExperimentalVideo {
    class VideoClipPlayable {
      结构体 视频片段节点
      播放视频剪辑并支持寻址
      Create 创建节点
      GetHandle 获取句柄
    }
  }

  %% 纹理 相机 材质命名空间
  namespace UnityEngineExperimentalPlayables {
    class TexturePlayableOutput {
      结构体 纹理输出端
      把图输出连接到RenderTexture
      Create 创建输出
      SetTarget 设置目标RenderTexture
      GetHandle 获取输出句柄
    }
    class TexturePlayableBinding {
      静态类 纹理绑定
      创建纹理输出绑定描述
      Create 创建绑定
    }
    class TexturePlayableGraphExtensions {
      静态类 纹理图扩展
      提供创建纹理输出入口
      CreateTextureOutput 创建输出
    }
    class TextureMixerPlayable {
      结构体 纹理混合节点
      混合多路纹理输入
      Create 创建节点
      GetHandle 获取句柄
    }
    class MaterialEffectPlayable {
      结构体 材质效果节点
      在图中驱动材质效果
      Create 创建节点
      GetHandle 获取句柄
    }
    class CameraPlayable {
      结构体 相机节点
      在图中驱动相机输出
      Create 创建节点
      GetHandle 获取句柄
    }
  }

  %% 编辑器命名空间
  namespace UnityEditorPlayables {
    class PlayablesEditorBindings {
      类 编辑器绑定
      编辑器侧Playables相关入口
      GetPlayableType 获取类型
    }
    class PlayableOutputEditorExtensions {
      类 编辑器输出扩展
      编辑器中扩展输出端工具
      GetReferenceObject 获取引用对象
    }
  }

  %% 关系 Playables 与外部类型
  IPlayable <|.. AnimationClipPlayable : 实现
  IPlayable <|.. AnimationMixerPlayable : 实现
  IPlayable <|.. AnimationLayerMixerPlayable : 实现
  IPlayable <|.. AnimationOffsetPlayable : 实现
  IPlayable <|.. AnimationPosePlayable : 实现
  IPlayable <|.. AnimationRemoveScalePlayable : 实现
  IPlayable <|.. AnimationMotionXToDeltaPlayable : 实现
  IPlayable <|.. AnimationScriptPlayable : 实现
  IPlayable <|.. AnimatorControllerPlayable : 实现
  IPlayableOutput <|.. AnimationPlayableOutput : 实现

  IPlayable <|.. AudioClipPlayable : 实现
  IPlayable <|.. AudioMixerPlayable : 实现
  IPlayableOutput <|.. AudioPlayableOutput : 实现

  IPlayable <|.. VideoClipPlayable : 实现

  IPlayableOutput <|.. TexturePlayableOutput : 实现
  IPlayable <|.. TextureMixerPlayable : 实现
  IPlayable <|.. MaterialEffectPlayable : 实现
  IPlayable <|.. CameraPlayable : 实现

  AnimationPlayableBinding ..> PlayableBinding : 依赖 绑定结构
  AudioPlayableBinding ..> PlayableBinding : 依赖 绑定结构
  TexturePlayableBinding ..> PlayableBinding : 依赖 绑定结构

  AnimationPlayableGraphExtensions ..> PlayableGraph : 依赖 图
  AudioPlayableGraphExtensions ..> PlayableGraph : 依赖 图
  TexturePlayableGraphExtensions ..> PlayableGraph : 依赖 图

  PlayablesEditorBindings ..> PlayableGraph : 编辑器依赖
  PlayableOutputEditorExtensions ..> PlayableOutput : 编辑器依赖

  AudioClipPlayable ..> PlayState : 使用 播放状态
  VideoClipPlayable ..> PlayState : 使用 播放状态

使用Playables的类型说明

UnityEngine.Animations

  • AnimationClipPlayable:播放单个动画剪辑并作为图输入;实现 IPlayable,用 PlayableGraph 创建并以 PlayableHandle 驱动。
  • AnimationMixerPlayable:混合多个动画输入形成最终动画;实现 IPlayable,作为 Playable 节点参与图连接与权重混合。
  • AnimationLayerMixerPlayable:支持层级混合与遮罩;实现 IPlayable,依赖 PlayableGraph 的输入输出端口连接。
  • AnimationOffsetPlayable:对动画施加位移或旋转偏移;实现 IPlayable,通过 PlayableHandle 参与图时间与评估。
  • AnimationPosePlayable:输出固定姿势或过渡姿势;实现 IPlayable,作为图节点被连接到输出。
  • AnimationRemoveScalePlayable:去除缩放对动画的影响;实现 IPlayable,作为图节点处理输入。
  • AnimationMotionXToDeltaPlayable:将根运动转换为位移增量;实现 IPlayable,由 PlayableGraph 管理与评估。
  • AnimationScriptPlayable:绑定动画作业,实现自定义处理;实现 IPlayable,底层由 PlayableHandle 承载作业数据。
  • AnimatorControllerPlayable:用 AnimatorController 作为图节点驱动;实现 IPlayable,可被图连接并受 PlayableGraph 控制。
  • AnimationPlayableOutput:将动画图输出到 Animator;实现 IPlayableOutput,通过 PlayableOutputHandle 绑定源节点。
  • AnimationPlayableGraphExtensions / AnimationPlayableExtensions / AnimationPlayableOutputExtensions:动画图、节点、输出扩展入口;分别扩展 PlayableGraphIPlayableIPlayableOutput 的创建与操作能力。
  • AnimationPlayableBinding:创建动画输出绑定描述;基于 PlayableBinding 构造动画输出并关联到图。
  • IAnimationJobPlayable:动画作业可播放接口;定义与 PlayableHandle 兼容的作业接口,供 IPlayable 节点实现。

UnityEngine.Audio

  • AudioClipPlayable:播放音频剪辑,可设置延迟与时长;实现 IPlayable,由 PlayableGraph 创建并连接。
  • AudioMixerPlayable:混合多路音频输入;实现 IPlayable,作为图节点参与混音。
  • AudioPlayableOutput:将图输出连接到 AudioSource;实现 IPlayableOutput,通过 PlayableOutputHandle 绑定源节点。
  • AudioPlayableBinding / AudioPlayableGraphExtensions:音频输出绑定与图扩展入口;使用 PlayableBindingPlayableGraph 创建音频输出。

UnityEngine.Experimental.Video

  • VideoClipPlayable:播放视频剪辑并支持寻址控制;实现 IPlayable,使用 PlayableHandle 驱动播放与时间控制。

UnityEngine.Experimental.Playables

  • TexturePlayableOutput:把图输出连接到 RenderTexture;实现 IPlayableOutput,由 PlayableOutputHandle 绑定源节点。
  • TexturePlayableBinding / TexturePlayableGraphExtensions:纹理输出绑定与图扩展入口;基于 PlayableBindingPlayableGraph 创建纹理输出。
  • TextureMixerPlayable:混合多路纹理输入;实现 IPlayable,作为图节点参与混合。
  • MaterialEffectPlayable:驱动材质效果作为图节点;实现 IPlayable,由 PlayableGraph 管理与评估。
  • CameraPlayable:驱动相机作为图节点;实现 IPlayable,通过 PlayableHandle 参与播放图。

UnityEditor.Playables

  • PlayablesEditorBindings:编辑器侧 Playables 入口绑定;依赖 PlayableGraph 与 Playables 类型,用于编辑器查看与扩展。
  • PlayableOutputEditorExtensions:编辑器中的输出端工具扩展;围绕 PlayableOutput / IPlayableOutput 提供编辑器功能。

10.10 总结

  1. 运行状态都在 Handle 层,Playable / ScriptPlayable 只是壳;时间、状态、端口、权重都在 PlayableHandle / PlayableOutputHandle 上。
  2. 图的评估入口是 PlayableOutput,不是节点。没连输出的子树不会参与评估,遍历和写回都是输出端驱动的。
  3. ScriptPlayable 干的事就是把行为实例挂到句柄上:CreateSetScriptInstance,评估时引擎带 FrameData 回调 PrepareFrame / ProcessFrame
  4. 遍历和评估顺序由 PlayableOutput 决定,SetSourcePlayable(playable, port)port 别当直通输入索引用;Passthrough 下直通哪一路看的是输出在图里的顺序。
  5. C# 只负责搭图(建图、连节点、绑输出),真正调度和执行在 C++。资源层(PlayableAsset / PlayableBinding)管「播什么、往哪输出」,驱动层(PlayableDirector / PlayableGraph)管创建和评估。


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 785293209@qq.com

×

喜欢就点赞,疼爱就打赏