IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 游戏开发 -> Unity的新动画系统(animator)在脚本中的操控 -> 正文阅读

[游戏开发]Unity的新动画系统(animator)在脚本中的操控

前言

  今天在做人物攻击的部分时遇到一个问题,因为攻击动画是东平西凑的素材,所以每个clip的时长并不符合我项目中
的实际武器攻击时长,对每一个clip做帧的删除和添加又太过麻烦,我希望能够通过我期望的攻击间隔(攻击动画播放
时间)和当前的动画时间计算一个动画播放速度来动态的修改animator中state的speed,原本以为很简单的操作却扯
出一片我以前都不知道的动画脚本操作。

探索

Animator为我们提供的与state和clip有关的操作少的可怜,你只能通过GetCurrentStateInfo和GetCurrentClipInfo获取当前正在播放的动画和所处状态,而且返回值均为Info,也就是只读信息不能进行修改,换句话说unity压根就没想让你通过Animator这个类进行动态的state和clip修改参数。
但是只利用这些也是可以实现我上述问题的,虽然有点牵强,代码如下

animator.SetTrigger("Attack");
float speed =  animator.GetCurrentAnimatorStateInfo(0).length / attackDuration;
animator.speed = speed;

如果你的状态转换中有Exit Time的话就要换成GetNextAnimatorStateInfo因为在ExitTime过渡状态时还是算做上一个状态的,这里直接修改了整个animator的speed,动画结束后记得改回1即可。
这就是我们能用animator做的所有事情了,要想对动画操纵更多你需要引入一个新的命名空间UnityEditor.Animations,我们需要编辑器层面的操作才能实现对state和clip的修改,其实原因也很简单,这里有一个容易忽视的地方那就是state不是属于我们挂载在物体上的Animator组件的,state能持久保存说明它是一个persistent asset,你不可能通过Animator(MonoBehaviour)来对它进行修改因为MonoBehaviour是游戏运行时才存在的,所有对Asset的操作基本都需要UnityEditor这一命名空间。那很容易想到它是属于这个东西的
请添加图片描述
它对应的类叫做AnimatorController,实际上我们的代码在正常逻辑中几乎不会用到它,但我们经常会用到它的基类RuntimeAnimatiorController,每个Animator组件都包含一个RuntimeAnimatorController的成员,可以用它实现运行时替换AnimatorController。

public sealed class AnimatorController : RuntimeAnimatorController

这同时就带来一个问题,如果你通过变量直接加载对应的Animator Controller Asset然后进行修改当然没有问题,所有以它作为RuntimeAnimatorController的Animator都会同时被修改。
但如果你想通过Animator去访问该Animator正在引用的AnimatorController,Animator只包含RuntimeAnimatorController成员,你需要将RuntimeAnimatorController强制转换为AnimatorController(父转子),这个转换不是安全的,正常情况下是没问题的无论你是直接在Inspector窗口为Animator的RuntimeAnimatorController赋值还是用代码运行时赋值,因为你的赋值都是一个AnimatorController所以这个转换可以完成
但我有个手残的操作在这里作为一个提醒,我们一般替换RuntimeAnimatorController都会使用OverrideAnimatorController,在替换时你可以有以下两种方式

animator.runtimeAnimatorController = overrideAnimator;
animator.runtimeAnimatorController = overrideAnimator.runtimeAnimatorController;

因为OverrideAnimatorController也是继承自RuntimeAnimatorController,所以第一种可以直接完成隐式转换,单就效果来说也是完全相同的。但也就是说它和AnimatorController是兄弟关系,在这种情况下你如果再尝试把runtimeAnimatorController转换为AnimatorController的话就会报错因为它其中的值实际上是一个OverrideAnimatorController。

public class AnimatorOverrideController : RuntimeAnimatorController

从这个问题我们也可以看出来,OverrideAnimatorController保留了父类RuntimeAnimatorController的clips等信息,但它实际上完全没有存储独立的State等信息而是保留了一个runtimeAnimatorController的成员(实际上其中存储的是AnimatorController对象也就是它Override的那个Controller),所以以后还是注意一下使用第二种比较好

解决方案

现在最初要解决的问题就已经可以完美解决了,我使用了ExtensionMethod方便操作,代码如下

public static class ExtensionAnimator
{
    public static AnimatorState GetAnimatorState(this AnimatorController ac, int layer, string statename)
    {
        AnimatorControllerLayer acl = ac.layers[layer];
        
        foreach (var state in acl.stateMachine.states)
        {
            if(state.state.name == statename) return state.state;
        }
        return null;
    }
    public static AnimationClip GetAnimationClip(this Animator animator, string name)
    {
        foreach (var clip in animator.runtimeAnimatorController.animationClips)
        {
            if(clip.name == name) return clip;
        }
        return null;
    }
}
void Attack()
{
    /*animator.SetTrigger("Attack");
    float speed =  animator.GetCurrentAnimatorStateInfo(0).length / attackDuration;
    animator.speed = speed;*/
    AnimatorController ac = (AnimatorController)animator.runtimeAnimatorController;
    AnimatorState state = ac.GetAnimatorState(0, "Attack");
    AnimationClip clip = animator.GetAnimationClip(state.motion.name);
    state.speed = clip.length / attackDuration;
    animator.SetTrigger("Attack");
}
  游戏开发 最新文章
6、英飞凌-AURIX-TC3XX: PWM实验之使用 GT
泛型自动装箱
CubeMax添加Rtthread操作系统 组件STM32F10
python多线程编程:如何优雅地关闭线程
数据类型隐式转换导致的阻塞
WebAPi实现多文件上传,并附带参数
from origin ‘null‘ has been blocked by
UE4 蓝图调用C++函数(附带项目工程)
Unity学习笔记(一)结构体的简单理解与应用
【Memory As a Programming Concept in C a
上一篇文章      下一篇文章      查看所有文章
加:2021-11-25 08:26:52  更:2021-11-25 08:27:54 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/27 23:53:34-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码