一.准备
想做动作表现的游戏,就需要多了解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学习笔记相关推荐

  1. Unity官方教程Ruby大冒险的自学笔记

    Unity官方教程Ruby大冒险的自学笔记 一. //正确例子: void Update(){//获取运动矢量moveX = Input.GetAxisRaw("Horizontal&quo ...

  2. unity官方换装教程Character Customization 学习笔记

    1. 下载示例demo,可以直接从AssetsStore上下载,但是速度比较慢,我在github上找了一个据说支持unity5.x的. 链接:https://github.com/spacebeagl ...

  3. Unity官方案例之星际航行游戏(Space Shooter)学习总结

    这几天我学习了<Unity官方案例精讲>的Space Shooter部分,这个案例作为刚刚学习Unity的入门还是不错的,这是整个案例的代码. 下面对我觉得比较常见的几个用法进行一下总结. ...

  4. Android开发的经典入门教材和学习…

    Android开发的经典入门教材和学习路线? 1.想利用寒假期间学习Android开发,了解到应该先学习Java,不知道选哪本书入门,学习Java和Android有什么经典教材,适合初学者.(有C++ ...

  5. 【初阶】unity3d官方案例_太空射击SpacingShooter 学习笔记 显示分数时,如何让函数之间相互交流...

    [初阶]unity3d官方案例_太空射击SpacingShooter 学习笔记 显示分数时,如何让函数之间相互交流 一.关于 显示分数时,如何让函数之间相互交流 这是一个非常好的逻辑问题 1 思路:主 ...

  6. 【经典回放】JavaScript学习详细干货笔记之(一)

    [经典回放]JavaScript学习详细干货笔记之(一) [经典回放]JavaScript学习详细干货笔记之(二) [经典回放]JavaScript学习详细干货笔记之(三) 目录 一.为什么要学Jav ...

  7. 【深度学习】深度学习和经典统计学是一回事?

    器之心编译 编辑:rome rome 深度学习和简单的统计学是一回事吗?很多人可能都有这个疑问,毕竟二者连术语都有很多相似的地方.在这篇文章中,理论计算机科学家.哈佛大学知名教授 Boaz Barak ...

  8. Unity红球吃绿球强化学习小任务——Ubuntu20.04系统于2022年2月26日实现

    Unity红球吃绿球强化学习小任务--Ubuntu20.04系统于2022年2月26日实现 Unity红球吃绿球强化学习小任务 一.主机环境参数说明 二.具体教程以之前提供的视频介绍为主,需要修改的章 ...

  9. 《异常检测——从经典算法到深度学习》9 异常检测资料汇总(持续更新抛砖引玉)

    <异常检测--从经典算法到深度学习> 0 概论 1 基于隔离森林的异常检测算法 2 基于LOF的异常检测算法 3 基于One-Class SVM的异常检测算法 4 基于高斯概率密度异常检测 ...

最新文章

  1. QML从右到左的用户界面
  2. Scala的Higher-Kinded类型
  3. 光通量发光强度照度亮度关系_技术丨LED电子显示屏真实的亮度指数鉴别
  4. 这次,让算法走下神坛!
  5. 雷电3菊链功能_同轴科技推出5款USB-C全功能数据线,清一色内置同轴线缆
  6. [Android] charles高级使用总结
  7. 四川首例 “自贡话智能语音识别系统”在检察院投用
  8. 番茄花园 Ghost XP SP3 极速装机版 V2013.05
  9. sqoop导入/导出
  10. 移远ec20 openLinux交叉编译python
  11. 超级安全!Python 合成多张图片到PDF格式
  12. IEEE 1588和IEEE 802.1AS的版本和关系
  13. 【MySQL】--数据库锁机制
  14. traceroute/tracert原理
  15. Delta并联机构在ADAMS仿真中的运动副设置
  16. 用Ventoy同时引导ubuntu/centos/windows多系统安装
  17. uni-app开发安卓APP运行到真机,未检测到手机或模拟器
  18. tomcat启动:startup.sh、catalina.sh、setclasspath.sh三者关系
  19. 常用数据库的特点、应用场景信息整理
  20. C++ STL源码剖析 笔记

热门文章

  1. 【苹果推位置推iMessage】Apple Notification Center Service (ANCS)
  2. 四种Linux系统版本号的查看方式
  3. 从零开始搭建4G DTU设备对应的云平台(一)
  4. 云台山风景区国庆黄金周连续两天游客爆棚
  5. 搭建SAP HANA2.0学习环境
  6. Git上fork后的代码仓库如何与原仓库进行同步
  7. [Python]文件操作
  8. 千兆服务器网卡哪个型号好,千兆光纤网卡
  9. mysql ngram全文检索引擎
  10. 互联网行业业务申办指南和主管单位网站列表