跳转到内容

时间轴

时间轴系统驱动多轨道动画,在时间线上协调属性变化、Spine 播放、精灵动画、音频事件和实体激活。时间轴在编辑器的 Timeline 面板中创建,运行时通过 TimelinePlayer 组件和 TimelineControl API 播放。

教程:动画化一个精灵

我们来创建一个时间轴,让精灵从左移到右并淡出。

第 1 步:选择实体

在 Hierarchy 中点击你要动画化的实体。

第 2 步:打开 Timeline 面板

打开 ViewTimeline(或在面板菜单中找到它)。如果实体还没有 TimelinePlayer 组件,面板会显示 Create 按钮——点击它自动添加。

第 3 步:添加属性轨道

  1. 点击轨道列表中的 + 按钮
  2. 选择 PropertyTransformposition.x
  3. 新轨道出现,目标指向所选实体的 Transform.position.x

第 4 步:添加关键帧

  1. 点击顶部标尺将播放头移到时间 0.0
  2. 点击菱形图标(或按 K)添加关键帧——捕获当前值
  3. 将播放头移到 1.0 秒
  4. 在 Inspector 中把 position.x 改为 200
  5. 再按 K ——添加第二个关键帧,记录新值

第 5 步:添加第二条轨道

  1. 点击 +PropertySpritecolor.a
  2. 在时间 0.0 添加关键帧(值:1.0)
  3. 在时间 1.0 添加关键帧(值:0.0)

第 6 步:预览

Space 在编辑器中播放。精灵在 1 秒内向右移动同时淡出。

第 7 步:配置播放

在 Inspector 中设置 TimelinePlayer 属性:

  • wrapMode: loop 循环播放
  • speed: 0.5 半速播放

轨道目标定位

每条轨道都有一个目标实体。默认情况下,目标是拥有 TimelinePlayer 组件的实体。

动画化子实体

要动画化子实体,设置轨道的 Child Path —— 以 / 分隔的名称层级路径:

Arm/Hand → 找到子实体 "Arm",再找它的子实体 "Hand"
Body → 找到直接子实体 "Body"
(空) → 目标为根实体自身

在编辑器中,当你在选中子实体时添加轨道,child path 会自动设置。

轨道类型

Property(属性)

通过关键帧插值动画化任意数值组件属性。

内置属性(优化路径,在 C++ 中计算):

组件属性
Transformposition.x/y/z, scale.x/y/z, rotation
Spritecolor.r/g/b/a, opacity, size.x/y
UIRectoffsetMin.x/y, offsetMax.x/y, anchorMin.x/y, anchorMax.x/y, pivot.x/y
CameraorthoSize

自定义属性:其他任何数值组件字段都可以通过嵌套路径动画化(如 myComponent.health)。

关键帧插值

右键关键帧可选择插值模式:

模式行为
Hermite使用切线手柄的平滑三次曲线(默认)
Linear关键帧之间的直线
Step保持值直到下一个关键帧(无插值)
EaseIn缓慢开始,加速到下一个关键帧
EaseOut快速开始,减速到下一个关键帧
EaseInOut平滑的加速和减速

Hermite 模式下,拖拽曲线编辑器中的切线手柄可以调整插值曲线形状。

Spine

SpineAnimation 组件上触发 Spine 动画片段。每个关键帧指定一个在该时间播放的动画名称。

Sprite Animation(精灵动画)

播放精灵动画片段。每个关键帧指定一个在该时间开始的片段名称。

AnimFrames(帧动画)

用于精灵序列帧动画的可视化帧轨道。与精灵动画片段不同,每帧显示为时间轴上带颜色、可调整大小的色块

  • 每个色块引用一张纹理,拥有独立的时长(默认:1/12 秒)
  • 拖拽色块边缘调整帧时序
  • 目标实体必须有 Sprite 组件——每帧自动切换纹理

当你需要逐帧控制时序而不想创建单独的动画片段时,使用 AnimFrames。

Audio(音频)

在特定时间触发音频播放。每个关键帧指定一个要播放的音频资源。

Activation(激活)

在时间范围内切换实体可见性。实体在活动范围内启用,范围外禁用。

Marker(标记)

在特定时间放置命名标记。标记不影响播放——仅作为编辑器中的参考点和定位用途。

Custom Event(自定义事件)

在特定时间触发命名事件。在你的系统中监听这些事件,可以在动画的精确时刻执行自定义逻辑。

编辑器控制

快捷键

快捷键操作
Space播放 / 暂停
, / .后退 / 前进一帧
K在播放头位置添加关键帧
Delete删除选中的关键帧
Ctrl+Click切换关键帧选择
Shift+Click范围选择关键帧
框选拖拽橡皮筋选择多个关键帧

工具栏

  • 录制:切换自动关键帧模式——在 Inspector 中修改属性时自动在当前播放头位置记录关键帧
  • 速度:循环切换播放速度(0.25x、0.5x、1x、2x、4x)
  • 循环模式:循环切换 Once、Loop 和 PingPong
  • 吸附:切换拖拽关键帧时的网格吸附
  • 时长:双击时间显示可编辑总时长

多选与批量操作

选择多个关键帧(Ctrl+Click、Shift+Click 或框选),然后:

  • 拖拽一起移动
  • Delete 作为单次撤销步骤删除

轨道管理

  • 拖拽轨道重新排序
  • 右键轨道可重命名/删除
  • 每种轨道类型在侧边栏有不同颜色标识

实时播放头同步

进入 Play 模式时,时间轴自动切换到 LIVE 模式。播放头以 60fps 实时跟踪运行时动画位置,工具栏显示红色闪烁的”LIVE”指示器。LIVE 模式下编辑器播放控件被禁用,退出 Play 模式后恢复。

TimelinePlayer 组件

TimelinePlayer 添加到实体上即可在运行时播放时间轴资源。

属性类型默认值说明
timelinestring''.estl 时间轴资源路径
playingbooleanfalse设为 true 开始播放
speednumber1.0播放速度倍率
wrapModestring'once''once''loop''pingPong'

playing 设为 true 时开始播放。播放完成后(once 模式下),playing 自动恢复为 false

WrapMode

说明
once播放一次后停止
loop播放完毕后从头开始
pingPong正向和反向交替播放

TimelineControl API

在系统中使用 TimelineControl 进行运行时播放控制:

import { defineSystem, addSystem, Query, TimelinePlayer, TimelineControl } from 'esengine';
import { Res, Input } from 'esengine';
addSystem(defineSystem(
[Res(Input), Query(TimelinePlayer)],
(input, query) => {
for (const [entity] of query) {
if (input.isKeyPressed('Space')) {
TimelineControl.play(entity);
}
if (input.isKeyPressed('KeyP')) {
TimelineControl.pause(entity);
}
}
}
));

方法

方法返回值说明
TimelineControl.play(entity)void开始或恢复播放
TimelineControl.pause(entity)void暂停播放
TimelineControl.stop(entity)void停止并重置到开头
TimelineControl.setTime(entity, time)void跳转到指定时间(秒)
TimelineControl.isPlaying(entity)boolean检查是否正在播放
TimelineControl.getCurrentTime(entity)number获取当前播放时间(秒)

注册时间轴资源

在编辑器外加载时间轴(如自定义资源管线中)时,使用注册 API:

import { registerTimelineAsset, getTimelineHandle, parseTimelineAsset } from 'esengine';
const asset = parseTimelineAsset(jsonData);
registerTimelineAsset('timelines/intro.estl', asset);
const tl = getTimelineHandle('timelines/intro.estl');

示例:开场序列

import {
defineSystem, addStartupSystem, addSystem,
Query, Mut, TimelinePlayer, TimelineControl, Res, Input
} from 'esengine';
// 启动时播放开场时间轴
addStartupSystem(defineSystem(
[Query(Mut(TimelinePlayer))],
(query) => {
for (const [entity, player] of query) {
if (player.timeline === 'timelines/intro.estl') {
TimelineControl.play(entity);
}
}
}
));
// 按 Space 跳过
addSystem(defineSystem(
[Res(Input), Query(TimelinePlayer)],
(input, query) => {
if (input.isKeyPressed('Space')) {
for (const [entity] of query) {
if (TimelineControl.isPlaying(entity)) {
TimelineControl.stop(entity);
}
}
}
}
));

资源清理

实体被销毁时,附加在实体上的时间轴和补间资源会自动清理。引擎通过 onDespawn 调用 _tl_destroy 释放 C++ 时间轴对象,并调用 Tween.cancelAll() 取消活跃的补间动画。销毁实体时无需手动停止时间轴或取消补间。

下一步