Unity中对Spine动画播放、暂停、事件处理管理类

news2025/3/1 8:57:18

Unity中对Spine动画播放、暂停、事件处理管理类

  • 介绍
  • Spine的事件处理
    • 动画师制作沟通
    • Unity前端使用事件
  • Unity中动画播放
  • Unity中动画暂定和继续
  • Unity中停止动画
  • Unity中动画转向
  • Unity中获取骨骼和设置插槽附件
  • 完整管理类分享
  • 总结

介绍

最近在做设计spine动画的抖音小程序,正好借此机会分享一下我对Spine动画的管理等相关知识,我们公司使用的是Spine4.0.64开发的动画,Unity使用的是Spine4.0,这里不分享spine的导入等其他操作了,如果想要了解可以看一下我之前的Unity如何使用Spine动画导出的动画文章。

Spine的事件处理

动画师制作沟通

这个地方动画师是需要跟unity前端沟通好,因为事件本身是在做动画的时候插入的,所以事件的时间节点和事件名称都需要提前定义好(事件的用法也会有很多,我们通常是攻击卡帧的时候需要)。如下图所示动画的事件。
请添加图片描述

Unity前端使用事件

当动画师将编辑好的事件动画给到Unity前段后的使用方式如下

//动画开始时执行开始事件
skeleton.AnimationState.Start += (t)=> 
{
    Debug.LogError("开始事件");
};
//中断或者结束都触发结束事件
skeleton.AnimationState.End += (t) =>
{
    Debug.LogError("结束事件");
};
//播放完成事件
skeleton.AnimationState.Complete += (t) =>
{
    Debug.LogError("动画播放完成");
};
//在Spine软件中制作动画时添加的自定义事件
skeleton.AnimationState.Event += (t, e) =>
{
    Debug.LogError("自定义事件 e.Time = " + e.Time + " , e = " + e.ToString());
};

当调用该动画时会执行自定义事件打印如下
请添加图片描述

Unity中动画播放

skeleton.timeScale = timeScale;
skeleton.AnimationState.SetAnimation(trackIndex, animName, loop);

Unity中动画暂定和继续

//暂停
skeleton.timeScale = 0;
//继续
skeleton.timeScale = 1;

Unity中停止动画

注意这里是将动画恢复至默认开始状态那一帧

skeleton.AnimationState.SetEmptyAnimation(trackIndex, mixDuration);

Unity中动画转向

skeleton.Skeleton.ScaleX = x;

Unity中获取骨骼和设置插槽附件

//获取骨骼
Bone b = skeleton.Skeleton.FindBone(boneName);

skeleton.Skeleton.SetAttachment(slotName, attachmentName);

完整管理类分享

using Spine;
using Spine.Unity;
using System;
using System.Net.Mail;
using UnityEngine;
using static Spine.AnimationState;

/// <summary>
/// Spine动画控制器
/// </summary>
public class SpineAnimManager : Singleton<SpineAnimManager>
{
    private TrackEntryDelegate ac = null;

    /// <summary>
    /// 播放动画
    /// </summary>
    /// <param name="skeleton">骨骼</param>
    /// <param name="action">回调</param>
    /// <param name="trackIndex"></param>
    /// <param name="animName">动画名</param>
    /// <param name="loop">是否循环</param>
    /// <param name="timeScale">时间缩放</param>
    public void PlayAnim(SkeletonGraphic skeleton, Action action, int trackIndex, string animName, bool loop, float timeScale = 1)
    {
        if (skeleton != null)
        {
            PlayAnim(skeleton, trackIndex, animName, loop, timeScale);
            if (action != null)
            {
                ac = delegate
                {
                    action?.Invoke();
                    skeleton.AnimationState.Complete -= ac;
                    ac = null;
                };
                skeleton.AnimationState.Complete += ac;
            }
        }
    }

    /// <summary>
    /// 停止播放动画
    /// </summary>
    /// <param name="sg"></param>
    /// <param name="trackIndex"></param>
    /// <param name="mixDuration"></param>
    public void StopAnim(SkeletonGraphic sg, int trackIndex, float mixDuration) 
    {
        //sg.Clear();--开启这个则动画和对象都消失
        sg.AnimationState.SetEmptyAnimation(trackIndex, mixDuration);
    }

    /// <summary>
    /// 暂停动画
    /// </summary>
    /// <param name="sg"></param>
    public void PauseAnim(SkeletonGraphic sg) 
    {
        sg.timeScale = 0;
    }

    /// <summary>
    /// 继续动画
    /// </summary>
    /// <param name="sg"></param>
    public void ResumeAnim(SkeletonGraphic sg) 
    {
        sg.timeScale = 1;
    }

    /// <summary>
    /// 设置动画时间缩放
    /// </summary>
    /// <param name="sg"></param>
    /// <param name="timeScale"></param>
    public void SetAnimTimeScale(SkeletonGraphic sg, float timeScale) 
    {
        sg.timeScale = timeScale;
    }

    /// <summary>
    /// 播放动画
    /// </summary>
    /// <param name="skeleton"></param>
    /// <param name="trackIndex"></param>
    /// <param name="animName"></param>
    /// <param name="loop"></param>
    /// <param name="timeScale"></param>
    public void PlayAnim(SkeletonGraphic skeleton, int trackIndex, string animName, bool loop, float timeScale = 1) 
    {
        if (skeleton != null)
        {
            skeleton.timeScale = timeScale;
            skeleton.AnimationState.SetAnimation(trackIndex, animName, loop);
        }
    }

    /// <summary>
    /// 动画添加事件
    /// </summary>
    /// <param name="skeleton"></param>
    /// <param name="startCall"></param>
    /// <param name="endCall"></param>
    /// <param name="completeCall"></param>
    /// <param name="eventCall"></param>
    public void AddEvent(SkeletonGraphic skeleton,Action<TrackEntry> startCall,Action<TrackEntry> endCall, Action<TrackEntry> completeCall, Action<TrackEntry, Spine.Event> eventCall) 
    {
        if (skeleton != null) 
        {
            skeleton.AnimationState.Start += (t)=> 
            {
                Debug.LogError("开始事件");
                startCall?.Invoke(t); 
            };
            //中段结束都算
            skeleton.AnimationState.End += (t) =>
            {
                Debug.LogError("结束事件");
                endCall?.Invoke(t);
            };
            //播放完成
            skeleton.AnimationState.Complete += (t) =>
            {
                Debug.LogError("动画播放完成");
                completeCall?.Invoke(t);
            };
            //在Spine软件中制作动画时添加的自定义事件
            skeleton.AnimationState.Event += (t, e) =>
            {
                Debug.LogError("自定义事件 e.Time = " + e.Time + " , e = " + e.ToString());
                eventCall?.Invoke(t,e);
            };
        }
    }

    /// <summary>
    /// 转向
    /// </summary>
    public void Rotate(SkeletonGraphic skeleton, float x) 
    {
        skeleton.Skeleton.ScaleX = x;
    }

    /// <summary>
    /// 获取骨骼、设置插槽附件
    /// </summary>
    /// <param name="skeleton"></param>
    /// <param name="slotName">插槽</param>
    /// <param name="boneName">骨骼名</param>
    /// <param name="attachmentName">特性</param>
    public void GetBone(SkeletonGraphic skeleton,string slotName, string boneName, string attachmentName) 
    {
        //获取骨骼
        Bone b = skeleton.Skeleton.FindBone(boneName);

        skeleton.Skeleton.SetAttachment(slotName, attachmentName);
    }

    /// <summary>
    /// 队列播放spine动画
    /// </summary>
    /// <param name="skeleton">spine组件</param>
    /// <param name="startFunc">动画开始的时候调用的方法</param>
    /// <param name="endFunc">动画结束的时候调用的方法</param>
    /// <param name="trackIndex">轨道索引(通过索引可实现多个动画同时播放)</param>
    /// <param name="animName">动画名字</param>
    /// <param name="loop">是否循环播放</param>
    /// <param name="timeScale">动画速度0-1</param>
    /// <param name="delay">动画播放完毕延迟时间</param>
    public void QueuePlayAni(SkeletonGraphic skeleton, Action startFunc, Action endFunc, int trackIndex, string animName, bool loop, float timeScale = 1, float delay = 0f)
    {
        if (skeleton != null)
        {
            TrackEntry tr = skeleton.AnimationState.AddAnimation(trackIndex, animName, loop, delay);
            if (startFunc != null)
                tr.Start += (sp) =>
                {
                    startFunc.Invoke();
                };
            if (endFunc != null)
                tr.Complete += (sp) =>
                {
                    endFunc.Invoke();
                };
        }
    }
}

总结

上述是我对Spine的一些控制和理解,希望可以帮助到大家。感谢大家的支持。

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

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

相关文章

RecyclerView的缓存机制(面试常客)

在构建滚动列表时&#xff0c;我们常首选RecyclerView&#xff0c;出于它优秀的缓存复用机制。 核心机制 RecyclerView的缓存机制又称回收复用机制&#xff0c;RecyclerView构建列表视图分为以下三步&#xff1a; 第一步的创建ViewHolder是RecyclerView构建视图时最耗时的操作…

鸿蒙(API 12 Beta3版)【使用通话设备切换组件】使用投播组件

基本概念 系统不再提供音频输出设备切换的API&#xff0c;如果需要应用内切换音频输出设备&#xff0c;请实现AVCastPicker组件&#xff0c;相关参数可参考[ohos.multimedia.avCastPicker]和 [ohos.multimedia.avCastPickerParam]。 本文将主要介绍AVCastPicker组件接入&…

【论文笔记】:PVswin-YOLOv8s:基于无人机的行人和车辆检测,使用改进的YOLOv8在智能城市中进行交通管理

摘要 在智慧城市中&#xff0c;有效的交通拥堵管理取决于熟练的行人和车辆检测。无人机 &#xff08;UAV&#xff09; 提供了一种具有移动性、成本效益和宽视野的解决方案&#xff0c;然而&#xff0c;优化识别模型对于克服小型和遮挡物体带来的挑战至关重要。为了解决这些问题…

推出 SAM 2:适用于视频和图像的下一代 Meta Segment Anything 模型

继图像元分割模型(SAM) 取得成功之后&#xff0c;我们发布了SAM 2&#xff0c;这是一个用于在图像和视频中实时提示对象分割的统一模型&#xff0c;可实现最先进的性能。 为了秉承我们的开放科学方针&#xff0c;我们通过宽松的 Apache 2.0 许可证共享代码和模型权重。 我们还…

嵌入式linux系统镜像制作day2

点击上方"蓝字"关注我们 01、前言 嵌入式linux系统镜像制作day1这一节先了解,后面实操 02、Yocto项目快速启动 Yocto项目通过OpenEmbedded构建系统为各种平台(包括x86-64和仿真平台)提供了一个针对ARM、MIPS、PowerPC和x86架构的开源开发环境。您可以使用Yocto项…

WebDeveloper:1靶机渗透测试

一、靶机下载地址 https://www.vulnhub.com/entry/web-developer-1,288/ 二、信息收集 1、主机发现 # 使用命令 nmap 192.168.145.0/24 -sn | grep -B 2 "00:0C:29:54:22:E9" 2、端口扫描 # 使用命令 nmap 192.168.145.216 -p- -sV 3、指纹识别 # 使用命令 wha…

【SpringCloud】什么是MQ

RabbitMQ 1.什么是MQ 1.1 同步和异步通信 微服务间通讯有同步和异步两种方式&#xff1a; 同步通讯&#xff1a;就例如打电话一样&#xff0c;需要实时响应异步通讯&#xff1a;就例如发邮件一样&#xff0c;不需要马上回复 1.1.1同步通讯 Feign的远程调用就属于同步通讯…

【数据分析】描述性统计分析 - 直方图

一、什么是直方图 由一批长方形构成&#xff0c;通过长方形的面积或高度来代表对应组在数据中所占的比例。用长方形的面积代表对应组的频数与组距的比时&#xff0c;则称为频率分布直方图;当用长方形的高代表对应组的频数时&#xff0c;则称为频数分布直方图。但严格统计意义上…

WT32-ETH01开发板模块,启明云端物联网方案,乐鑫ESP32多样化开发应用

在物联网(IoT)的浪潮中&#xff0c;无线Wi-Fi模块作为连接传统硬件与现代智能网络的桥梁&#xff0c;正逐渐成为智能家居和设备通信不可或缺的一部分。Wi-Fi模块也被称为串口Wi-Fi模块&#xff0c;是一种嵌入式模块&#xff0c;它能够将串口或TTL电平信号转换为符合Wi-Fi无线网…

普元EOS-多数据源时业务数据库初始化

1 问题 EOS开发应用的时候&#xff0c;可以采用多数据源。 项目启动的是时候报错如下&#xff1a; 2024-08-14 16:03:56.105 ERROR [EOS-DEMO-SYS,EOSLOWCODE,default,cuipengyu:EOSLOWCODE:28015,,,,] 28960 --- [Timer-0] c.p.g.a.a.m.RdcloudStatisticsThread : User…

66_1JSON【浏览器中处理JSON、Java中处理JSON(FastJSON、Jackson)】、Java中的bean

JSON 概念 JSON&#xff1a;JavaScript Object Notation是一种表示对象的方式 基于JavaScript语言的轻量级的数据交换格式;&#xff08;即:用来传输数据的一种格式&#xff09; 现在传输数据的方式更多是采用json的格式&#xff0c;渐渐代替了XML JSON的数据表示 JSON采用名值…

封装一个细粒度的限流器

文章目录 原因限流对象限流后的做法怎么确定限流阈值观测业务性能数据压测借鉴链路上的其他服务手动计算 四种静态限流算法令牌桶漏桶固定窗口与滑动窗口 手写限流算法令牌桶漏桶固定窗口滑动窗口 分布式限流的具体实现 原因 尽管云原生网关里有统一入口的限流&#xff08;根据…

股指期货套期保值中的展期管理有哪些?

在复杂的金融市场环境中&#xff0c;展期作为一种重要的风险管理工具&#xff0c;被广泛应用于期货交易中&#xff0c;特别是当投资者需要对长期资产进行套期保值时。展期的核心思想在于&#xff0c;通过连续替换高流动性的近月期货合约来替代流动性较差的远月合约&#xff0c;…

untiy有渲染线程和逻辑线程嘛

之前我也这么认为&#xff0c;其实unity引擎是单线程的&#xff0c;当然后续的jobs不在考虑范围内 如果你在一个awake 或者 start方法中 延时&#xff0c;是会卡住主线程的 比如 其实游戏引擎有一个基础简单理解&#xff0c;那就是不断的进行一个循环&#xff0c;在这个周期循…

网络分段如何增强 OT 网络的可见性

防火墙在保护运营技术(OT) 网络和系统方面发挥什么作用&#xff1f; 很多人会说&#xff0c;防火墙是一种防御机制&#xff0c;用于保护该环境免受 IT 和外界的影响。对于负责该关键系统正常运行的操作员来说&#xff0c;防火墙是阻止他人进入的外围保护。它也是需要从 OT 系统…

Unity Render Streaming项目实践经验

UnityRenderStreaming项目 项目github地址见上,我使用项目的3.1.0-exp.7版本、Unity 2023.1.0版本、windows11运行。 1下载项目包 2在Unity Hub中打开RenderStreaming~文件夹 3在package manager中导入com.unity.renderstreaming package 因为已经下载过了就选择install pa…

Linux shell编程学习笔记71: sort 命令——构造有序世界

0 前言 在大数据时代&#xff0c;我们面对和使用大量数据&#xff0c;如果数据是有序的&#xff0c;无疑是有益的。 在Linux中&#xff0c;我们可以使用 sort 命令来构造一个有序的空间。 1 sort命令 的功能、格式和选项说明 我们可以使用命令sort --help来获取帮助信息。 …

Spark MLlib 特征工程(下)

Spark MLlib 特征工程(下) 前面我们提到&#xff0c;典型的特征工程包含如下几个环节&#xff0c;即预处理、特征选择、归一化、离散化、Embedding 和向量计算&#xff0c;如下图所示。 在上一讲&#xff0c;我们着重讲解了其中的前 3 个环节&#xff0c;也就是预处理、特征选…

Memecoin的火爆与AMM在Solana上的主导地位

随着Solana区块链的高速发展&#xff0c;尤其是近年来Memecoin市场的崛起&#xff0c;AMM&#xff08;自动做市商&#xff09;逐渐成为Solana去中心化交易所&#xff08;DEX&#xff09;的主导交易模式。尽管Solana以其高效性能能够支持每秒数千笔交易&#xff0c;足以让中心化…

【C语言篇】深入理解指针3(附转移表源码)

文章目录 数组指针什么是数组指针数组指针变量的初始化 二维数组传参的本质函数指针函数指针变量的创建函数指针变量的使用 两端有趣的代码typedef 关键字 函数指针数组转移表写在最后 数组指针 什么是数组指针 在【C语言篇】深入理解指针2我们学习了指针数组&#xff0c;指针…