Native SpriteAnimation
SpriteAnimation 以固定 tick 播放 atlas 帧,控制同一个 actor 上指定的
SpriteRenderer。可运行示例见 sprite_animation fixture。
它提供片段播放、帧事件和完成事件;本轮没有 blend tree、骨骼动画、浮点播放速率或动画资源编辑器。
场景和资源
{
"uid": 1,
"name": "player",
"components": {
"transform": {"type": "Transform"},
"sprite": {"type": "SpriteRenderer", "sprite": "base.png"},
"anim": {
"type": "SpriteAnimation",
"animation_resource": "hero.animation.json",
"renderer_key": "sprite",
"clip": "walk"
}
}
}
资源位于 animations/hero.animation.json:
{
"version": 1,
"image": "characters/hero.png",
"columns": 2,
"rows": 1,
"tile_width_px": 16,
"tile_height_px": 16,
"clips": [
{"name": "walk", "loop": true, "frames": [
{"row": 1, "column": 1, "ticks": 2},
{"row": 1, "column": 2, "ticks": 2, "event": "footstep"}
]}
]
}
image 精确相对于项目 images/,上例实际图片必须为 32×16 px。
资源路径必须以 .animation.json 结尾;资源及图片均不做 basename 搜索,
不允许绝对路径、..、反斜杠或越出各自根目录的符号链接。
图片尺寸必须恰好等于列数 × 单帧宽、行数 × 单帧高。
clip 可以用可选的 sheet 指定自己的图表(例如 Unity Resources 下每个动作一张表):
{"name": "run", "loop": true, "sheet": {"image": "hero/Run.png", "columns": 8, "rows": 3}, "frames": [...]}。
它沿用资源的 tile_width_px/tile_height_px,图片尺寸必须恰好匹配,帧的行列落在该图表内;
未指定 sheet 的 clip 使用顶层 image。
所有示例字段都必填,只有 frame 的 event 与 clip 的 sheet 可省略。格式拒绝未知或重复字段、
重名 clip、错误类型、非法 UTF-8 和越界行列。行列从 1 开始;每帧 ticks
为 1..36000 的整数。资源最多 4 MiB、256 个非空 clip、总共 65,536 帧;
clip 名称和显式 event 为 1..128 个 UTF-8 字节,区分大小写。
省略 event 表示无 marker,显式空字符串不合法。
每个 renderer 最多由一个配置了资源的 animator 控制。场景加载会在清空旧世界前
校验资源、renderer key 和初始 clip。空 animation_resource 是不播放的作者占位组件;
空 clip 表示初始停止,不会默认选择首个 clip。编辑器清空资源会清除动画姿态;
无效属性修改保留原有有效配置。Lua 通过播放 API 切换状态,不直接改作者资源字段。
Lua API
local animation = self.actor:GetComponentByKey("anim")
animation:Play("walk")
| 方法 | 行为 |
|---|---|
Play(name) |
选择片段并显示第一帧,切换成功返回 true。已选同名片段返回 false,即使暂停或已完成也不重播。 |
Restart(name) |
显式从片段第一帧重新播放。 |
Pause() / Resume() |
暂停或继续当前片段;暂停保留姿态及待发事件。 |
Stop() |
清空当前播放和事件,恢复 SpriteRenderer 的作者姿态。 |
未知 clip 名称报错,保留已有播放状态。没有资源或组件已失效时,Play / Restart
同样报错。玩法脚本不要手工调用注册的 OnStart / OnDestroy 生命周期入口;
移除组件用 actor:RemoveComponent(animation),销毁 actor 用 Actor.Destroy(actor)。
| 属性 | 访问与含义 |
|---|---|
animation_resource, renderer_key, clip |
Lua 只读作者配置;clip 是初始配置,不随播放切换。 |
enabled |
可读写;禁用冻结时间和事件,保留当前姿态。 |
current_clip |
只读当前选中名称;停止时为空。 |
frame |
只读片段内帧编号,从 1 开始;停止时为 0。 |
elapsed_ticks |
只读当前帧已消耗的 tick,换帧后归零,不是整个片段的累计时长。 |
playing |
只读;有效、启用、已选中且未暂停或完成时为 true。 |
paused, completed |
只读暂停和完成状态。 |
actor, key |
引擎管理的所属 actor handle 与组件 key,玩法脚本不要重新赋值。 |
初始配置在 frame 0 就显示首帧。动画推进发生在 pending OnStart 后、普通
OnUpdate 前;在 tick T 的 OnUpdate 调用 Play,会立即显示第一帧,T+1 才消耗
首个 tick。每帧保持其整数 duration,循环片段回到第一帧;单次片段在最后一帧
duration 耗尽后仅完成一次,并保持最后姿态。冻结编辑器预览不推进时间或发送事件。
动画姿态是临时 renderer override,不改写作者 sprite、行列或
.pocketengine/spritesheets.json。它保留 tint、pivot、scale/mirroring、排序与 transform。
Stop、移除、销毁和 reset 清理其拥有的 override;运行中切换片段不写回资源文件。
动画事件
Footsteps = {
OnAnimationEvent = function(self, event)
if event.type == "frame" and event.marker == "footstep" then
-- 在这里调用游戏自己的音效或脚步逻辑。
elseif event.type == "completed" and event.clip == "attack" then
self.actor:GetComponentByKey(event.component_key):Play("walk")
end
end
}
每次进入帧都会产生 type="frame",即使 marker 为空。单次片段结束产生
type="completed"。payload 包含 type、clip、frame、marker 和
component_key;frame 是片段内编号,不是 atlas 列号。完成事件 marker 为空。
Play 的首帧事件在后续动画推进阶段派发。
事件按组件 key 顺序发送给所属 actor 上已启动、启用的 Lua 组件,在普通 OnUpdate 之前执行。接收者和动画身份会在可执行 getter 之后重新校验:在 callback 或 getter 中 Stop、Restart、移除动画或销毁 actor,会取消剩余旧播放事件;新播放事件留给后续 tick。新增组件不会进入当前接收者快照。Lua getter 和 callback 错误走正常结构化 runtime error 路径,不应使 native worker 崩溃。
测试包括 资源校验、 原生播放、 像素读回、 agent 时序和重放及 getter 重入。最终集成矩阵以 development status 为准。