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.rhkb.cn/news/148253.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C++_pen_重载(1)

普通运算符重载 C准许以运算符命名函数 string love "i";love " love";//(love, " love"); love " you";//(love, " you");cout<< "i love you";// <<(cout, "i love you");运算符分类 …

RabbitMQ安装与简单使用

安装 下载资源 可以访问官网查看下载信息rabbitmq官网 选择合适的版本&#xff0c;注意&#xff1a;rabbitmq需要下载一个Erlang才能使用 我自己是在一下两个连接中下载的 rabbitmq 3.8.8 erlang 21.3.8.15 需要下载其他版本的同学注意erlang版本是否匹配&#xff0c;可以访…

小谈设计模式(13)—外观模式

小谈设计模式&#xff08;13&#xff09;—外观模式 专栏介绍专栏地址专栏介绍 外观模式主要目的角色分析外观&#xff08;Facade&#xff09;角色子系统&#xff08;Subsystem&#xff09;角色客户端&#xff08;Client&#xff09;角色 工作原理核心思想总结简化接口解耦客户…

【多模态融合】TransFusion学习笔记(1)

工作上主要还是以纯lidar的算法开发,部署以及系统架构设计为主。对于多模态融合(这里主要是只指Lidar和Camer的融合)这方面研究甚少。最近借助和朋友们讨论论文的契机接触了一下这方面的知识&#xff0c;起步是晚了一点&#xff0c;但好歹是开了个头。下面就借助TransFusion论文…

GEO生信数据挖掘(五)提取临床信息构建分组,分组数据可视化(绘制层次聚类图,绘制PCA图)

检索到目标数据集后&#xff0c;开始数据挖掘&#xff0c;本文以阿尔兹海默症数据集GSE1297为例 上节做了很多的基因数据清洗&#xff08;离群值处理、低表达基因、归一化、log2处理&#xff09;操作&#xff0c;本节介绍构建临床分组信息。 我们已经学习了提取表达矩阵的临床…

蓝桥杯每日一题2023.10.4

双向排序 - 蓝桥云课 (lanqiao.cn) 题目描述 题目分析 六十分解法如下&#xff1a;按照题意简单排序 #include<bits/stdc.h> using namespace std; const int N 2e5 10; int n, m, p, q, a[N]; bool cmp(int x, int y) {return x > y; } int main() {cin >&g…

postgresql-备份与恢复

postgresql-备份与恢复 基本概念备份类型物理备份与逻辑备份在线备份与离线备份全量备份与增量备份 备份恢复工具备份与恢复逻辑备份与还原备份单个数据库psqlpg_dumppg_store 备份整个集群 基本概念 服务器系统错误、硬件故障或者人为失误都可能导致数据的丢失或损坏。因此&am…

【Java 进阶篇】JDBC 数据库连接池详解

数据库连接池是数据库连接的管理和复用工具&#xff0c;它可以有效地降低数据库连接和断开连接的开销&#xff0c;提高了数据库访问的性能和效率。在 Java 中&#xff0c;JDBC 数据库连接池是一个常见的实现方式&#xff0c;本文将详细介绍 JDBC 数据库连接池的使用和原理。 1…

Qt扩展-QCustomPlot绘图基础概述

QCustomPlot绘图基础概述 一、概述二、改变外观1. Graph 类型2. Axis 坐标轴3. 网格 三、案例1. 简单布局两个图2. 绘图与多个轴和更先进的样式3. 绘制日期和时间数据 四、其他Graph&#xff1a;曲线&#xff0c;条形图&#xff0c;统计框图&#xff0c;… 一、概述 本教程使用…

调度程序以及调度算法的评价指标

1.调度器/调度程序 调度程序决定调度算法&#xff0c;时间片大小 ②&#xff0c;③由调度程序引起&#xff0c;调度程序决定: 1.调度时机 创建新进程进程退出运行进程阻塞I/O中断发生&#xff08;可能唤醒某些阻塞进程)非抢占式调度策略&#xff0c;只有运行进程阻塞或退出…

小谈设计模式(14)—建造者模式

小谈设计模式&#xff08;14&#xff09;—建造者模式 专栏介绍专栏地址专栏介绍 建造者模式角色分类产品&#xff08;Product&#xff09;抽象建造者&#xff08;Builder&#xff09;具体建造者&#xff08;Concrete Builder&#xff09;指挥者&#xff08;Director&#xff0…

10.5作业

磕磕绊绊还是差不多完成了,tcp多客户端在线词典 代码&#xff1a; 数据库导入&#xff1a;有点粗糙&#xff0c;不知道怎么搞成两列&#xff0c;一个单词中间还是空格卧槽难搞 #include <stdio.h> #include <string.h> #include <stdlib.h> #include <s…

Go 代码中的文档和注释

撰写清晰、简洁和全面的代码文档的指南 在软件开发领域&#xff0c;编写代码只占了一半的战斗。另一半则围绕着创建清晰、简洁和全面的文档展开&#xff0c;这些文档不仅有助于开发人员理解代码库&#xff0c;还充当未来开发的路线图。在本指南中&#xff0c;我们将深入探讨编…

Spring:通过@Lazy解决构造方法形式的循环依赖问题

一、定义2个循环依赖的类 package cn.edu.tju.domain2;import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Component;Component public class A {private final B b;public B getB() {return b;}Lazypublic A(B b){this.b b;//Sy…

[React源码解析] React的设计理念和源码架构 (一)

任务分割异步执行让出执法权 文章目录 1.React的设计理念1.1 Fiber1.2 Scheduler1.3 Lane1.4 代数效应 2.React的源码架构2.1 大概图示2.2 jsx2.3 Fiber双缓存2.4 scheduler2.5 Lane模型2.6 reconciler2.7 renderer2.8 concurrent 3.React源码调试 1.React的设计理念 Fiber: 即…

python获取时间戳

使用 datetime 库获取时间。 获取当前时间&#xff1a; import datetime print(datetime.datetime.now()) . 后面的是微秒&#xff0c;也是一个时间单位&#xff0c;1秒1000000微秒。 转为时间戳&#xff1a; import datetimedate datetime.datetime.now() timestamp date…

最短路径专题5 最短路径

题目&#xff1a; 样例&#xff1a; 输入 4 5 0 2 0 1 2 0 2 5 0 3 1 1 2 2 3 2 2 输出 3 0->3->2 思路&#xff1a; 根据题目意思&#xff0c;求最短路&#xff0c;这个根据平时的Dijkstra&#xff08;堆优化&#xff09;即可&#xff0c;关键在于求路径的方法&#x…

uni-app:实现页面效果2(canvas绘制,根据页面宽度调整元素位置)

效果 代码 <template><view><!-- 车搭配指示器-双显 --><view class"content_position"><view class"content"><view class"SN"><view class"SN_title">设备1</view><view class…

视频讲解|含可再生能源的热电联供型微网经济运行优化(含确定性和源荷随机两部分代码)

1 主要内容 该视频为《含可再生能源的热电联供型微网经济运行优化》代码讲解内容&#xff0c;对应的资源下载链接为考虑源荷不确定性的热电联供微网优化-王锐matlab&#xff08;含视频讲解&#xff09;&#xff0c;对该程序进行了详尽的讲解&#xff0c;基本做到句句分析和讲解…

源码系列 之 ThreadLocal

简介 ThreadLocal的作用是做数据隔离&#xff0c;存储的变量只属于当前线程&#xff0c;相当于当前线程的局部变量&#xff0c;多线程环境下&#xff0c;不会被别的线程访问与修改。常用于存储线程私有成员变量、上下文&#xff0c;和用于同一线程&#xff0c;不同层级方法间传…