CocosCreator3.8研究笔记(二十五)CocosCreator 动画系统-2d骨骼动画spine

本文主要是介绍CocosCreator3.8研究笔记(二十五)CocosCreator 动画系统-2d骨骼动画spine,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!


大家都知道,在游戏中 一般用帧动画或者骨骼动画,实现 人物的行走、奔跑、攻击等动作。


帧动画,在上一篇已经做了介绍,感兴趣的朋友可以前往阅读: CocosCreator3.8研究笔记(二十四)CocosCreator 动画系统-动画编辑器实操-关键帧实现动态水印动画效果。


今天,我们主要介绍什么是骨骼动画?Spine 是什么,骨骼动画怎么制作的?怎么使用骨骼动画?

一、什么是骨骼动画?


骨骼动画是把角色的各部分身体部件图片绑定到一根根互相作用连接的“骨骼”上,通过控制这些骨骼的位置、旋转方向和放大缩小而生成的动画。


骨骼动画比帧动画要求更高的处理器性能,但它也有更多的优势:

  • 更少的美术资源: 骨骼动画的资源是一块块小的角色部件(比如:头、手、胳膊、腰等),美术再也不用提供每一帧完整的图片了,节省了资源大小。

  • 更小的体积: 帧动画需要提供每一帧图片。而骨骼动画只需要少量的图片资源,并把骨骼的动画数据保存在一个 json 文件里面(后文会提到),它所占用的空间非常小。

  • 更好的流畅性: 骨骼动画使用差值算法计算中间帧,这能让动画总是保持流畅的效果。

  • 装备附件: 图片绑定在骨骼上来实现动画。如果需要,可以方便的更换角色的装备满足不同的需求。甚至改变角色的样貌来达到动画重用的效果。

  • 不同动画可混合使用: 不同的骨骼动画可以被结合到一起。比如一个角色可以转动头部、射击并且同时也在走路。

  • 程序动画: 可以通过代码控制骨骼,比如可以实现跟随鼠标的射击,注视敌人,或者上坡时的身体前倾等效果。


二、Spine 是什么


Spine是一款针对游戏的2D骨骼动画编辑工具,它具有良好的UI设计和完整的功能,是一个比较成熟的骨骼动画编辑器。

Spine旨在提供更高效和简洁的工作流程,以创建游戏所需的动画。


使用Spine创建骨骼动画分为以下步骤:

(1)、在SETUP模式下,选中Images属性,导入所需图片资源所在文件夹。

​ 注意:路径名和资源名中不能出现中文,否则无法解析。

(2)、拖动Images下的图片到场景,对角色进行组装(把各个身体部位拼在一起),可通过Draw Order属性调整图片所在层的顺序。
(3)、创建骨骼,并绑定图片到骨骼上,要注意各骨骼的父子关系。
(4)、切换到ANIMATE模式,选中要“动”的骨骼,对其进行旋转、移动、缩放等操作,每次改动后要记得打关键帧。
(5)、在菜单栏找到Texture Packer项,对角色纹理进行打包,资源文件后缀为atlas(而非Cocos2d-x常用的plist)

​ 打包后将生成两个文件,即:png 和 atlas。

(6)、导出动画文件Json。


感兴趣的朋友,请查看官方网站教程:Spine快速入门教程。


三、Creator 编辑器中Spine 骨骼动画的使用


Creator 中的骨骼动画资源目前支持 JSON 和 二进制 两种数据格式。

Creator 中创建骨骼动画我们需要使用 Spine Skeleton 组件。

Spine Skeleton 组件支持 Spine 官方工具导出的数据格式,并对 Spine(骨骼动画)资源进行渲染和播放。


1、Spine Skeleton 组件属性说明

属性功能说明
CustomMaterial自定义材质,可用于实现溶解、外发光等渲染效果。
Color设置骨骼动画颜色
SkeletonData骨骼信息数据,拖拽 Spine 导出后的骨骼资源到该属性中
Default Skin选择默认的皮肤
Animation当前播放的动画名称
Animation Cache Mode渲染模式,包括 REALTIME(默认)、SHARED_CACHEPRIVATE_CACHE 三种。 1. REALTIME 模式,实时运算,支持 Spine 所有的功能。 2. SHARED_CACHE 模式,将骨骼动画及贴图数据进行缓存并共享,相当于预烘焙骨骼动画。拥有较高性能,但不支持动作融合和动作叠加,只支持动作开始和结束事件。至于内存方面,当创建 N(N>=3)个相同骨骼、相同动作的动画时,会呈现内存优势。N 值越大,优势越明显。综上 SHARED_CACHE 模式适用于场景动画、特效、副本怪物、NPC 等,能极大提高帧率和降低内存。 3. PRIVATE_CACHE 模式,与 SHARED_CACHE 类似,但不共享动画及贴图数据,且会占用额外的内存,仅存在性能优势,如果大量使用该模式播放动画可能会造成卡顿。若想利用缓存模式的高性能,但又存在换装需求(不能共享贴图数据)时,那么 PRIVATE_CACHE 就适合你。
Loop是否循环播放当前动画
PremultipliedAlpha图片是否启用贴图预乘,默认为 True。 当图片的透明区域出现色块时需要关闭该项,当图片的半透明区域颜色变黑时需要启用该项。
TimeScale当前骨骼中所有动画的时间缩放率
DebugSlots是否显示 Slot 的 Debug 信息
DebugBones是否显示骨骼的 Debug 信息
DebugMesh是否显示 Mesh 的 Debug 信息
UseTint是否开启染色效果,默认关闭。
Sockets用于将某些外部节点挂到指定的骨骼关节上,属性的值表示挂点的数量。
Enable Bacth是否开启 Spine 合批

2、导入骨骼动画资源

骨骼动画所需资源有:

  • .json/.skel 骨骼数据
  • .png 图集纹理
  • .txt/.atlas 图集数据

如图,这是一个飞机的骨骼动画资源:

在这里插入图片描述


我们将其导入cocos creator 资源管理器中:

在这里插入图片描述


3、创建骨骼动画


骨骼动画创建步骤:

(1)、为节点添加 Spine Skeleton 组件

层级管理器 中选中需要添加 Spine Skeleton 组件的节点**,然后点击 **属性检查器下方的 添加组件 -> Spine -> Skeleton 按钮,即可添加 Skeleton 组件到节点上。


在这里插入图片描述


在这里插入图片描述


(2)、从 资源管理器 中将骨骼动画资源拖动到 属性检查器 Spine 组件的 SkeletonData 属性中。


注意:要拖动带动作的json文件,如图:

在这里插入图片描述


(3)、在属性检查器中,根据实际情况设置animation 、animation cache 以及 loop 等属性

在这里插入图片描述


注意

(1)、当使用 Spine Skeleton 组件时,属性检查器 中 Node 组件上的 AnchorSize 属性是无效的。

(2)、Spine Skeleton 组件属于 UI 渲染组件,而 Canvas 节点是 UI 渲染的 渲染根节点,所以带有该组件的节点必须是 Canvas 节点(或者是带有 RenderRoot2D 组件的节点)的子节点才能在场景中正常显示。

(3)、当使用 Spine Skeleton 组件时,由于拥有 UseTint 属性,所以其自定义材质需要有两个颜色信息,否则 Spine 的染色效果可能会出错。


四、代码中Spine 骨骼动画的使用


1、从服务器远程加载文本格式的 Spine 资源

let comp = this.getComponent('sp.Skeleton') as sp.Skeleton;let image = "http://localhost/download/spineres/test/test.png";
let ske = "http://localhost/download/spineres/test/test.json";
let atlas = "http://localhost/download/spineres/test/test.atlas";
assetManager.loadAny([{ url: atlas, ext: '.txt' }, { url: ske, ext: '.txt' }], (error, assets) => {assetManager.loadRemote(image, (error, texture: Texture2D) => {let asset = new sp.SkeletonData();asset.skeletonJson = assets[1];asset.atlasText = assets[0];asset.textures = [texture];asset.textureNames = ['test.png'];skeleton.skeletonData = asset;});
});

2、从服务器远程加载二进制格式的 Spine 资源

let comp = this.getComponent('sp.Skeleton') as sp.Skeleton;let image = "http://localhost/download/spineres/1/test.png";
let ske = "http://localhost/download/spineres/1/test.skel";
let atlas = "http://localhost/download/spineres/1/test.atlas";
assetManager.loadAny([{ url: atlas, ext: '.txt' }, { url: ske, ext: '.bin' }], (error, assets) => {assetManager.loadRemote(image, (error, texture: Texture2D) => {let asset = new sp.SkeletonData();asset._nativeAsset = assets[1];asset.atlasText = assets[0];asset.textures = [texture];asset.textureNames = ['test.png'];asset._uuid = ske; // 可以传入任意字符串,但不能为空asset._nativeURL = ske; // 传入一个二进制路径用作 initSkeleton 时的 filePath 参数使用comp.skeletonData = asset;let ani = comp.setAnimation(0, 'walk', true);});
});

3、加载本地 Spine 资源

import { _decorator, Component, Node, loader, sp, Label } from 'cc';
const { ccclass, property } = _decorator;@ccclass('LoadSpine')
export class LoadSpine extends Component {@property({type:Label})tips:Label| null = null;isLoadedRes = false;start () {// Your initialization goes here.loader.loadRes("spine/alien/alien-pro", sp.SkeletonData, (err, spineAsset)=> {if(err) {this.tips!.string = "Failed to load asset";this.isLoadedRes = true; // AutoTest: Consider loading complete even if loading failedreturn;}let comp = this.getComponent('sp.Skeleton') as sp.Skeleton;comp.skeletonData = spineAsset!;let ani = comp.setAnimation(0, 'run', true);this.tips!.string = 'Load Success';this.isLoadedRes = true;});}// update (deltaTime: number) {//     // Your update function goes here.// }
}

4、动作控制、属性设置、事件监听

import { _decorator, CCClass, Component, sp } from "cc";
const { ccclass, property } = _decorator;@ccclass('SpineCtrl')
export default class SpineCtrl extends Component{mixTime:number= 0.2;private spine?: sp.Skeleton;private _hasStop = true;onLoad () {var spine = this.spine = this.getComponent('sp.Skeleton') as sp.Skeleton;this._setMix('walk', 'run');this._setMix('run', 'jump');this._setMix('walk', 'jump');spine.setStartListener(trackEntry => {var animationName = trackEntry.animation ? trackEntry.animation.name : "";console.log("[track %s][animation %s] start.", trackEntry.trackIndex, animationName);});spine.setInterruptListener(trackEntry => {var animationName = trackEntry.animation ? trackEntry.animation.name : "";console.log("[track %s][animation %s] interrupt.", trackEntry.trackIndex, animationName);});spine.setEndListener(trackEntry => {var animationName = trackEntry.animation ? trackEntry.animation.name : "";console.log("[track %s][animation %s] end.", trackEntry.trackIndex, animationName);});spine.setDisposeListener(trackEntry => {var animationName = trackEntry.animation ? trackEntry.animation.name : "";console.log("[track %s][animation %s] will be disposed.", trackEntry.trackIndex, animationName);});spine.setCompleteListener((trackEntry) => {var animationName = trackEntry.animation ? trackEntry.animation.name : "";if (animationName === 'shoot') {this.spine!.clearTrack(1);}var loopCount = Math.floor(trackEntry.trackTime / trackEntry.animationEnd);console.log("[track %s][animation %s] complete: %s", trackEntry.trackIndex, animationName, loopCount);});spine.setEventListener(((trackEntry:any, event:any) => {var animationName = trackEntry.animation ? trackEntry.animation.name : "";console.log("[track %s][animation %s] event: %s, %s, %s, %s", trackEntry.trackIndex, animationName, event.data.name, event.intValue, event.floatValue, event.stringValue);}) as any);this._hasStop = false;}// OPTIONStoggleDebugSlots () {this.spine!.debugSlots = !this.spine?.debugSlots;}toggleDebugBones () {this.spine!.debugBones = !this.spine?.debugBones;}toggleDebugMesh () {this.spine!.debugMesh = !this.spine?.debugMesh;}toggleUseTint () {this.spine!.useTint = !this.spine?.useTint;}toggleTimeScale () {if (this.spine!.timeScale === 1.0) {this.spine!.timeScale = 0.3;}else {this.spine!.timeScale = 1.0;}}// ANIMATIONSstop () {this.spine?.clearTrack(0);this._hasStop = true;}walk () {if (this._hasStop) {this.spine?.setToSetupPose();}this.spine?.setAnimation(0, 'walk', true);this._hasStop = false;}run () {if (this._hasStop) {this.spine?.setToSetupPose();}this.spine?.setAnimation(0, 'run', true);this._hasStop = false;}jump () {if (this._hasStop) {this.spine?.setToSetupPose();}this.spine?.setAnimation(0, 'jump', true);this._hasStop = false;}shoot () {this.spine?.setAnimation(1, 'shoot', false);}idle () {this.spine?.setToSetupPose();this.spine?.setAnimation(0, 'idle', true);}portal () {this.spine?.setToSetupPose();this.spine?.setAnimation(0, 'portal', false);}//_setMix (anim1: string, anim2: string) {this.spine?.setMix(anim1, anim2, this.mixTime);this.spine?.setMix(anim2, anim1, this.mixTime);}
}

这篇关于CocosCreator3.8研究笔记(二十五)CocosCreator 动画系统-2d骨骼动画spine的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/3244

相关文章

Python学习笔记之getattr和hasattr用法示例详解

《Python学习笔记之getattr和hasattr用法示例详解》在Python中,hasattr()、getattr()和setattr()是一组内置函数,用于对对象的属性进行操作和查询,这篇文章... 目录1.getattr用法详解1.1 基本作用1.2 示例1.3 原理2.hasattr用法详解2.

Linux查询服务器系统版本号的多种方法

《Linux查询服务器系统版本号的多种方法》在Linux系统管理和维护工作中,了解当前操作系统的版本信息是最基础也是最重要的操作之一,系统版本不仅关系到软件兼容性、安全更新策略,还直接影响到故障排查和... 目录一、引言:系统版本查询的重要性二、基础命令解析:cat /etc/Centos-release详

更改linux系统的默认Python版本方式

《更改linux系统的默认Python版本方式》通过删除原Python软链接并创建指向python3.6的新链接,可切换系统默认Python版本,需注意版本冲突、环境混乱及维护问题,建议使用pyenv... 目录更改系统的默认python版本软链接软链接的特点创建软链接的命令使用场景注意事项总结更改系统的默

在Linux系统上连接GitHub的方法步骤(适用2025年)

《在Linux系统上连接GitHub的方法步骤(适用2025年)》在2025年,使用Linux系统连接GitHub的推荐方式是通过SSH(SecureShell)协议进行身份验证,这种方式不仅安全,还... 目录步骤一:检查并安装 Git步骤二:生成 SSH 密钥步骤三:将 SSH 公钥添加到 github

Linux系统中查询JDK安装目录的几种常用方法

《Linux系统中查询JDK安装目录的几种常用方法》:本文主要介绍Linux系统中查询JDK安装目录的几种常用方法,方法分别是通过update-alternatives、Java命令、环境变量及目... 目录方法 1:通过update-alternatives查询(推荐)方法 2:检查所有已安装的 JDK方

Linux系统之lvcreate命令使用解读

《Linux系统之lvcreate命令使用解读》lvcreate是LVM中创建逻辑卷的核心命令,支持线性、条带化、RAID、镜像、快照、瘦池和缓存池等多种类型,实现灵活存储资源管理,需注意空间分配、R... 目录lvcreate命令详解一、命令概述二、语法格式三、核心功能四、选项详解五、使用示例1. 创建逻

使用Python构建一个高效的日志处理系统

《使用Python构建一个高效的日志处理系统》这篇文章主要为大家详细讲解了如何使用Python开发一个专业的日志分析工具,能够自动化处理、分析和可视化各类日志文件,大幅提升运维效率,需要的可以了解下... 目录环境准备工具功能概述完整代码实现代码深度解析1. 类设计与初始化2. 日志解析核心逻辑3. 文件处

golang程序打包成脚本部署到Linux系统方式

《golang程序打包成脚本部署到Linux系统方式》Golang程序通过本地编译(设置GOOS为linux生成无后缀二进制文件),上传至Linux服务器后赋权执行,使用nohup命令实现后台运行,完... 目录本地编译golang程序上传Golang二进制文件到linux服务器总结本地编译Golang程序

Linux系统性能检测命令详解

《Linux系统性能检测命令详解》本文介绍了Linux系统常用的监控命令(如top、vmstat、iostat、htop等)及其参数功能,涵盖进程状态、内存使用、磁盘I/O、系统负载等多维度资源监控,... 目录toppsuptimevmstatIOStatiotopslabtophtopdstatnmon

linux重启命令有哪些? 7个实用的Linux系统重启命令汇总

《linux重启命令有哪些?7个实用的Linux系统重启命令汇总》Linux系统提供了多种重启命令,常用的包括shutdown-r、reboot、init6等,不同命令适用于不同场景,本文将详细... 在管理和维护 linux 服务器时,完成系统更新、故障排查或日常维护后,重启系统往往是必不可少的步骤。本文