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是评估阶段生成的帧上下文(Play或Evaluate都会产生),传入PrepareFrame/ProcessFrame;FrameRate负责时间基准与帧率换算。 - 驱动与图层:
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作为事件标识,适合简单通知场景。PlayableExtensions:IPlayable的扩展工具类,封装播放、暂停、设置时间、连接输入等常用操作,简化 API 使用。PlayableOutputExtensions:IPlayableOutput的扩展工具类,封装源节点绑定、权重设置、通知推送等常用操作,同时保留旧接口兼容。ScriptPlayableBinding:创建脚本输出绑定的工具类,用于把脚本输出类型注册到图的绑定系统中。PlayableDirector:驱动组件,负责管理PlayableAsset与PlayableGraph的创建、播放、暂停与停止,通常挂在 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;Playable的GetHandle()只是回传句柄;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 写到这里)
PlayableOutputHandle 与 PlayableOutput 的关系:
PlayableOutput持有PlayableOutputHandle;PlayableOutput.SetSourcePlayable()/SetWeight()直接转发到句柄;- 具体输出类型(动画/音频/纹理)在自身 SetTarget 时,最终写到 OutputHandle 的引用对象。
PlayableOutputHandle 与 PlayableHandle 的关系:
PlayableOutputHandle用SetSourcePlayable(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。 - 节点操作:
PlayableExtensions的Play()/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,而是具体类型各自提供(如AnimationClipPlayable、AudioClipPlayable、VideoClipPlayable等),命名也可能是GetAnimationClip/SetAnimationClip、GetCamera等。 - Playable 是
struct,具体类型(如AnimationClipPlayable、ScriptPlayable)靠显/隐式转换统一到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 模式里可以理解成:
一个中间节点有 input0、input1 两路输入,你同时创建了 output0、output1 两个输出口;
当 output0 发起评估就只取 input0,切到 output1 就只取 input1,这就是“输出口序号对应输入口序号”的含义。
Passthrough 时只走一条输入
中间节点是 Passthrough,输出口一旦发起评估,就只会取某一个 input 的数据,其它输入不参与。
切换输入口的方式
这里“切换输入口”不是去改节点的 input,而是切换哪个输出口在驱动评估。
同一张图里创建多个PlayableOutput,或调整输出的创建顺序/启用顺序,就会改变“谁来发起遍历”。
评估入口一变,Passthrough 直通的输入口自然随之变化。
Mix 会把多路输入一起算
切到 Mix 后,多条输入都会参与,权重决定最终合成结果。
源码注释的原意
注释明确写的是“输出连接到第 n 个 input 时,只传播第 n 个输入”。
也就是说设计意图是:sourceOutputPort ↔ input 端口序号一一对应。
实测现象:更像“按输出顺序取输入”
从可视化结果看,直通的输入口与 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();
}
- 权重查找逻辑里的佐证
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/ProcessFrame → Stop/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 数据层
数据层的两个关键结构是 FrameData 和 FrameRate。
前者承载评估结果,后者提供时间基准;都来自评估流程,但用途并不相同。
FrameData:评估快照
FrameData 是评估阶段生成的帧快照,不用于长期保存。
当图被评估(Play 或 Evaluate),C++ 侧生成并填充 FrameData,再交给回调使用。
它承载的内容:
- 一帧的上下文快照:
deltaTime / weight / effectiveWeight / effectiveSpeed / flags等。 - 评估类型与状态由 flags 推导:
evaluationType、seekOccurred、timeLooped、timeHeld。 output记录本次评估的输出端,可用来区分输出来源。
它从哪里来:
- 由引擎评估流程在 C++ 侧生成并填充,然后注入到
IPlayableBehaviour的回调里。 OnBehaviourPlay/Pause、PrepareFrame、ProcessFrame中拿到的都是这次评估快照。
读源码时的关注点:
FrameData是struct,字段多为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等)。 rate与dropFrame用于表示实际帧率与掉帧模式。- 比较与格式化接口,便于时间换算与显示。
classDiagram
direction LR
class FrameRate {
结构体 帧率基准
m_Rate 内部帧率
rate 实际帧率
dropFrame 是否掉帧
IsValid 是否有效
}
class PlayableGraph {
结构体 播放图
m_Handle 指针
m_Version 版本
}
PlayableGraph ..> FrameRate : 依赖时间基准
10.7 驱动与图层
这一层只看两件事:谁驱动图,以及图如何创建/连接/评估/销毁。核心类型是 PlayableDirector 和 PlayableGraph。
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 做了什么
- 读取
PlayableAsset,触发CreatePlayable构建根节点。 - 资产返回的节点树交给
PlayableGraph管理,节点间连接落到PlayableHandle。 - 遍历
PlayableAsset.outputs,按PlayableBinding.CreateOutput创建输出端,并SetSourcePlayable绑定根节点。 - 输出目标的绑定值来自
IExposedPropertyTable(也就是 Director 的绑定表)。 - 最终生成一个可评估的 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 资源层
资源层只关心两件事:播什么、往哪里输出。对应的核心类型就是 PlayableAsset 和 PlayableBinding。
PlayableAsset:播什么
定位:资源入口,负责描述“这一段播放内容是什么”,并提供 CreatePlayable 构建节点树。
方法脉络:CreatePlayable 是关键入口;outputs 返回一组 PlayableBinding;duration 默认走 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:动画图、节点、输出扩展入口;分别扩展PlayableGraph、IPlayable、IPlayableOutput的创建与操作能力。AnimationPlayableBinding:创建动画输出绑定描述;基于PlayableBinding构造动画输出并关联到图。IAnimationJobPlayable:动画作业可播放接口;定义与PlayableHandle兼容的作业接口,供IPlayable节点实现。
UnityEngine.Audio
AudioClipPlayable:播放音频剪辑,可设置延迟与时长;实现IPlayable,由PlayableGraph创建并连接。AudioMixerPlayable:混合多路音频输入;实现IPlayable,作为图节点参与混音。AudioPlayableOutput:将图输出连接到 AudioSource;实现IPlayableOutput,通过PlayableOutputHandle绑定源节点。AudioPlayableBinding / AudioPlayableGraphExtensions:音频输出绑定与图扩展入口;使用PlayableBinding与PlayableGraph创建音频输出。
UnityEngine.Experimental.Video
VideoClipPlayable:播放视频剪辑并支持寻址控制;实现IPlayable,使用PlayableHandle驱动播放与时间控制。
UnityEngine.Experimental.Playables
TexturePlayableOutput:把图输出连接到 RenderTexture;实现IPlayableOutput,由PlayableOutputHandle绑定源节点。TexturePlayableBinding / TexturePlayableGraphExtensions:纹理输出绑定与图扩展入口;基于PlayableBinding和PlayableGraph创建纹理输出。TextureMixerPlayable:混合多路纹理输入;实现IPlayable,作为图节点参与混合。MaterialEffectPlayable:驱动材质效果作为图节点;实现IPlayable,由PlayableGraph管理与评估。CameraPlayable:驱动相机作为图节点;实现IPlayable,通过PlayableHandle参与播放图。
UnityEditor.Playables
PlayablesEditorBindings:编辑器侧 Playables 入口绑定;依赖PlayableGraph与 Playables 类型,用于编辑器查看与扩展。PlayableOutputEditorExtensions:编辑器中的输出端工具扩展;围绕PlayableOutput/IPlayableOutput提供编辑器功能。
10.10 总结
- 运行状态都在 Handle 层,
Playable/ScriptPlayable只是壳;时间、状态、端口、权重都在PlayableHandle/PlayableOutputHandle上。 - 图的评估入口是
PlayableOutput,不是节点。没连输出的子树不会参与评估,遍历和写回都是输出端驱动的。 ScriptPlayable干的事就是把行为实例挂到句柄上:Create里SetScriptInstance,评估时引擎带FrameData回调PrepareFrame/ProcessFrame。- 遍历和评估顺序由
PlayableOutput决定,SetSourcePlayable(playable, port)的port别当直通输入索引用;Passthrough 下直通哪一路看的是输出在图里的顺序。 - C# 只负责搭图(建图、连节点、绑输出),真正调度和执行在 C++。资源层(
PlayableAsset/PlayableBinding)管「播什么、往哪输出」,驱动层(PlayableDirector/PlayableGraph)管创建和评估。
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 785293209@qq.com