Unity获取Animator动画播放完成事件
整理了一些在日常经验中处理动画播放完成事件的方法
方法:
1.Dotween配合异步实现
2.状态机计时方法实现
3.原生动画行为方法实现
方法一:Dotween异步方法
using UnityEngine;
using System.Threading.Tasks;
using DG.Tweening;public class PlayerAnimAsync : MonoBehaviour
{private Animator animator;private bool isAnimPlaying = false;void Start(){animator = GetComponent<Animator>();}void Update(){animator = GetComponent<Animator>();// 开始动画if (!isAnimPlaying){StartAttack();}}private async void StartAttack(){isAnimPlaying = true;await AnimationFinish("Attack", 0f); //等待Attack播放完Debug.Log("Attack播放完了,可以执行Idle");animator.Play("Idle");isAnimPlaying = false;}//AnimationFinish 异步播放动画public async Task AnimationFinish(string animName, float extreTime = 0f){await DOTween.Sequence().AppendCallback(() => animator.Play(animName)).AppendInterval(GetAnimationClipLength(animName) + extreTime).AsyncWaitForCompletion();}//GetAnimationClipLength 获取动画片段时长private float GetAnimationClipLength(string animName){RuntimeAnimatorController ac = animator.runtimeAnimatorController;foreach (AnimationClip clip in ac.animationClips){if (clip.name == animName){return clip.length;}}return 0f;}
}
方法二:状态机计时方法
using UnityEngine;
public class PlayerAnimFSM : MonoBehaviour
{private enum AnimationState {Idle, Attack}private AnimationState currentState;private Animator animator;private float waitAnimTime = 0f; //动画计时器void Start(){animator = GetComponent<Animator>();}void Update(){CheckState();currentState = currentState switch{AnimationState.Idle => IdleState(),AnimationState.Attack => AttackState(),_ => currentState};}private AnimationState IdleState(){animator.Play("Idle");return AnimationState.Idle;}private AnimationState AttackState(){//播放动画animator.Play("Attack");waitAnimTime += Time.deltaTime;if (waitAnimTime >= animator.GetCurrentAnimatorStateInfo(0).length){//当动画记录时间大于当前正在播放动画的时间时//todo:这里有一个BUG,在animator.Play()的动画需要在下一帧animator.GetCurrentAnimatorStateInfo(0).length才能获取到正确的时间//在这里默认动画长度都会大于1帧所以没太大的问题//正确的做法是参考方法一种的GetAnimationClipLength来获取动画时间waitAnimTime = 0; //重置动画时间Debug.Log("Attack播放完了,可以执行Idle");return AnimationState.Idle; //转换Idle状态}return AnimationState.Attack; //维持攻击状态}private void CheckState() //检测状态转换{if (currentState == AnimationState.Attack) //维持攻击动画不被打断return;if (Input.GetKeyDown(KeyCode.Space)){currentState = AnimationState.Attack;return;}currentState = AnimationState.Idle;}
}
方法三:原生动画行为方法实现
这里需要用到两个脚本PlayerAnimSM和AttackFinish来实现,此处isAnimPlaying借助原生动画行为来复原
using UnityEngine;public class PlayerAnimSM : MonoBehaviour
{private Animator animator;public bool isAnimPlaying = false;void Start(){animator = GetComponent<Animator>();}void Update(){animator = GetComponent<Animator>();// 开始动画if (!isAnimPlaying){StartAttack();}}private void StartAttack(){isAnimPlaying = true;animator.Play("Attack");}
}
AttackFinish脚本借助界面创建步骤如下:
1.在动画器中点击需要传递动画完成事件的动画,点击右下角的Add Behaviour(添加行为),可以添加Unity预制的脚本
2.使用这个方法需要有动画过渡的方式(此处为AnyState到Idle),供后续代码中的OnStateExit使用(举例:如果希望有攻击结束到闲置动画的过渡,就需要从攻击动画连线到闲置动画,重点!!!一定要有退出时间,设置0s也没事,但一定要勾选,这里我就使用AnyState过渡过去了,使用AnyState时也一定要勾选退出时间



创建名为AttackFinish 的脚本(这里Unity叫行为)
双击点开这个脚本
using UnityEngine;public class AttackFinish : StateMachineBehaviour
{// OnStateEnter is called when a transition starts and the state machine starts to evaluate this state//override public void OnStateEnter(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)//{//}// OnStateUpdate is called on each Update frame between OnStateEnter and OnStateExit callbacks//override public void OnStateUpdate(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)//{//}//OnStateExit is called when a transition ends and the state machine finishes evaluating this stateoverride public void OnStateExit(Animator animator, AnimatorStateInfo stateInfo, int layerIndex){//在此处将isAnimPlaying重置回falseanimator.GetComponent<PlayerAnimSM>().isAnimPlaying = false;Debug.Log("Attack播放完了");//播放动画结束后的默认动画,我这里设置为idle你可以设置为任意动画但是一定要有过渡,从Attack到Idle的过渡animator.Play("Idle");}// OnStateMove is called right after Animator.OnAnimatorMove()//override public void OnStateMove(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)//{// // Implement code that processes and affects root motion//}// OnStateIK is called right after Animator.OnAnimatorIK()//override public void OnStateIK(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)//{// // Implement code that sets up animation IK (inverse kinematics)//}
}
后谈:还有一些诸如动画帧事件的方法没有收录在内。作者总感觉无论哪一个方法都不是特别合适或者顺手,当然无论是用原生还是自己去写,寻找适合自己项目的方法才是最好的。所以作者也会在今后的开发道路上继续学习。
相关文章:
Unity获取Animator动画播放完成事件
整理了一些在日常经验中处理动画播放完成事件的方法 方法: 1.Dotween配合异步实现 2.状态机计时方法实现 3.原生动画行为方法实现 方法一:Dotween异步方法 using UnityEngine; using System.Threading.Tasks; using DG.Tweening;public class PlayerAnimAsync : M…...
git submodule 使用
在Git中,子模块(submodule)是一种将一个Git仓库作为另一个Git仓库的子目录嵌入的方式。这使得主仓库能够跟踪和管理对外部依赖的更改。 添加子模块 初始化父仓库:如果你还没有创建父仓库,先创建它。 添加子模块&…...
【Jenkins未授权访问漏洞 】
默认情况下 Jenkins面板中用户可以选择执行脚本界面来操作一些系统层命令,攻击者可通过未授权访问漏洞或者暴力破解用户密码等进入后台管理服务,通过脚本执行界面从而获取服务器权限。 第一步:使用fofa语句搜索 搜索语句: port&…...
前端处理 Excel 文件
引入XLSX XLSX 是一个流行的 JavaScript 库,用于处理 Excel 文件(包括 .xls 和 .xlsx 格式)。它可以在 Node.js 环境和浏览器中运行,提供了丰富的 API 来读取、写入、修改 Excel 文件。当你使用 import * as XLSX from xlsx; 这行…...
(vue)el-cascader级联选择器按勾选的顺序传值,摆脱层级约束
(vue)el-cascader级联选择器按勾选的顺序传值,摆脱层级约束 需求:按勾选的顺序给后端传值 难点:在 Element UI 的 el-cascader 组件中,默认的行为是根据数据的层级结构来显示选项,用户的选择也会基于这种层级结构,el-…...
Redis进阶(四):哨兵
为了解决主节点故障,需要人工操作切换主从的情况;因此需要一种方法可以自动化的切换:哨兵的引入大大改变这种情况。 哨兵的基本概念 自动切换主从节点 哨兵架构 1、当一个哨兵节点发现主节点挂了的时候,还需要其他节点也去检测一…...
蓝屏事件:网络安全的启示
“微软蓝屏”事件暴露了网络安全哪些问题? 近日,一次由微软视窗系统软件更新引发的全球性“微软蓝屏”事件,不仅成为科技领域的热点新闻,更是一次对全球IT基础设施韧性与安全性的深刻检验。这次事件,源于美国电脑安全技…...
技术方案评审原则
系列文章目录 提示:这里可以添加系列文章的所有文章的目录,目录需要自己手动添加 TODO:写完再整理 文章目录 系列文章目录前言技术方案评审原则1.理论突破阶段2.技术突破阶段3.工程化阶段自动驾驶行业的技术方案分析前言 认知有限,望大家多多包涵,有什么问题也希望能够与大…...
117页PPT埃森哲-物流行业信息化整体规划方案
一、埃森哲-物流行业信息化整体规划方案 资料下载方式,请看每张图片右下角信息 埃森哲在物流行业信息化整体规划项目中的核心内容,旨在帮助物流企业通过信息技术的应用实现业务流程的优化、运营效率的提升以及市场竞争力的增强。以下是埃森哲在此类项目…...
百度网盘不下载怎么直接打印文件?
在数字化时代,百度网盘作为我们存储和分享文件的重要工具,承载了大量的文档、图片和资料。然而,当需要打印这些文件时,很多用户会面临一个共同的问题:不想下载到本地再打印,既占用空间又浪费时间。那么&…...
设置了 robots.txt 禁止爬虫抓取,为什么还是能被百度搜索出来
虽然设置了 robots.txt 禁止爬虫抓取,但网页仍可能被百度搜索出来,主要有以下几个原因: robots.txt 只是一种建议性协议,并非强制性[2]。虽然大多数搜索引擎会遵守 robots.txt 的规则,但并不是所有爬虫都会严格遵守。 …...
DedeCMS-V5.7.82-UTF8织梦管理系统漏洞
将靶场环境放到www目录下——访问/dedecms/uploads 安装程序 - 织梦内容管理系统 V5.7 UTF8SP2 同意协议——继续 继续 配置后——点击继续 进入后台 登录后台——填写用户名密码。 方法一:上传shell文件 后台——核心——附件管理——上传新文件。 访问/dedecms…...
【Python】字符串练习题及代码示例
1、使用while循环实现对字符串中每个字符进行输出。 代码示例: 2、请将代码实现如下进制的转换。 (1)v1675,请将v1转换为二进制。 代码: 注意:将十进制数转换为二进制数的方法是:bin(a),a是整型&#x…...
fluent动网格profile udf 注意事项
案例一: ((profile_name transient 2 0) ....第一行 (time 0 15.0) ....第二行 (v_x 1.2 1.2)) …...
【doghead】mac构建 2: player 端 clion构建
准备工作 【doghead】mac构建 1 【doghead】mac: clion2024.1启动崩溃 mbp的 uv 构建ok zhangbin@zhangbin-mbp-2 ~/tet/Fargo/zhb-bifrost/Bifrost-202403/worker/third_party/libuv main clion使用lldb cmake构建 更...
论网络流(最大流篇)--新手入门超详解--包教包会
论网络流--新手入门超详解--包教包会 1 前言2 什么是最大流3最大流问题的求解(1)问题转化--增广路的引入(2)走回头路--EK算法(3)EK的弊端(4)化图为树--DINIC算法 4后记 1 前言 网络…...
环境搭建:全面详尽的 MongoDB Shell MongoDB Server介绍、安装、验证与配置指南(以 Windows 系统为主)
环境搭建:全面详尽的 MongoDB Shell & MongoDB Server介绍、安装、验证与配置指南(以 Windows 系统为主) MongoDB 是一个基于文档的 NoSQL 数据库,以其高性能、灵活性和可扩展性而受到广泛欢迎。本文将带您完成 MongoDB 的安装…...
使用 OpenSearch 的 K-NN 向量搜索来增强搜索功能
使用 OpenSearch 的 K-NN 向量搜索来增强搜索功能 许多应用程序都依赖于提供精确且相关的搜索结果的能力。尽管传统关系数据库的全文搜索功能在某些情况下已经足够,但这些数据库在从文本中提取语义含义或搜索结构化程度较低的数据方面可能会出现不足。在这篇博文中&…...
Less-2(闭合)
我们使用第一关的测试方法尝试一下,打咩 直接看源码,看到,尝试一下闭合 <?php ini_set("display_errors", 0); $str $_GET["keyword"]; echo "<h2 aligncenter>没有找到和".htmlspecialchars($str)."相…...
mysql介绍
MySQL是一种开源的关系型数据库管理系统(RDBMS),广泛用于存储和管理数据。它支持多种操作系统,如Linux、Windows、MacOS等。MySQL的特点包括: 1.开源免费:MySQL是开源的,可以免费使用和分发。 2…...
django filter 统计数量 按属性去重
在Django中,如果你想要根据某个属性对查询集进行去重并统计数量,你可以使用values()方法配合annotate()方法来实现。这里有两种常见的方法来完成这个需求: 方法1:使用annotate()和Count 假设你有一个模型Item,并且你想…...
工业自动化时代的精准装配革新:迁移科技3D视觉系统如何重塑机器人定位装配
AI3D视觉的工业赋能者 迁移科技成立于2017年,作为行业领先的3D工业相机及视觉系统供应商,累计完成数亿元融资。其核心技术覆盖硬件设计、算法优化及软件集成,通过稳定、易用、高回报的AI3D视觉系统,为汽车、新能源、金属制造等行…...
OpenPrompt 和直接对提示词的嵌入向量进行训练有什么区别
OpenPrompt 和直接对提示词的嵌入向量进行训练有什么区别 直接训练提示词嵌入向量的核心区别 您提到的代码: prompt_embedding = initial_embedding.clone().requires_grad_(True) optimizer = torch.optim.Adam([prompt_embedding...
SpringCloudGateway 自定义局部过滤器
场景: 将所有请求转化为同一路径请求(方便穿网配置)在请求头内标识原来路径,然后在将请求分发给不同服务 AllToOneGatewayFilterFactory import lombok.Getter; import lombok.Setter; import lombok.extern.slf4j.Slf4j; impor…...
C++使用 new 来创建动态数组
问题: 不能使用变量定义数组大小 原因: 这是因为数组在内存中是连续存储的,编译器需要在编译阶段就确定数组的大小,以便正确地分配内存空间。如果允许使用变量来定义数组的大小,那么编译器就无法在编译时确定数组的大…...
人工智能(大型语言模型 LLMs)对不同学科的影响以及由此产生的新学习方式
今天是关于AI如何在教学中增强学生的学习体验,我把重要信息标红了。人文学科的价值被低估了 ⬇️ 转型与必要性 人工智能正在深刻地改变教育,这并非炒作,而是已经发生的巨大变革。教育机构和教育者不能忽视它,试图简单地禁止学生使…...
C# 表达式和运算符(求值顺序)
求值顺序 表达式可以由许多嵌套的子表达式构成。子表达式的求值顺序可以使表达式的最终值发生 变化。 例如,已知表达式3*52,依照子表达式的求值顺序,有两种可能的结果,如图9-3所示。 如果乘法先执行,结果是17。如果5…...
算法打卡第18天
从中序与后序遍历序列构造二叉树 (力扣106题) 给定两个整数数组 inorder 和 postorder ,其中 inorder 是二叉树的中序遍历, postorder 是同一棵树的后序遍历,请你构造并返回这颗 二叉树 。 示例 1: 输入:inorder [9,3,15,20,7…...
【深度学习新浪潮】什么是credit assignment problem?
Credit Assignment Problem(信用分配问题) 是机器学习,尤其是强化学习(RL)中的核心挑战之一,指的是如何将最终的奖励或惩罚准确地分配给导致该结果的各个中间动作或决策。在序列决策任务中,智能体执行一系列动作后获得一个最终奖励,但每个动作对最终结果的贡献程度往往…...
规则与人性的天平——由高考迟到事件引发的思考
当那位身着校服的考生在考场关闭1分钟后狂奔而至,他涨红的脸上写满绝望。铁门内秒针划过的弧度,成为改变人生的残酷抛物线。家长声嘶力竭的哀求与考务人员机械的"这是规定",构成当代中国教育最尖锐的隐喻。 一、刚性规则的必要性 …...
