Unity官方Animator经典学习范例MecanimGDC2013学习笔记
一.准备
想做动作表现的游戏,就需要多了解Mecanim。Mecanim最近一直没有跟新,只是后来加了个额外的Playable。因此看这个示例并不过时,应该能比较全面的了解。
(1)把官方关于Mecanim的文档先大概看一遍
https://docs.unity.cn/cn/current/Manual/AnimationOverview.html
(2)去b站看下这个教学视频对Mecanim有个初步的了解
https://www.bilibili.com/video/BV1Mx411d7CY?p=11&spm_id_from=pageDriverhttps://www.bilibili.com/video/BV1Mx411d7CY?p=11&spm_id_from=pageDriver
(3)去AssetStore下载MecanimGDC2013
二 Animator相关
1.Animator.MatchTarget
在Action.cs中处理在地上滑动(slider)和跳过障碍(vault)时用到。这个函数主要是解决在某些动画在执行时候会穿模的问题。相当于提供Unity提供这样一个函数来让程序根据情况来自己调整
public void MatchTarget (Vector3 matchPosition, Quaternion matchRotation, AvatarTarget targetBodyPart, MatchTargetWeightMask weightMask, float startNormalizedTime, float targetNormalizedTime= 1);
第一,二个参数值要匹配的位置和旋转,第三个参数可以指定部位,第四个参数名的是XXWeightMask,如名,又是mask又是weight,分别是针对位置的x,y,z和旋转,实际上是4个权重。
最后两个参数,他是可以指定到动画中某一段时间的,这个需要自己预先和美术的同学沟通好。
推荐几篇文章给大家
http://www.manongjc.com/article/112632.html
https://www.cnblogs.com/chongxin/p/4104441.html
https://blog.csdn.net/qq_27361571/article/details/53307366
2.Animator.SetFloat(int id, float value, float dampTime, float deltaTime)
这是一系列函数,修改事先定义好的参数,我只是拿这个函数举例。大家见的比较多的是
整形参数 = Animator.StringToHash("参数名称");
然后Animator.SetFloat(int id, float value)这类参数。就是三个参数的SetXX,那后面两个参数是什么呢?
官方文档上对后面两个参数的解释
dampTime | 阻尼器总时间。 |
deltaTime | 给予阻尼器的增量时间。 |
看来是这个参数设置也可以有一个过程。
示例中Locomotion.cs是这么用的
m_Animator.SetFloat(m_SpeedId, speed, speedDampTime, Time.deltaTime);m_Animator.SetFloat(m_AgularSpeedId, angularSpeed, angularSpeedDampTime, Time.deltaTime);m_Animator.SetFloat(m_DirectionId, direction, directionDampTime, Time.deltaTime);
但是Animator.SetBool是没有后面两个参数的版本的。为什么? 因为所谓有个过程,其实是做插值,并不是一下就设置成目标值。那bool的插值是什么?bool应该是没有插值。
3.通过Locomotion.cs了解几个API
AnimatorStateInfo state = m_Animator.GetCurrentAnimatorStateInfo(0);bool inTransition = m_Animator.IsInTransition(0);bool inIdle = state.IsName("Locomotion.Idle");bool inTurn = state.IsName("Locomotion.TurnOnSpot");bool inRun = state.IsName("Locomotion.Run");
Animator.GetCurrentAnimatorStateInfo(0);获取指定层的动画信息,这个0就是第一层
Animator.IsInTransition(0) 是否在指定层上转换过度,就是一个动画状态机中从一个状态转到另一个状态的过程中
state.IsName("Locomotion.Idle"); 是否在执行动画状态机的某状态,就是是否在执行如果没有混合树的话就是某动画。
4.MonoBehaviour.OnAnimatorMove()
这个函数的作用是如果你想用动画的跟运动,但根运动中又没有你想要的移动方式的情况下去处理移动。比如跑步这个动作中,有轻微的旋转(这是你想要的),然后你要在自己的代码中加上移动。就写在这个函数中。因为只要你用了根运动,就没有直接进行位移操作。
这个官网文档
https://docs.unity3d.com/cn/current/ScriptReference/MonoBehaviour.OnAnimatorMove.html
这里有其他网友遇到的一个坑
https://blog.csdn.net/qq_36584063/article/details/78380394
在这个示例中玩家(perfab Player)是用的跟运动,但OnAnimatorMove是空的。npc(小熊prefab NPC)是用了这个的NPC_ShootPlayer.cs
5.IK
简单概括IK就是子节点驱动父节点的一种动画方式,而一般的动画都是前向动画,是父节点驱动子节点。IK的需求出发点是根据目标位置动态调整动画,而这个目标位置常常是未知的,或者说是动态的。其过程是一个解方程的过程。不过Unity已经给我们做好了,并且IK需要动画师那边要预先对美术资源进行处理,也就是动画在美术那边制作的时候也要预先支持IK.
可以推荐这两篇
https://blog.csdn.net/huang9012/article/details/18183437
https://blog.csdn.net/zhenghongzhi6/article/details/82851617
在Unity中除了要Layer的设置勾选上IK Pass之外,主要要处理有几对API
Animator.SetIKPositionWeight
Animator.SetIKPosition
Animator.bodyPosition
Animator.SetIKRotationWeight
Animator.SetIKRotation
Animator.bodyRotation
Animator.SetLookAtWeight
Animator.SetLookAtPosition
这些API一般都写在MonoBehaviour的OnAnimatorIK(int layerIndex)函数中,具体看这几个API的说明文档。我看SetIKXXWeght倒不是强制的,但SetIKXX应该是强制的。这里的权重值是控制的模型到目标的过度,这也是API给大家开放的自由度。在Mecanim中,权重应该会多次提到。
IK的旋转(SetIKRotation)和位置(SetIKPosition)支持左右手,脚四个部位(见系统定义的AvatarIKGoal)。
//// 摘要:// IK Goal.public enum AvatarIKGoal{//// 摘要:// The left foot.LeftFoot = 0,//// 摘要:// The right foot.RightFoot = 1,//// 摘要:// The left hand.LeftHand = 2,//// 摘要:// The right hand.RightHand = 3}
SetLookAtPosition支持头,身体,眼睛三个部位。SetLookAtPosition本质上也是一种旋转的处理,主要还是还是需求。在本示例中LookAhead.cs和Bazooka.cs中都有OnAnimatorIK的处理。
SetLookAtWeight的前四个参数都是权重值,第一个是全局权重,后面三个是头,身体,眼睛的权重,最后一个参数是(0-1) 0.0 表示角色在运动时完全不受限制,1.0 表示角色被完全固定(无法执行 LookAt),0.5 表示角色能够移动可能范围的一半(180 度)。
Mecanim确实有很多权重的概念,我的理解是Animator的设计目标(或者说存在的价值)之于Animation绝不仅仅是多了动画状态机的概念。这里有很多动画融合的概念,很多API都有权重的参数,我估计其内部实现是去做各种插值,为的是更流畅平滑的动画。
public void SetLookAtWeight (float weight, float bodyWeight= 0.0f, float headWeight= 1.0f, float eyesWeight= 0.0f, float clampWeight= 0.5f);
这里贴上示例中的代码。首先可以通过OnAnimatorIK的layer参数是可以指定层的,再次可以通过Animator.GetCurrentAnimatorStateInfo(a).IsName(b) 更具体的针对某一个状态。这个代码是在HoldLog时旋转的位置的权重都是1,也就是全过度到目标。
那其实我们也可以写程只在某状态中设置。
void OnAnimatorIK(int layerIndex){if(!enabled) return;if (layerIndex == 2) // do the log holding on the last layer, since LookAt is done in previous layer{float ikWeight = m_Animator.GetCurrentAnimatorStateInfo(2).IsName("HoldLog.HoldLog") ? 1 : 0;if (leftHandle != null){m_Animator.SetIKPosition(AvatarIKGoal.LeftHand, leftHandle.transform.position);m_Animator.SetIKRotation(AvatarIKGoal.LeftHand, leftHandle.transform.rotation);m_Animator.SetIKPositionWeight(AvatarIKGoal.LeftHand, ikWeight);m_Animator.SetIKRotationWeight(AvatarIKGoal.LeftHand, ikWeight);}if (rightHandle != null){m_Animator.SetIKPosition(AvatarIKGoal.RightHand, rightHandle.transform.position);m_Animator.SetIKRotation(AvatarIKGoal.RightHand, rightHandle.transform.rotation);m_Animator.SetIKPositionWeight(AvatarIKGoal.RightHand, ikWeight);m_Animator.SetIKRotationWeight(AvatarIKGoal.RightHand, ikWeight);}}}
其实就需求来说,IK与MatchTarget有相同的地方,都是要根据目标来自身模型Avatar的指定部位,指定权重。其实就达到的效果来说IK和MarchTarget是类似的,但内部实现的方式不一样。
MatchTarget实现从一段时间区域内,一个点到另一个点匹配的过程【其中一个点可以是人物模型的枚举】;IK动画用于直接将手或脚与某点的匹配【没有过 程这一说】
6.分层(Animation Layers)
Animator实际上是一个状态机的实现,并且是一个可视化的编辑。根据状态机的理论,在同一时刻只能执行一个状态机中的一个状态。如果要用状态机实现同一个时刻执行多个状态怎么办?答案就是多层状态机。而Animator的分层就是多层状态机。当然也可以不用状态机去用行为树,有机会我会写一些关于状态机和行为树的文章。那回到Animator,如果我们想实现跑步中开枪(跑步和开枪在同一时刻,不是跑着停下来开枪),这就可以通过分层来实现。同样,也能通过权重指定两层之间的混合程度,也能指定层上影响身体的部位。比如开枪这一层,角色身体的下半部分其实是无关的。类似的还有跑步中招手,招手时与下半身无关。
关于分层可以看看我另外一篇文章https://blog.csdn.net/wuming2016/article/details/88395907
这里还有一篇网友的 https://www.cnblogs.com/hammerc/p/4832637.html
7.混合树(Blend Tree)
混合树是指在同一个状态下,将多个动画片段融合在一起。比如,当前有一个动画是向跑步的动画,还有两个动画分别是向左跑的动画和向右跑的动画。当角色的运动方向是向前是,就是跑步,当运动方向是向左边,或者向右边时,这时就需要一个融合过程。因为并不是一瞬间方向就突然变化的。而美术这边只给三个动画(其实两个就可以,左右可以只做一个,另一个用镜像),中间的过渡的效果,就需要通过混合树来实现。
在这个例子中,方向是混合树的参数,也是融合的依据。可以给Animator定义一个Float 方向参数Dir,0代表正方向,1代表完全向左,-1代表完全向右。那就可以通过脚本动态改变Dir的值来控制混合树。还记得上面讲的带5个参数的Animator.SetFloat吗?
这就是1D混合树,就是只需要一个参数来控制混合树。
那什么是2D混合树呢?我们再拿上面的例子举例,假设有向后跑的情况,也增加了往左后和往右后两个动画。那这时就需要两个方向参数来控制融合树,一个参数表示向左或者向右,一个参数表示向前或者向后。2D混合树就是用两个参数来控制的混合树。
那有没有3D,4D。。。。的混合树,就是超过2个控制参数以上的混合树呢?答案是有。在Animator里称为直接混合(Direct Blending)。
需要注意的是,混合树中的动画动画长度需要一致,动画的起始姿势需要一致。
也推荐两篇文章
https://blog.csdn.net/Zjl956321111/article/details/107027296
https://www.cnblogs.com/SHOR/p/5764014.html
8.复写(override)重定向(Retargeting )
两个都是重用动画控制器的。前者类似于程序中的继承,然后再改写不一样的部分。主要用在动画控制器的逻辑完全相同,但用的动画片段不一样的情况。
而重定向是只模型不一样,但动画控制器和动画片段都一样的情况。
9.录制
不知道为什么,现在官网文档的动画章节已经找不到相关介绍了,API里还有,这个示例也有相关代码。以下是我自己的理解
(1)Animator其实是有两个工作模式,一个模式就是我们平时常用的模式,状态机模式,另一个模式就是播放模式,在这个模式下状态机其实是不运转的,只负责播放录制的画面。怎么没有录制模式?其实录制是在状态机模式下开的一个功能。
(2)录制既然做在Animator上,那显然是以挂了Animator的角色为主体进行录制,而不是放在相机上。
(3)虽然录制的结果不能做持久化存储,但就这个功能还是很有用的。比如RTS游戏中做即刻回放功能。当然这只是一种方案,比如还有其他的类似于帧同步的概念记住输入,再运行一遍。
(4)能录多久?这个是以帧为单位的,可以设置,也可以通过脚本控制循环录制。事实上示例就是循环录制。
开始录制
Animator进入状态机模式,
Animator开始录制并设置录制缓冲的帧数
m_Animator.StopPlayback();m_Animator.StartRecording(FrameCount);
停止录制
Animator停止录制
Animator进入播放模式
m_Animator.StopRecording();m_Animator.StartPlayback();
播放录制
播放实际上就是指定Animator在录制好的缓冲区中的帧数。所以这个播放的速度完全由我们自己控制。在这个示例中做了快进,快退,也做了拉滑动条的方式进行播放。
下面是几个相关的API.其中Animator.playbackTime就是指定帧数。
// Sets the playback position in the recording buffer.public float playbackTime { get; set; }// Start time of the first frame of the buffer relative to the frame at which StartRecording// was called.public float recorderStartTime { get; set; }// End time of the recorded clip relative to when StartRecording was called.public float recorderStopTime { get; set; }
关于这个部分,示例是实现在Recorder.cs中的。
也推荐文章
https://blog.csdn.net/dongfushu7972/article/details/102281146
10.其他还不错的文章
https://blog.csdn.net/tanyu159/article/details/83477587
二 其他
1.JoystickToWorld.cs
public class JoystickToWorld
{ public static Vector3 ConvertJoystickToWorldSpace (){ Vector3 direction; float horizontal = Input.GetAxis ("Horizontal");float vertical = Input.GetAxis ("Vertical"); Vector3 stickDirection = new Vector3 (horizontal, 0, vertical); direction = Camera.main.transform.rotation * stickDirection; // Converts joystick input in Worldspace coordinatesdirection.y = 0; // Kill Zdirection.Normalize (); return direction;}public static void ComputeSpeedDirection (Transform root, ref float speed, ref float direction){Vector3 worldDirection = ConvertJoystickToWorldSpace ();speed = Mathf.Clamp (worldDirection.magnitude, 0, 1);if (speed > 0.01f) { // dead zoneVector3 axis = Vector3.Cross (root.forward, worldDirection);direction = Vector3.Angle (root.forward, worldDirection) / 180.0f * (axis.y < 0 ? -1 : 1);} else {direction = 0.0f; }}
}
第一个函数是把键盘或者游戏杆的水平方向和垂直方向(其实x,y,z中的z)。
示例中用的是
float horizontal = Input.GetAxis ("Horizontal");
float vertical = Input.GetAxis ("Vertical");|
其实还有个鼠标版本
float h = horizontalSpeed * Input.GetAxis("Mouse X");
float v = verticalSpeed * Input.GetAxis("Mouse Y");
关于Input.GetAxis函数参考
https://docs.unity.cn/cn/current/ScriptReference/Input.GetAxis.html
关于坐标转换
Vector3 stickDirection = new Vector3 (horizontal, 0, vertical);
direction = Camera.main.transform.rotation * stickDirection; // Converts joystick input in Worldspace coordinates
这里的转世界座位为什么是用相机的旋转?其实通过相机在世界坐标的选择值转到世界坐标。
求方向 direction
先说明下这个方向算出来是弧度
speed = Mathf.Clamp (worldDirection.magnitude, 0, 1);
if (speed > 0.01f) { // dead zone
Vector3 axis = Vector3.Cross (root.forward, worldDirection);
direction = Vector3.Angle (root.forward, worldDirection) / 180.0f * (axis.y < 0 ? -1 : 1);
} else {
direction = 0.0f;
}
如果worldDirection.magnitude特别小,小于0.01就认为没有旋转
至于Vector3.Cross (root.forward, worldDirection),可以记住,点乘法能算出两个向量的的0~180,但不能算出0~360,只有通过叉乘后的y的正负来辅助判断。Vector3.Angle我估计内部是实现是点乘,然后通过反cos函数就是弧度。
2 ThirdPersonCamera.cs
这个一个指定跟踪对象的相机跟随。
public class ThirdPersonCamera : MonoBehaviour
{public float distanceAway; public float distanceUp; public float smooth; // how smooth the camera movement isprivate Vector3 m_TargetPosition; // the position the camera is trying to be in)Transform follow;void Start(){follow = GameObject.FindWithTag ("Player").transform; }void LateUpdate (){// setting the target position to be the correct offset from the m_TargetPosition = follow.position + Vector3.up * distanceUp - follow.forward * distanceAway;// making a smooth transition between it's current position and the position it wants to be intransform.position = Vector3.Lerp(transform.position, m_TargetPosition, Time.deltaTime * smooth);// make sure the camera is looking the right way!transform.LookAt(follow);}
}
Unity官方Animator经典学习范例MecanimGDC2013学习笔记相关推荐
- Unity官方教程Ruby大冒险的自学笔记
Unity官方教程Ruby大冒险的自学笔记 一. //正确例子: void Update(){//获取运动矢量moveX = Input.GetAxisRaw("Horizontal&quo ...
- unity官方换装教程Character Customization 学习笔记
1. 下载示例demo,可以直接从AssetsStore上下载,但是速度比较慢,我在github上找了一个据说支持unity5.x的. 链接:https://github.com/spacebeagl ...
- Unity官方案例之星际航行游戏(Space Shooter)学习总结
这几天我学习了<Unity官方案例精讲>的Space Shooter部分,这个案例作为刚刚学习Unity的入门还是不错的,这是整个案例的代码. 下面对我觉得比较常见的几个用法进行一下总结. ...
- Android开发的经典入门教材和学习…
Android开发的经典入门教材和学习路线? 1.想利用寒假期间学习Android开发,了解到应该先学习Java,不知道选哪本书入门,学习Java和Android有什么经典教材,适合初学者.(有C++ ...
- 【初阶】unity3d官方案例_太空射击SpacingShooter 学习笔记 显示分数时,如何让函数之间相互交流...
[初阶]unity3d官方案例_太空射击SpacingShooter 学习笔记 显示分数时,如何让函数之间相互交流 一.关于 显示分数时,如何让函数之间相互交流 这是一个非常好的逻辑问题 1 思路:主 ...
- 【经典回放】JavaScript学习详细干货笔记之(一)
[经典回放]JavaScript学习详细干货笔记之(一) [经典回放]JavaScript学习详细干货笔记之(二) [经典回放]JavaScript学习详细干货笔记之(三) 目录 一.为什么要学Jav ...
- 【深度学习】深度学习和经典统计学是一回事?
器之心编译 编辑:rome rome 深度学习和简单的统计学是一回事吗?很多人可能都有这个疑问,毕竟二者连术语都有很多相似的地方.在这篇文章中,理论计算机科学家.哈佛大学知名教授 Boaz Barak ...
- Unity红球吃绿球强化学习小任务——Ubuntu20.04系统于2022年2月26日实现
Unity红球吃绿球强化学习小任务--Ubuntu20.04系统于2022年2月26日实现 Unity红球吃绿球强化学习小任务 一.主机环境参数说明 二.具体教程以之前提供的视频介绍为主,需要修改的章 ...
- 《异常检测——从经典算法到深度学习》9 异常检测资料汇总(持续更新抛砖引玉)
<异常检测--从经典算法到深度学习> 0 概论 1 基于隔离森林的异常检测算法 2 基于LOF的异常检测算法 3 基于One-Class SVM的异常检测算法 4 基于高斯概率密度异常检测 ...
最新文章
- QML从右到左的用户界面
- Scala的Higher-Kinded类型
- 光通量发光强度照度亮度关系_技术丨LED电子显示屏真实的亮度指数鉴别
- 这次,让算法走下神坛!
- 雷电3菊链功能_同轴科技推出5款USB-C全功能数据线,清一色内置同轴线缆
- [Android] charles高级使用总结
- 四川首例 “自贡话智能语音识别系统”在检察院投用
- 番茄花园 Ghost XP SP3 极速装机版 V2013.05
- sqoop导入/导出
- 移远ec20 openLinux交叉编译python
- 超级安全!Python 合成多张图片到PDF格式
- IEEE 1588和IEEE 802.1AS的版本和关系
- 【MySQL】--数据库锁机制
- traceroute/tracert原理
- Delta并联机构在ADAMS仿真中的运动副设置
- 用Ventoy同时引导ubuntu/centos/windows多系统安装
- uni-app开发安卓APP运行到真机,未检测到手机或模拟器
- tomcat启动:startup.sh、catalina.sh、setclasspath.sh三者关系
- 常用数据库的特点、应用场景信息整理
- C++ STL源码剖析 笔记