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);}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/148688.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

linux入门---信号的保存和捕捉

目录标题 信号的一些概念信号的保存pending表block表handler表 信号的捕捉内核态和用户态信号的捕捉 信号的一些概念 1.进程会收到各种各样的信号,那么程序对该信号进行实际处理的动作叫做信号的递达。 2.我们之前说过当进程收到信号的时候可能并不会立即处理这个信…

计算机考研 | 2016年 | 计算机组成原理真题

文章目录 【计算机组成原理2016年真题44题-9分】【第一步:信息提取】【第二步:具体解答】 【计算机组成原理2016年真题45题-14分】【第一步:信息提取】【第二步:具体解答】 【计算机组成原理2016年真题44题-9分】 假定CPU主频为5…

51单片机可调幅度频率波形信号发生器( proteus仿真+程序+原理图+报告+讲解视频)

51单片机可调幅度频率信号发生器( proteus仿真程序原理图报告讲解视频) 讲解视频1.主要功能:2.仿真3. 程序代码4. 原理图4. 设计报告5. 设计资料内容清单&&下载链接***[资料下载链接](https://docs.qq.com/doc/DS1daV1BKRXZMeE9u)*** 51单片机可…

软件工程与计算总结(三)示例项目描述

本节介绍一个标准的项目描述,大家可以作为蓝本学习~ 目录 一.背景 二.目标 三.系统用户 四.用户访谈要点 1.收银员 2.客户经理 3.总经理 4.系统管理员 五.项目实践过程 一.背景 A是一家刚刚发展起来的小型连锁商店,其前身是一家独立的小百货门面…

算法-数学-斜率-直线上最多的点数

算法-数学-斜率-直线上最多的点数 1 题目概述 1.1 题目出处 https://leetcode.cn/problems/max-points-on-a-line/ 1.2 题目描述 给你一个数组 points ,其中 points[i] [xi, yi] 表示 X-Y 平面上的一个点。求最多有多少个点在同一条直线上。 2 暴力搜索斜率…

虚拟机VMware的使用流程以及出现的问题附解决方法

虚拟机VMware的使用流程以及出现的问题附解决方法 下载安装 略。。。 创建虚拟机 虚拟机的设置如下:注意网络适配器为NAT 如果出现ip addr 命令:不显示IP地址的话: 解决方式如下: 首先设置网卡:先查看一下onboot是…

OpenCV读取图像时按照BGR的顺序HWC排列,PyTorch按照RGB的顺序CHW排列

OpenCV读取RGB图像 在OpenCV中,读取的图片默认是HWC格式,即按照高度、宽度和通道数的顺序排列图像尺寸的格式。我们看最后一个维度是C,因此最小颗粒度是C。 例如,一张形状为2562563的RGB图像,在OpenCV中读取后的格式…

【Java 进阶篇】JDBC 管理事务详解

在数据库操作中,事务是一个非常重要的概念。事务可以确保一系列的数据库操作要么全部成功执行,要么全部失败回滚,以保持数据库的一致性和完整性。在 Java 中,我们可以使用 JDBC 来管理事务。本文将详细介绍 JDBC 管理事务的方法和…

Leetcode1071. 字符串的最大公因子(三种方法,带详细解析)

Leetcode1071. 字符串的最大公因子 对于字符串 s 和 t,只有在 s t … t(t 自身连接 1 次或多次)时,我们才认定 “t 能除尽 s”。 给定两个字符串 str1 和 str2 。返回 最长字符串 x,要求满足 x 能除尽 str1 且 x 能…

【面试总结大纲】

面试 1. springSpring AOP的具体实现核心概念分别指的是什么?基于注解的切面实现主要包括以下几个步骤:两个切面,它们之间的顺序是怎么控制的 springmvc的工作流程设计模式原则Spring 框架中用到了哪些设计模式? 2. Java-锁2.1锁的分类可重入…

开发调试管理系统遇到的问题大全错误解决大全收集

问题大全错误解决大全 多模块项目依赖中,项目启动失败-org.yaml.snakeyaml.error.YAMLException: java.nio.charset.MalformedInputException报错:Error: The project seems to require yarn but it‘s not installednpm ERR! fatal: Could not read fro…

动态规划-状态机(188. 买卖股票的最佳时机 IV)

状态分类: f[i,j,0]考虑前i只股票,进行了j笔交易,目前未持有股票 所能获得最大利润 f[i,j,1]考虑前i只股票,进行了j笔交易,目前持有股票 所能获得最大利润 状态转移: f[i][j][0] Math.max(f[i-1][j][0],f[…

Linux高性能服务器编程 学习笔记 第十章 信号

信号是由用户、系统、进程发送给目标进程的信息,以通知目标进程某个状态的改变或系统异常。Linux信号可由以下条件产生: 1.对于前台进程,用户可通过输入特殊终端字符来给它发送信号,如输入CtrlC通常会给进程发送一个中断信号。 2…

视频讲解|基于DistFlow潮流的配电网故障重构代码

目录 1 主要内容 2 视频链接 1 主要内容 该视频为基于DistFlow潮流的配电网故障重构代码讲解内容,对应的资源下载链接为基于DistFlow潮流的配电网故障重构(输入任意线路),对该程序进行了详尽的讲解,基本做到句句分析和讲解(讲解…

双重差分模型(DID)论文写作指南与操作手册

手册链接:双重差分模型(DID)论文写作指南与操作手册https://www.cctalk.com/m/group/90983583?xh_fshareuid60953990 简介: 当前,对于准应届生们来说,毕设季叠加就业季,写作时间显得十分宝贵…

Polygon Miden zkRollup中的UTXO+账户混合状态模型

1. 引言 本文重点讨论Polygon Miden所设计的UTXO账户混合状态模型,以实现某些有趣的属性。 Miden的目标是:【即越具有隐私性,其可扩展性越好】 构建可扩展去中心化的rollup采用支持隐私的架构 Miden支持灵活的交易模式: 公开…

QT实现TCP服务器客户端的实现

ser: widget.cpp: #include "widget.h" #include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget) {ui->setupUi(this);//实例化一个服务器server new QTcpServer(this);// 此时&#xf…

软件设计师_计算机网络_学习笔记

文章目录 4.1 网路技术标准与协议4.1.1 协议4.1.2 DHCP4.1.3 DNS的两种查询方式 4.2 计算机网络的分类4.2.1 拓扑结构 4.3 网络规划与设计4.3.1 遵循的原则4.3.2 逻辑网络设计4.3.3 物理网络设计4.3.4 分层设计 4.4 IP地址与子网划分4.4.1 子网划分4.4.2 特殊IP 4.5 HTML4.6 无…

BL808学习日志-2-LVGL for M0 and D0

一、lvgl测试环境 对拿到的M1S_DOCK开发板进行开发板测试,博流的官方SDK是支持M0和D0两个内核都进行测试的;但是目前只实现了M0的LVGLBenchmark,测试D0内核中发现很多莫名其妙的问题。一会详细记录。 使用的是开发板自带的SPI显示屏&#xff…

STM32复习笔记(五):FSMC连接外部SRAM

目录 Preface: (一)原理相关 (二)CUBEMX配置 (三)轮询方式读写 (四)DMA方式读写 Preface: STM32F4有一个FSMC(Flexible Static Memory Contr…