使用动画节点
在游戏开发中,动画是使角色和场景生动起来的重要元素。Dora SSR 引擎提供了一个强大的动画处理节点类——Playable。它是三种动画系统的基础类:
- Model:
- Dora SSR 引擎实现的骨骼动画系统。
- 动画模型通常由一个
.model
文件,一个.clip
文件和一个.png
文件组成。
- DragonBone:
- 开源的 DragonBones 动画系统。
- 动画模型通常由一个以
_ske.json
结尾的文件,一个以_tex.json
结尾的文件和一个以_tex.png
结尾的图片文 件组成。
- Spine:
- 著名的商业动画软件 Spine2D 的动画系统。
- 动画模型通常由一个
.json
(或是.skel
) 文件,一个.atlas
文件和一个.png
文件组成。
本教程将指导您如何在程序中使用 Playable 节点,涵盖从加载动画到控制播放的各个方面。
1. 创建 Playable 实例
要使用 Playable 节点,首先需要创建其实例。Playable 支持三种动画系统,通常使用的加载方式如下:
- Model 文件:
"model:"
前缀 + 不带后缀的模型文件路径。 - Spine 文件:
"spine:"
前缀 + 不带后缀的 Spine 文件路径。 - DragonBones 文件:
"bone:"
前缀 + 不带后缀的 DragonBones 文件路径。
1.1 示例:加载 Model 动画
- Lua
- Teal
- TypeScript
- YueScript
local Playable <const> = require("Playable")
-- 加载 Model 动画
local modelPath = "model:assets/character"
local character = Playable(modelPath)
if character then
character.position = Vec2(100, 200)
stage:addChild(character)
else
print("加载 Model 动画失败!")
end
local Playable <const> = require("Playable")
-- 加载 Model 动画
local modelPath = "model:assets/character"
local character = Playable(modelPath)
if not character is nil then
character.position = Vec2(100, 200)
stage:addChild(character)
else
print("加载 Model 动画失败!")
end
import { Playable, Vec2 } from "Dora";
// 加载 Model 动画
const modelPath = "model:assets/character";
const character = Playable(modelPath);
if (character) {
character.position = Vec2(100, 200);
stage.addChild(character);
} else {
print("加载 Model 动画失败!");
}
_ENV = Dora
// 加载 Model 动画
modelPath = "model:assets/character"
character = Playable modelPath
if character
with character
.position = Vec2 100, 200
\addTo stage
else
print "加载 Model 动画失败!"
1.2 示例:加载 Spine 动画
- Lua
- Teal
- TypeScript
- YueScript
local Playable <const> = require("Playable")
-- 加载 Spine 动画
local spinePath = "spine:assets/monster"
local monster = Playable(spinePath)
if monster then
monster.position = Vec2(300, 200)
stage:addChild(monster)
else
print("加载 Spine 动画失败!")
end
local Playable <const> = require("Playable")
-- 加载 Spine 动画
local spinePath = "spine:assets/monster"
local monster = Playable(spinePath)
if not monster is nil then
monster.position = Vec2(300, 200)
stage:addChild(monster)
else
print("加载 Spine 动画失败!")
end
import { Playable, Vec2 } from "Dora";
// 加载 Spine 动画
const spinePath = "spine:assets/monster";
const monster = Playable(spinePath);
if (monster) {
monster.position = Vec2(300, 200);
stage.addChild(monster);
} else {
print("加载 Spine 动画失败!");
}
_ENV = Dora
// 加载 Spine 动画
spinePath = "spine:assets/monster"
monster = Playable spinePath
if monster
with monster
.position = Vec2 300, 200
\addTo stage
else
print "加载 Spine 动画失败!"
1.3 示例:加载 DragonBones 动画
- Lua
- Teal
- TypeScript
- YueScript
local Playable <const> = require("Playable")
-- 加载 DragonBones 动画
local dragonBonePath = "bone:assets/dragon"
local dragon = Playable(dragonBonePath)
if dragon then
dragon.position = Vec2(500, 200)
stage:addChild(dragon)
else
print("加载 DragonBones 动画失败!")
end
local Playable <const> = require("Playable")
-- 加载 DragonBones 动画
local dragonBonePath = "bone:assets/dragon"
local dragon = Playable(dragonBonePath)
if not dragon is nil then
dragon.position = Vec2(500, 200)
stage:addChild(dragon)
else
print("加载 DragonBones 动画失败!")
end
import { Playable, Vec2 } from "Dora";
// 加载 DragonBones 动画
const dragonBonePath = "bone:assets/dragon";
const dragon = Playable(dragonBonePath);
if (dragon) {
dragon.position = Vec2(500, 200);
stage.addChild(dragon);
} else {
print("加载 DragonBones 动画失败!");
}
_ENV = Dora
// 加载 DragonBones 动画
dragonBonePath = "bone:assets/dragon"
dragon = Playable dragonBonePath
if dragon
with dragon
.position = Vec2 500, 200
\addTo stage
else
print "加载 DragonBones 动画失败!"
1.4 示例:异步加载动画
在实际开发中,加载动画可能需要一些时间。您可以使用 Cache:loadAsync()
方法异步加载动画,加载完成后执行回调函数。
- Lua
- Teal
- TypeScript
- YueScript
local Playable <const> = require("Playable")
local thread <const> = require("thread")
-- 异步加载 Model 动画
local modelPath = "model:assets/character"
thread(function()
if Cache:loadAsync(modelPath) then
local character = Playable(modelPath)
character.position = Vec2(100, 200)
stage:addChild(character)
else
print("异步加载 Model 动画失败!")
end
end)
local Playable <const> = require("Playable")
local thread <const> = require("thread")
-- 异步加载 Model 动画
local modelPath = "model:assets/character"
thread(function()
if Cache:loadAsync(modelPath) then
local character = Playable(modelPath)
if not character is nil then
character.position = Vec2(100, 200)
stage:addChild(character)
end
else
print("异步加载 Model 动画失败!")
end
end)
import { Playable, Vec2, Cache, thread } from "Dora";
// 异步加载 Model 动画
const modelPath = "model:assets/character";
thread(() => {
if (Cache.loadAsync(modelPath)) {
const character = Playable(modelPath);
if (character) {
character.position = Vec2(100, 200);
stage.addChild(character);
}
} else {
print("异步加载 Model 动画失败!");
}
});
_ENV = Dora
-- 异步加载 Model 动画
modelPath = "model:assets/character"
thread ->
if Cache\loadAsync modelPath
with Playable modelPath
.position = Vec2 100, 200
\addTo stage
else
print "异步加载 Model 动画失败!"
2. 播放动画
创建实例后,可以使用 play
方法播放指定的动画。
- Lua
- Teal
- TypeScript
- YueScript
-- 播放动画 "run",并循环播放
local duration = character:play("run", true)
-- 播放动画 "run",并循环播放
local duration = character:play("run", true)
// 播放动画 "run",并循环播放
const duration = character.play("run", true);
// 播放动画 "run",并循环播放
duration = character\play "run", true
- 参数说明:
name
:要播放的动画名称。loop
(可选):是否循环播放,默认为false
。
- 返回值:动画的持续时间(秒)。
3. 停止动画
使用 stop
方法可以停止当前正在播放的动画。
- Lua
- Teal
- TypeScript
- YueScript
-- 停止动画
character:stop()
-- 停止动画
character:stop()
// 停止动画
character.stop();
// 停止动画
character\stop()
4. 设置播放速度
通过调整 speed
属性,可以改变动画的播放速度。
- Lua
- Teal
- TypeScript
- YueScript
-- 将播放速度加倍
character.speed = 2.0
-- 将播放速度加倍
character.speed = 2.0
// 将播放速度加倍
character.speed = 2.0;
// 将播放速度加倍
character.speed = 2.0
- 注意:
speed
的默认值为1.0
。
5. 翻转动画
使用 fliped
属性可以水平翻转动画,常用于角色转向。
- Lua
- Teal
- TypeScript
- YueScript
-- 水平翻转
character.fliped = true
-- 水平翻转
character.fliped = true
// 水平翻转
character.fliped = true;
// 水平翻转
character.fliped = true
fliped
:true
表示翻转,false
表示正常。
6. 获取关键点坐标
getKey
方法用于获取模型上关键点的坐标,例如角色的手. 脚位 置。在 Model 动画系统中,关键点是模型上设置的特定点。在 DragonBone 中,关键点是骨骼的位置。在 Spine2D 中,关键点是顶点附件的位置。
- Lua
- Teal
- TypeScript
- YueScript
-- 获取角色右手的坐标
local handPosition = character:getKey("right_hand")
print("右手坐标:", handPosition.x, handPosition.y)
-- 获取角色右手的坐标
local handPosition = character:getKey("right_hand")
print("右手坐标:", handPosition.x, handPosition.y)
// 获取角色右手的坐标
const handPosition = character.getKey("right_hand");
print("右手坐标:", handPosition.x, handPosition.y);
-- 获取角色右手的坐标
handPosition = character\getKey "right_hand"
print "右手坐标:", handPosition.x, handPosition.y
- 参数:关键点名称(字符串)。
- 返回值:
Vec2
,表示关键点的坐标。
7. 使用插槽添加子节点
setSlot
方法允许在模型的特定插槽上添加子节点,例如为角色添加武器或装备。
- Lua
- Teal
- TypeScript
- YueScript
-- 创建武器精灵
local sword = Sprite("assets/sword.png")
-- 将武器添加到角色的 "right_hand" 插槽
character:setSlot("right_hand", sword)
-- 创建武器精灵
local sword = Sprite("assets/sword.png")
-- 将武器添加到角色的 "right_hand" 插槽
character:setSlot("right_hand", sword)
// 创建武器精灵
const sword = Sprite("assets/sword.png");
// 将武器添加到角色的 "right_hand" 插槽
character.setSlot("right_hand", sword);
-- 创建武器精灵
sword = Sprite "assets/sword.png"
-- 将武器添加到角色的 "right_hand" 插槽
character\setSlot "right_hand", sword
- 参数说明:
name
:插槽名称。item
:要添加的节点对象。
8. 获取插槽上的子节点
使用 getSlot
方法可以获取指定插槽上的子节点。
- Lua
- Teal
- TypeScript
- YueScript
-- 获取 "right_hand" 插槽上的节点
local equippedItem = character:getSlot("right_hand")
if equippedItem then
print("已装备物品:", equippedItem)
else
print("插槽为空")
end
-- 获取 "right_hand" 插槽上的节点
local equippedItem = character:getSlot("right_hand")
if not equippedItem is nil then
print("已装备物品:", equippedItem)
else
print("插槽为空")
end
// 获取 "right_hand" 插槽上的节点
const equippedItem = character.getSlot("right_hand");
if (equippedItem) {
print("已装备物品:", equippedItem);
} else {
print("插槽为空");
}
-- 获取 "right_hand" 插槽上的节点
equippedItem = character\getSlot "right_hand"
if equippedItem
print "已装备物品:", equippedItem
else
print "插槽为空"
- 返回值:
Node
对象或nil
。
9. 监听动画结束事件
可以使用 onAnimationEnd
方法注册一个回调函数,当动画播放结束时触发。
- Lua
- Teal
- TypeScript
- YueScript
-- 注册动画结束回调
character:onAnimationEnd(function(animationName, target)
print("动画结束:", animationName)
-- 可以在此处进行后续操作,例如切换动画
end)
-- 注册动画结束回调
character:onAnimationEnd(function(animationName: string, target: Playable.Type)
print("动画结束:", animationName)
-- 可以在此处进行后续操作,例如切换动画
end)
// 注册动画结束回调
character.onAnimationEnd((animationName, target) => {
print("动画结束:", animationName);
// 可以在此处进行后续操作,例如切换动画
});
-- 注册动画结束回调
character\onAnimationEnd (animationName, target) ->
print "动画结束:", animationName
-- 可以在此处进行后续操作,例如切换动画
- 参数说明:
callback
:回调函数,接受animationName
和target
两个参数。
10. 综合示例
以下是一个综合示例,演示如何创建角色,播放动画,添加装备,并处理动画结束事件。
- Lua
- Teal
- TypeScript
- YueScript
local Playable <const> = require("Playable")
local Sprite <const> = require("Sprite")
local Vec2 <const> = require("Vec2")
local sleep <const> = require("sleep")
-- 创建角色
local character = Playable("model:assets/hero.model")
character.position = Vec2(200, 300)
-- 设置属性
character.speed = 1.0
character.fliped = false
-- 播放待机动画
character:play("idle", true)
-- 创建武器并装备
local sword = Sprite("assets/sword.png")
character:setSlot("right_hand", sword)
-- 注册动画结束事件
character:onAnimationEnd(function(animationName, target)
if animationName == "attack" then
-- 攻击结束后返回待机状态
target:play("idle", true)
end
end)
-- 每隔3秒执行一次攻击
character:loop(function()
-- 播放攻击动画
character:play("attack")
sleep(3)
end)
local Playable <const> = require("Playable")
local Sprite <const> = require("Sprite")
local Vec2 <const> = require("Vec2")
local sleep <const> = require("sleep")
-- 创建角色
local character = Playable("model:assets/hero.model")
if not character is nil then
character.position = Vec2(200, 300)
-- 设置属性
character.speed = 1.0
character.fliped = false
-- 播放待机动画
character:play("idle", true)
-- 创建武器并装备
local sword = Sprite("assets/sword.png")
character:setSlot("right_hand", sword)
-- 注册动画结束事件
character:onAnimationEnd(function(animationName: string, target: Playable.Type)
if animationName == "attack" then
-- 攻击结束后返回待机状态
target:play("idle", true)
end
end)
-- 每隔3秒执行一次攻击
character:loop(function(): boolean
-- 播放攻击动画
character:play("attack")
sleep(3)
return false
end)
end
import { Playable, Sprite, Vec2, sleep } from "Dora";
// 创建角色
const character = Playable("model:assets/hero.model");
if (character) {
character.position = Vec2(200, 300);
// 设置属性
character.speed = 1.0;
character.fliped = false;
// 播放待机动画
character.play("idle", true);
// 创建武器并装备
const sword = Sprite("assets/sword.png");
character.setSlot("right_hand", sword);
// 注册动画结束事件
character.onAnimationEnd((animationName, target) => {
if (animationName === "attack") {
// 攻击结束后返回待机状态
target.play("idle", true);
}
});
// 每隔3秒执行一次攻击
character.loop(() => {
// 播放攻击动画
character.play("attack");
sleep(3);
return false;
});
}
_ENV = Dora
-- 创建角色
with Playable "model:assets/hero.model"
.position = Vec2 200, 300
-- 设置属性
.speed = 1.0
.fliped = false
-- 播放待机动画
\play "idle", true
-- 创建武器并装备
sword = Sprite "assets/sword.png"
\setSlot "right_hand", sword
-- 注册动画结束事件
\onAnimationEnd (animationName, target) ->
if animationName == "attack"
-- 攻击结束后返回待机状态
target\play "idle", true
-- 每隔3秒执行一次攻击
\loop ->
-- 播放攻击动画,不循环
\play "attack"
sleep 3
11. 结论
通过本教程,您已经学会了如何在 Dora SSR 引擎中使用 Playable 节点类来加载和控制各种动画模型。Playable 提供了丰富的接口,支持多种动画系统,使您能够轻松地在游戏中添加复杂的动画效果。
由于 Spine2D 是商业软件,使用其动画需要遵守相应的许可协议,具体请参考 Spine 官方网站。
希望本教程对您有所帮助,祝您在游戏开发中取得成功!