第三十四章 Unity人形动画(上)
在我们DirectX课程中,我们讲过一个模型最少拥有网格和材质,可以没有动画。游戏场景中的静态物体就可以是这样的模型,例如花草树木,建筑物等等,他们通过MeshRenderer就可以渲染。对于一个带有动画的FBX文件,里面不仅仅包含了网格和材质,还包括了“骨架”和动画数据,这里的动画数据其实就是骨架中每块骨骼的移动和旋转变换数据。这里大家一定要分清骨架和骨骼两个概念哦。这样的模型需要使用SkinMeshRenderer才可以渲染。我们知道,骨骼动画就是骨骼变换控制网格顶点变换形成的,两者是通过“蒙皮”产生的关联关系,“蒙皮”的过程就是骨骼对顶点影响的权重设置。这里面涉及到了两种数据,一种是网格和材质,另一种就是骨骼动画。前者是差异化的,每个模型肯定都是不一样的;而后者有可能共享通用的,每个模型可能会有相同的动画。例如,两个人物模型,他们都应该有待机,走路,跑步等等通用的动画。那么,这些动画数据能不能独立成单独的文件,被其他模型使用呢?如果可以使用的话,我们就不需要为每一个模型制作动画了,节省一些不必要的时间了。答案,肯定是可以的。从数据角度出发,两个不同的模型,只要他们的“骨骼结构”(骨架)是一样的,那么基于该骨架的动画数据,就可以重用。这里的一样指的是骨骼的数量以及父子关系大致保持一致,名称不一样可以通过“映射”来统一。在3ds max中,就提供了一个完整的人形骨架,称之为“Biped”,如下所示:

也就是说,如果大家在给人物模型添加骨架的时候,都使用“Biped”的话,那么我们所有的基于“Biped”的动画就可以重用了。事实上,3ds max也是这样设计的,我们可以从网上下载到很多的基于“Biped”的动画数据。在Unity中,同样也拥有一套人形骨架,如下:

Unity的人形骨架与3ds max的人形骨骼大致相同。但是,大部分情况下,我们还需要将两者的骨骼进行一个“映射”,也就是“mapping”。映射的目的就是为了让Unity能够知道模型上面的那些骨骼对应Unity自己的头部,身体,胳膊以及腿脚信息等等。这个应该很容易理解。
Mecanim Example Scenes是由Unity发布的一款免费资源,旨在指导开发者如何使用Unity的Mecanim及角色动画系统,项目中包含11个示例场景,分别演示了动画状态机、混合树、集群模拟、多层次IK、目标匹配、处理武器、跟随、预测及导航网格集成等。

我们在Assets资源面板中导入上面的资源包。如下所示:


这个资源包并没有统一放置到一个独立的文件夹中,比较乱。我们发现在Assets根目录下有很多的场景文件,我们随便打开一个“Animator Controller.unity”查看一下。

当我们Play运行之后,就可以通过WASD键来控制里面的人物模型移动了,同时会有移动动画。当然,这个不是我们研究的内容。这个人物模型存放在 “Assets\Characters\U_Character”目录下,如下所示

在这个目录下,我们可以看到材质,贴图以及模型 “U_Character_REF.fbx”文件,查看它的Inspector检视视图。

我们注意到它的Animation Type一项为 Humanoid类型,也就是人形动画。关于这一项,我们之前已经介绍过Legacy旧版动画 和Generic新版动画,而Humanoid就是新版人形动画。我们继续点击“Animcation”选项面板,查看其下动画剪辑内容。

我们发现,这个模型文件并没有动画剪辑数据。我们重新创建一个新的“SampleScene3.unity”场景,然后将这个模型文件拖拽到Scene视图中,如下所示

我们在“Hierarchy”层次面板中查看该文件中有什么数据信息吧。

其中顶级“U_Character_REF”是我们整个游戏对象,它的下面有U,U_Char,U_Char_Eye_L,U_Char_Eye_R四个子对象,后面三个分别是人物主模型,人物左眼模型和人物右眼模型。那么第一个U对象是什么呢?U是一个父对象,它的下面有一个“joint_Char”的对象,这个对象就是“人形骨架”的根骨骼。我们上面已经说明过,动画数据是可以共享的,模型想要从外部获取动画的话,它本身必须拥有“人形骨架”,并且是被“蒙皮”处理过的。应该蒙皮处理过的模型需要“Skinned Mesh Renderer”组件才能渲染出来。我们点击U_Char查看Inspector检视视图,就能看到这个渲染组件,如下

在上面的“Root Bone”一项中,就能看到根骨骼就是“joint_Char”了。我们上面也提到了,这个模型是没有动画剪辑的(但是有骨骼信息),那么动画信息在哪里呢?
接下来,我们去“Assets\Animations”目录下查看一下,

我们会发现,有很多的动画片段,例如Idles,Run,Jump等等。这些专门的动作文件是怎么来的呢?他们也是可以通过3ds max导出来的,操作很简单,我们只需要在3ds max中选中“骨架”,然后导出FBX文件,再导出设置中只勾选“Animation”动画一项。我们还可以设置开始帧数和结束帧数。Untiy要求导出的文件名称格式为“模型名@动画名”。例如,我们可以给之前的“Elf”单独导出一个攻击的动画文件“Elf@Attact.FBX”,如下所示

导出“Elf@Attact.FBX”之后,我们将其复制到“Assets/Elf2”目录下查看

我们可以点击这个动画,在Inspector中的Animation选项卡中查看这个动画效果。

有兴趣的同学,可以将这个动画片段添加到“动画控制器”中进行编辑。接下来,我们继续查看“U_Character_REF”模型对应的动画,例如“Run”动画,效果如下

接下来,我们就使用Idles,Run,Jump这三个动画来演示一下吧。首先,我们在“Hierarchy”层次视图中选中“U_Character_REF”这个游戏对象,查看它的Inspector检视视图。如果没有“Animator”组件的话,我们就给它添加一个,如下所示

毫无疑问,我们需要先创建一个动画控制器。在Asset视图空白处,右击选择“Create”-》“Animator Controller”,将其命名为“U_Character_Animator_Controller”

接下来,我们双击这个文件,打开“Animator”窗口,如下

然后,我们将上面提到的Idles,Run,Jump三个动画文件拖拽进来。

默认是Idle动画,这个问题不大。接下来,我们需要链接Idle到Run动画。我们在“Idle”动画状态上面右击选择“Make Transition”,然后移动鼠标到“Run”动画状态上面点击完成。然后我们继续链接“Run”到“Jump”,如下所示

接下来,我们在创建一个“动画参数”,这次我们创建一个Int类型的“run_jump”变量

接下来,我们点击“Idle”到“Run”之间的连线,并在Inspector检视视图中编辑

这次我们选择的操作符是“Equals”等于操作符,对应的数值是1
然后我们点击“Run”到“Jump”之间的连线,设置操作数值为 2

这样的话,当我们设置“run_jump”为数值1的时候,我们的游戏角色就会从idle状态过渡到run状态,然后设置为数值2的时候,就会从run状态过渡到Jump状态。这个动画控制器设置完毕后,不要忘了将它拖拽到“Animator”组件的“Controller”栏中去。

在上图中,我们还有一个小问题要调整。就是取消“Apply Root Motion”勾选项。它的意思是说,当我们播放动画的时候,人物模型不会产生移动,也就是原地播放动画。接下来,我们来创建一个C#脚本,命名为“U_Character_Animator_Script.cs”,内容如下所示
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class U_Character_Animator_Script : MonoBehaviour
{// 动画播放组件private Animator animator;// Start is called before the first frame updatevoid Start(){// 获取Animator组件animator = GetComponent<Animator>();}// Update is called once per framevoid Update(){if (Input.GetKeyDown(KeyCode.W)){animator.SetInteger("run_jump", 1);}if (Input.GetKeyDown(KeyCode.J)){animator.SetInteger("run_jump", 2);}}
}
以上的代码非常简单,我们就不再过多的解释了。我们将脚本附加到人物角色上后运行工程。

运行工程之后,我们就发现了几个问题。首先,工程运行后自动播放idle动画,这个没有问题。但是,必须等idle动画播放完毕后,我们才能按下W键进入跑步动画,紧接着我们继续按下J键播放jump动作。如果不想等待idle动画播放完毕就能直接进入跑步动画的,可以点击“idle”到“run”之间的过渡线,然后取消掉“Has Exit Time”选项,如下所示

最后,人物模型就静止不动了。因为jump动画不是循环播放,且此时run_jump的数值等于2。如果想让人物模型继续进入idle动画或者run动画的话,我们需要重新设置run_jump的数值了。因此,这里的动画切换逻辑需要重新设计一下。例如,当我们按下W键的时候,设置run_jump数值为1,当按起的时候,设置run_jump数值为2。这里我们就不再演示了。
本课程涉及的内容已经共享到百度网盘:https://pan.baidu.com/s/1e1jClK3MnN66GlxBmqoJWA?pwd=b2id
相关文章:
第三十四章 Unity人形动画(上)
在我们DirectX课程中,我们讲过一个模型最少拥有网格和材质,可以没有动画。游戏场景中的静态物体就可以是这样的模型,例如花草树木,建筑物等等,他们通过MeshRenderer就可以渲染。对于一个带有动画的FBX文件,…...
计算机图形学-GAMES101-7
引言 场景中有很多的三角形,如果实现可见性和遮挡呢? 一个简单的想法是,从远到近画,近处的物体自然会覆盖掉远处的物体,这种画法也叫画家算法。 但是实际绘制中物体的顺序是不容易确定的,比如如下图绘制…...
AndroidAuto 解决PCTS NF7
直接上代码 public void handleNavigationFocusRequest(int focusType) {// Always grant requested focus in this example.-mGal.galReceiver.sendNavigationFocusState(focusType);+mGal.galReceiver.sendNavigationFocusState...
GPT:你知道这五年我怎么过的么?
时间轴 GPT 首先最初版的GPT,来源于论文Improving Language Understanding by Generative Pre-Training(翻译过来就是:使用通用的预训练来提升语言的理解能力)。GPT这个名字其实并没有在论文中提到过,后人将论文名最后…...
Python一行命令搭建HTTP服务器并外网访问 - 内网穿透
文章目录 1.前言2.本地http服务器搭建2.1.Python的安装和设置2.2.Python服务器设置和测试 3.cpolar的安装和注册3.1 Cpolar云端设置3.2 Cpolar本地设置 4.公网访问测试5.结语 转载自远程内网穿透的文章:【Python】快速简单搭建HTTP服务器并公网访问「cpolar内网穿透…...
TypeScript5-泛型
泛型是 TS 中一个重要的概念,它可以创建可复用的组件,同时保持对类型信息的一致性。 泛型提供了一种方式使得类型可以被参数化,这样就可以创建可以适用于各种数据类型的函数或类,而不仅仅限于一个数据类型。 一、泛型 先来看一…...
IMX6ULL裸机篇之DDR3的时钟配置
一. MMDC 控制器 对于 I.MX6U 来说,有 DDR 内存控制器,否则的话它怎么连接 DDR 呢?MMDC控制器 就是 I.MX6U 的 DDR内存控制器。 MMDC 外设包含一个内核(MMDC_CORE)和 PHY(MMDC_PHY),内核和 PHY 的功能如下: MMDC 内…...
PBDB Data Service:Specimens and measurements(标本和测量)
Specimens and measurements(标本和测量) 描述摘要1. [Single specimen(单个标本)](https://blog.csdn.net/whitedrogen/article/details/130685099)2. [Add specimen records or update existing records(添加标本记录…...
Zookeeper(一)
简介 设计模式角度 Zookeeper:是一个基于观察者模式设计的分布式服务管理框架,它负责存储和管理大家都关心的数据,然后接受观察者的注册,一旦这些数据的状态发生变化,Zookeeper就将负责通知已经在Zookeeper上注册的那…...
Maven(五):Maven的使用——依赖的测试
Maven(五):Maven的使用——依赖的测试 前言一、实验六:测试依赖的范围1、依赖范围1.1 compile 和 test 对比1.2 compile 和 provided 对比1.3 结论 二、实验七:测试依赖的传递性1、依赖的传递性1.1 概念1.2 传递的原则…...
超级独角兽 Databricks 的崛起之路
在数据扩张以及 AI 兴起的时代,数据存储和分析平台拥有巨大价值和能量。 随着互联网数据的爆炸性增长,数据已经成为企业的新型资源,犹如石油般重要。越来越多的企业希望利用各种结构化和非结构化数据来发挥自己的优势。 然而,他…...
python 3.8 + tensorflow 2.4.0 + cuda11.0 的问题
版本匹配 🔗从源代码构建 | TensorFlow 报错:Could not load dynamic library ‘cupti64_110.dll’; dlerror: cupti64_110.dll not found 是因为我电脑中的 cuda 版本以前是 10,现在是 11.4 ,所以需要安装对应版本的 cudatoolk…...
华为杯”研究生数学建模竞赛2021 年中国研究生数学建模竞赛 E 题: 信号干扰下的超宽带(UWB)精确定位问题-参考思路
一、背景 UWB ( Ultra-Wideband )技术也被称之为“超宽带”,又称之为脉冲无线电技术。这是一 种无需任何载波,通过发送纳秒级脉冲而完成数据传输的短距离范围内无线通信技术,并且信 号传输过程中的功耗仅仅有几十 W 。 UWB 因其独有的特点,使其在军事、物联网等各个领…...
Java 中的访问修饰符有什么区别?
Java 中的访问修饰符用于控制类、类的成员变量和方法的访问权限,主要有以下四种: public:公共访问修饰符,可以被任何类访问。public 修饰的类、成员变量和方法可以在任何地方被访问到。 protected:受保护的访问修饰符…...
Go基础篇:接口
目录 前言✨一、什么是接口?二、空接口 interface{}1、eface的定义2、需要注意的问题 三、非空接口1、iface的定义2、itab的定义3、itab缓存 前言✨ 前段时间忙着春招面试,现在也算告一段落,找到一家比较心仪的公司实习,开始慢慢回…...
边缘计算:数字时代的新战场
随着数字化时代的到来,云计算已经成为了各行各业不可或缺的技术支持。但是,由于云计算涉及到数据的传输和存储,对于网络带宽和延迟的要求也非常高,这使得云计算难以满足一些低延迟、高实时性要求的场景。在这种情况下,…...
PBDB Data Service:Fossil occurrences(化石产出记录)
Fossil occurrences(化石产出记录) 描述摘要1. [Single fossil occurrence(单条化石产出记录)](https://blog.csdn.net/whitedrogen/article/details/130519180)2. [List of fossil occurrences(化石产出记录列表&…...
虾皮Shopee商品详情接口(item_get-根据ID取商品详情)代码封装
item_get-根据ID取商品详情接口 通过代码封装该接口可以拿到商品标题,商品价格,商品促销信息,商品优惠价,商品库存,sku属性,商品图片,desc图片,desc描述,sku图片…...
原生js手动实现一个多级树状菜单效果(高度可过渡变化) + 模拟el-menu组件实现(简单版)
文章目录 学习链接效果图代码要点 简单模拟el-menu实现TestTree.vueMenu.vueSubMenu.vue 学习链接 vue实现折叠展开收缩动画 - 自己的链接 elment-ui/plus不定高度容器收缩折叠动画组件 - 自己的链接 vue的过渡与动画理解 Vue transition 折叠类动画自动获取隐藏层高度以及…...
RK3568平台开发系列讲解(Linux内存篇)Linux内存管理框架
🚀返回专栏总目录 文章目录 一、内核态内存分配二、用户态内存分配三、内存篇章更新哪些内容沉淀、分享、成长,让自己和他人都能有所收获!😄 📢本篇我们一起将整个内存管理的体系串起来。 对于内存的分配需求,可能来自内核态,也可能来自用户态。 一、内核态内存分配…...
观成科技:隐蔽隧道工具Ligolo-ng加密流量分析
1.工具介绍 Ligolo-ng是一款由go编写的高效隧道工具,该工具基于TUN接口实现其功能,利用反向TCP/TLS连接建立一条隐蔽的通信信道,支持使用Let’s Encrypt自动生成证书。Ligolo-ng的通信隐蔽性体现在其支持多种连接方式,适应复杂网…...
全球首个30米分辨率湿地数据集(2000—2022)
数据简介 今天我们分享的数据是全球30米分辨率湿地数据集,包含8种湿地亚类,该数据以0.5X0.5的瓦片存储,我们整理了所有属于中国的瓦片名称与其对应省份,方便大家研究使用。 该数据集作为全球首个30米分辨率、覆盖2000–2022年时间…...
测试markdown--肇兴
day1: 1、去程:7:04 --11:32高铁 高铁右转上售票大厅2楼,穿过候车厅下一楼,上大巴车 ¥10/人 **2、到达:**12点多到达寨子,买门票,美团/抖音:¥78人 3、中饭&a…...
vue3 定时器-定义全局方法 vue+ts
1.创建ts文件 路径:src/utils/timer.ts 完整代码: import { onUnmounted } from vuetype TimerCallback (...args: any[]) > voidexport function useGlobalTimer() {const timers: Map<number, NodeJS.Timeout> new Map()// 创建定时器con…...
JUC笔记(上)-复习 涉及死锁 volatile synchronized CAS 原子操作
一、上下文切换 即使单核CPU也可以进行多线程执行代码,CPU会给每个线程分配CPU时间片来实现这个机制。时间片非常短,所以CPU会不断地切换线程执行,从而让我们感觉多个线程是同时执行的。时间片一般是十几毫秒(ms)。通过时间片分配算法执行。…...
HubSpot推出与ChatGPT的深度集成引发兴奋与担忧
上周三,HubSpot宣布已构建与ChatGPT的深度集成,这一消息在HubSpot用户和营销技术观察者中引发了极大的兴奋,但同时也存在一些关于数据安全的担忧。 许多网络声音声称,这对SaaS应用程序和人工智能而言是一场范式转变。 但向任何技…...
软件工程 期末复习
瀑布模型:计划 螺旋模型:风险低 原型模型: 用户反馈 喷泉模型:代码复用 高内聚 低耦合:模块内部功能紧密 模块之间依赖程度小 高内聚:指的是一个模块内部的功能应该紧密相关。换句话说,一个模块应当只实现单一的功能…...
xmind转换为markdown
文章目录 解锁思维导图新姿势:将XMind转为结构化Markdown 一、认识Xmind结构二、核心转换流程详解1.解压XMind文件(ZIP处理)2.解析JSON数据结构3:递归转换树形结构4:Markdown层级生成逻辑 三、完整代码 解锁思维导图新…...
RushDB开源程序 是现代应用程序和 AI 的即时数据库。建立在 Neo4j 之上
一、软件介绍 文末提供程序和源码下载 RushDB 改变了您处理图形数据的方式 — 不需要 Schema,不需要复杂的查询,只需推送数据即可。 二、Key Features ✨ 主要特点 Instant Setup: Be productive in seconds, not days 即时设置 :在几秒钟…...
C#最佳实践:为何优先使用as或is而非强制转换
C#最佳实践:为何优先使用as或is而非强制转换 在 C# 的编程世界里,类型转换是我们经常会遇到的操作。就像在现实生活中,我们可能需要把不同形状的物品重新整理归类一样,在代码里,我们也常常需要将一个数据类型转换为另…...
