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