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

news2024/11/16 7:41:24

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


帧动画,在上一篇已经做了介绍,感兴趣的朋友可以前往阅读: 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 failed
                return;
            }
            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;
    }

    // OPTIONS

    toggleDebugSlots () {
        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;
        }
    }

    // ANIMATIONS

    stop () {
        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.coloradmin.cn/o/1060590.html

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

相关文章

10.5作业

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

NPDP产品经理知识(产品创新流程)

1.复习组合管理: 组合管理的目标 ===> 价值最大化,项目平衡,战略一致,管道平衡(资源需求和供给),盈利充分 (实现财务目标) 产品创新流程就是管理风险的过程。 模糊前端: 产品创新章程:PIC 包含 =====> 背景,聚焦舞台,目标和目的,特别准则,可持续性 新产…

mysql面试题13:MySQL中什么是异步复制?底层实现?

该文章专注于面试,面试只要回答关键点即可,不需要对框架有非常深入的回答,如果你想应付面试,是足够了,抓住关键点 面试官:讲一讲mysql中什么是异步复制?底层实现? MySQL中的异步复制(Asynchronous Replication)是一种复制模式,主服务器将数据写入二进制日志后,无…

计组——I/O方式

一、程序查询方式 CPU不断轮询检查I/O控制器中“状态寄存器”&#xff0c;检测到状态为“已完成”之后&#xff0c;再从数据寄存器取出输入数据。 过程&#xff1a; 1.CPU执行初始化程序&#xff0c;并预置传送参数&#xff1b;设置计数器、设置数据首地址。 2. 向I/O接口发…

星际争霸之小霸王之小蜜蜂(十六)--狂奔的花猫

系列文章目录 星际争霸之小霸王之小蜜蜂&#xff08;十五&#xff09;--剧将终场 星际争霸之小霸王之小蜜蜂&#xff08;十四&#xff09;--资本家的眼泪 星际争霸之小霸王之小蜜蜂&#xff08;十三&#xff09;--接着奏乐接着舞 星际争霸之小霸王之小蜜蜂&#xff08;十二…

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

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

路径问题【动态规划】

一、不同路径 class Solution { public:int uniquePaths(int m, int n) {vector<vector<int>> dp(m1,vector<int>(n1));dp[0][1] 1;for(int i 1;i < m;i){for(int j 1;j < n;j){dp[i][j] dp[i-1][j]dp[i][j-1];}}return dp[m][n];} }; 二、不同路…

三、thymeleaf基本语法

3.1、基本语法 3.1.1变量表达式&#xff1a;${...} 变量表达式用于在页面中输出指定的内容&#xff0c;此内容可以是变量&#xff0c;可以是集合的元素&#xff0c;也可以是对象的属性。主要用于填充标签的属性值&#xff0c;标签内的文本&#xff0c;以及页面中js变量的值等…

OpenCV项目开发实战--CUDA 模块使用详细介绍--附完整代码

如果您已经使用 OpenCV 一段时间,您应该已经注意到,在大多数情况下 OpenCV 使用 CPU,这并不总能保证您获得所需的性能。为了解决这个问题,2010 年 OpenCV 中添加了一个使用 CUDA 提供 GPU 加速的新模块。您可以在下面找到展示 GPU 模块优势的基准测试: 图 1: CPU 上的 Op…

Day-06 基于 Docker安装 Nginx 镜像

1.去官方公有仓库查询nginx镜像 docker search nginx 2.拉取该镜像 docker pull nginx 3. 启动镜像&#xff0c;使用nginx服务&#xff0c;代理本机8080端口(测试是不是好使) docker run -d -p 8080:80 --name nginx-8080 nginx docker ps curl 127.0.0.1:8080

黑马程序员 MySQL数据库入门到精通——进阶篇(2)

黑马程序员 MySQL数据库入门到精通——进阶篇&#xff08;2&#xff09; 1. SQL优化1.1 插入数据1.2 主键优化1.3 order by优化1.4 group by优化1.5 limit优化1.6 count优化1.7 update优化 2. 视图2.1 视图-介绍及基本语法2.2 视图-检查选项(cascaded)2.3 视图-检查选项(local)…

SQL Server不允许保存更改的解决方法

SQL Server不允许保存更改的解决方法

深信服SG上网优化管理系统存在任意文件读取漏洞 附POC

文章目录 深信服SG上网优化管理系统存在任意文件读取漏洞 附POC1. 深信服SG上网优化管理系统简介2.漏洞描述3.影响版本4.fofa查询语句5.漏洞复现6.POC&EXP7.整改意见8.往期回顾 深信服SG上网优化管理系统存在任意文件读取漏洞 附POC 免责声明&#xff1a;请勿利用文章内的…

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

文章目录 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 无…

C++入门-day01

一、认识C C融合了三种不同的编程方式 C代表的过程性语言在C基础上添加的类、结构体puls代表的面向对象语言C模板支持泛型编程 C完全兼容C的特性 Tips&#xff1a;侯捷老师提倡的Modren C是指C11、C14、C17和C20这些新标准所引入的一系列新特性和改进。在我们练习的时候也应当去…

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

ser&#xff1a; widget.cpp&#xff1a; #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…

MyBatisPlus(八)范围查询

说明 范围查询&#xff0c;包括&#xff1a; 大于大于等于小于小于等于在范围内在范围外 大于&#xff1a;gt 代码 Testvoid gt() {LambdaQueryWrapper<User> wrapper new LambdaQueryWrapper<>();wrapper.gt(User::getAge, 20);List<User> users mapp…

Netty 4.1.98.Final 发布

Netty 4.1.98 稳定版已发布。Netty 是一个异步事件驱动的网络应用框架&#xff0c;主要用于可维护的高性能协议服务器和客户端的快速开发。 此版本还原了上一版本中所做的更改&#xff0c;这些更改导致 HTTP header 验证比所需的更严格 (#13615)。除此之外&#xff0c;当使用 n…

隐私交易成新刚需,Unijoin 凭什么优势杀出重围?

随着区块链技术的普及和发展&#xff0c;全球加密货币用户在持续增长&#xff0c;根据火币研究院公布的数据&#xff0c;2022年全球加密用户已达到 3.2亿人&#xff0c;目前全球人口总数超过了 80亿&#xff0c;加密货币用户渗透率已达到了 4%。 尤其是在 2020 年开启的 DeFi 牛…

mysql面试题12:讲一讲MySQL中什么是半同步复制?底层实现?

该文章专注于面试,面试只要回答关键点即可,不需要对框架有非常深入的回答,如果你想应付面试,是足够了,抓住关键点 面试官:讲一讲mysql中什么是半同步复制?底层实现? MySQL中的半同步复制(Semi-Synchronous Replication)是一种复制模式,它在主服务器将数据写入到二…