前言:

最近开始跟着SIKI学院系统学习Unity,这篇文章就是Unity中的动画系统和Timeline的笔记


动画

动画的录制和动画曲线的编辑

以前我都是在动画中一步一步来做动画,从来不知道还有这个录制功能,厉害了

我们点击Animation编辑版中左上角的红点即可开始录制,录制状态中我们调整物体就可以自动创建帧。

另外我们还可以在Curves中编辑动画曲线,点击动画曲线的关键点我们还可以设置动画曲线变化的均匀程度

创建2D精灵动画

方法一:

  1. 直接将你的2D图片从Assets中多选住拖到场景中(它会自动创建一个同名的状态机并且一个拼接这些2D图片的短暂动画)
  2. 然后就可以在动画状态机中调整了。

方法二:

  1. 直接在Assets中创建动画状态机
  2. 把动画状态机给了场景中的2D物体,然后在该2D物体身上通过Ctrl+6创建动画
  3. 在动画中,通过sprite来编辑动画,可以直接将别的图片拖过来形成新的一帧。
  4. 然后就可以在动画状态机中调整了

3D模型

不同的建模软件会导出不同的模型,但是要记住.fbx是最支持Unity的格式。

一般我们的模型除了模型以外还会有材质和贴图,这两个东西就是模型的皮肤,非常重要。

导出的一般有两个方式:第一种是把动画和模型放在一起,第二种是将每一个动画导出成一个单独的文件(例如:xxxx@xxx.fbx 这种格式)

导入模型和解决材质、贴图丢失问题

首先要确保场景模型的材质球可以编辑,如果不能编辑怎么办?不要急,打开文件中的模型(是文件中,不是场景中!)然后在Inspector面板中的Materials下这样设置

use external materials(Legacy):使用外部材料(遗产)

use embeddedmaterials :使用嵌入材料

选好使用外部材料后,我们双击模型,找到他的网格渲染组件(Mesh Renderer)看看是否缺少材质球,如果不缺材质,那就是贴图的问题,在材质球编辑处添加适当的贴图即可。

指定好贴图后,我们可以改一下shader为标准,可以看得更真实,再加一下法线贴图也好。

指定好这些属性后及时你将场景中的模型删除了,文件中也已经指定好贴图了。

导入动画

Unity中导入的模型主要是由3DMAX、Maya等建模软件制作的,后缀为.fbx的文件。

点击文件后我们会发现关于动画导入的一些设置

Rig面板下:

我们的Animation type主要有如下选项:

Legacy:已经启用的导入方式(无法使用状态机)。
Humanoid:只能人形动画使用。
Generic:人形非人形都可以使用。

注意Humanoid和Generic使用状态动画机播放,不能使用Animation播放

后两者的区别:当有两个骨骼结构相同的模型时,其中一个有动画而另一个没有。就可以把两者都设置为Humanoid,没有动画的模型的Avatar Definition设为Copy From Other Avatar。赋值有动画模型的Avatar,就可以使用动画了。

我们来分别介绍一下Generic和Humanoid

Generic(通用的)

它是新的动画系统,支持非人形(怪物)动画,也支持人形动画,应用它会生成一套骨骼(Avatar)。

但它无法使用Humanoid动画重定向功能。即美术给一个模型做的动画,这些做的动画只能给这个模型使用,不能给其他模型使用。而Humanoid的动画重定向功能,可以实现一个模型的动画,给其他模型使用。

  • Avatar Define:化身定义,或者说骨骼映射定义。

Create From This Model:使用这个Model创建骨架
Copy From Other Avatar:使用其他骨骼(前提是和另一个模型的骨骼相同)

  • Root node:根节点

选择模型根节点

  • Optimize(优化) Game Objects:是否优化游戏物体(在发布游戏时勾选)

Humanoid(人形的)

Humanoid最牛X的地方就是支持动画重定向

选择Generic或者Humanoid后,系统都自动为Perfab模型生成Avatar。这个Avatar可以提供给其他同Humanoid的骨骼用来共用Avator(动画重定向)

在我们将Avatar Definition选择CreateFromThisModel后,点击Apply后可以点击Configure来配置骨骼映射,会跳转到大概这样的场景

这里我们可以调整骨骼映射。

这里的白色,绿色骨骼都是我们的素材创造的骨骼,而Mapping(映射)里面为Unity自带骨骼,我们创建的骨骼要映射到Unity自带的骨骼上。

绿色、白色都是Unity内置骨骼,会跟人物的骨骼节点映射,白色为未映射正确的。

实线为必须映射骨骼,虚线为非必须的。

更改映射方法:点击Model里的白色骨骼,在Hierarchy里选择正确的骨骼节点,拖到它的Mapping(映射)对话框中

动画重定向:

Unity引擎中动画重定向的实现不是一个直观的方法,而是封装在了Humanoid类型的动画系统里面,也就是必须是人形的骨架、使用Humanoid才可以使用它。Unity没有像前文描述的基本原理那样去定义两套骨架之间的映射关系,而是自己在内部定义一套骨架模板,所有的Avatar骨骼都必须映射到这套模板上才可以由同一个Animator来驱动产生Retargeting之后的动画效果。

比如我们给A创建了骨骼,并与Unity模板映射成功,给A的状态机配置了动画;现在我们来了个B人物,我们将它的骨骼配置一下与Unity骨骼模板基本绑定后,我们就可以让B去做出A动画状态机的动画(但是骨骼要填入各自的骨骼)

动画切割

在物体的Animation中可以进行动画的切割

按照提示直接切割就好。

下面的Loop Match 是用来检测动画片段的第一帧和最后一帧是否重合,方便我们做循环动画。

脚本处理

将动画名字符串转成哈希值方便调用

Animator.StringToHash("字符串");

它是Animator的静态方法,用Animator直接调用即可。

Animator.GetCurrentAnimatorStateInfo()

Animator.GetCurrentAnimatorStateInfo(a)

确定当前第a层动画的AnimatorStateInfo 对象(有关当前或下一个状态的动画器信息)。

属性:

  • fullPathHash
    该状态的完整路径哈希值。
  • length
    该状态的当前持续长度。
  • loop
    该状态是否循环。
  • normalizedTime
    该状态的归一化时间。
  • shortNameHash
    使用Animator.StringToHash生成的哈希值。传递的字符串不包含父层的名字。
  • tagHash
    该状态的标签

完整路径可以结合上面的StringToHash这样利用:

表示当前外层动画状态是否为外层的idle

补充:得到当前动画状态机播放的动画的方法:

        //GetCurrentAnimatorStateInfo 获取动画控制器中指定层的状态信息AnimatorStateInfo info = _anim.GetCurrentAnimatorStateInfo(0);//现在播放的动画是第0层的normalif ( Animator.StringToHash("normal").Equals(info.shortNameHash)){}

Animator.IsInTransition

bool IsInTransition(int layerIndex);

检测当前是否在过渡

layerIndex The layer’s index.
该层的索引。
0表示Base Layer

混合树:

一维混合树:

我们很多时候经常用一个变量来作为三个动画的转换条件,比如速度等于0则站着,大于0则走,再大点就跑起来,这样我们要做三个动画在后期是很不方便管理的,我们可以直接使用混合树来混合多个动画。

混合树也是一个state,但是可以混合多个动画,用一个值来做划分

我们在状态机中右击选择

生成新混合树后,双击就可以进入混合树编辑中,在混合树编辑中双击空白地方就可以退出混合树编辑。

如我设置的,就是根据一个参数speed,当它大于0,则向walk转换,大于0.5则开始向Run装换。

Automate Thresholds:自动设置阈值(我们可以取消勾选来亲自设置范围限制)

Adjust Time Scale:调整时间比例

这样,我们就做好了一个一维混合树,一个state就控制了站·走·跑的转换。

二维混合树:

二维混合树就是有了两个参数可以决定动画,我们常用的操作就是在站·走·跑的基础上加上

PosY(即speedz参数)可以控制站·走·跑的播放,而PosX(即speedRotate参数)可以控制旋转动画的播放

我们可以Compute Position来自动计算阈值(根据动画分析阈值)

x的大小来自于动画的角速度(角度为单位)

在脚本中控制两个参数的设置即可。

private int speedRotateID = Animator.StringToHash("speedRotate");
private int speedZ = Animator.StringToHash("speedz");
void Update()
{animator.SetFloat(speedZ, Input.GetAxis("Vertical")*4.1f);animator.SetFloat(speedRotateID, Input.GetAxis("Horizontal") * 121f);
}

关于2D混合树有很多不同的类型,我们要选择恰当的类型

官网奉上

我来简单翻译一下:

2D Simple Directional(简单方向): 当你的动作代表不同的方向时,例如「向前走」、「向后走」、「向左走」、「向右走」、「向上走」、「向下看」、「向左看」及「向右看」 ,你就可以使用这些动作。 可以选择包括位置(0,0)的单个运动,如“空转”或“瞄准直线”。 在简单方向类型中,不应该有同一方向的多个动作,例如“向前走”和“向前跑”。

2D Freeform Directional(自由方向): 当你的动作代表不同的方向时,也可以使用这种混合类型,但是你可以在同一个方向上有多个动作,例如“向前走”和“向前跑”。 在自由方向类型的运动集应始终包括一个单一的运动在位置(0,0) ,如“空闲”。

2D Freeform Cartesian(自由的笛卡尔): 当你的动作不代表不同的方向时最好使用。 使用自由笛卡尔坐标系,你的 x 参数和 y 参数可以代表不同的概念,比如角速度和线速度。 例如“向前走不转弯”、“向前跑不转弯”、“向前走向右转弯”、“向前跑向右转弯”等动作。

Direct: 这种类型的混合树允许用户直接控制每个节点的权重。 有用的面部形状或随机闲置混合。

相机跟随

相机跟随的代码,自行参考

public class CameraControl : MonoBehaviour
{private Transform player;private Vector3 offset;private float smoothing = 3;// Start is called before the first frame updatevoid Start(){player = GameObject.FindGameObjectWithTag("Player").transform;offset = transform.position - player.position;}// Update is called once per framevoid Update(){//player.TransformDirection(offset)将offset作为player的局部坐标再转换成世界坐标//这样子,player的转向会对offset产生影响Vector3 targetPosition = player.position+player.TransformDirection(offset);transform.position = Vector3.Lerp(transform.position, targetPosition, Time.deltaTime * smoothing);transform.LookAt(player.position);}
}

MatchTarget目标匹配

我们有的动作会需要和场景中的东西互动(比如爬墙,跳跃翻墙),这个时候,为了保证真实性,我们需要就匹配目标

示例代码:

    void Update(){animator.SetFloat(speedZ, Input.GetAxis("Vertical")*4.1f);animator.SetFloat(speedRotateID, Input.GetAxis("Horizontal") * 121f);bool isVault = false;if (animator.GetFloat(speedZ) > 3&&animator.GetCurrentAnimatorStateInfo(0).IsName("locomotion")) {RaycastHit hit;//距离我们有墙,则跳跃if (Physics.Raycast(transform.position + Vector3.up * 0.3f, transform.forward, out hit, 3.1f)) {if (hit.collider.tag == "Obstacle") {if (hit.distance > 2.9){Vector3 point = hit.point;point.y = hit.collider.transform.position.y + hit.collider.bounds.size.y+0.1f;matchTarget = point;isVault = true;}}}}animator.SetBool(vaultID, isVault);if (animator.GetCurrentAnimatorStateInfo(0).IsName("Vault")&&animator.IsInTransition(0)==false) {animator.MatchTarget(matchTarget,Quaternion.identity,AvatarTarget.LeftHand,new MatchTargetWeightMask(Vector3.one,0),0.25f,0.45f);}}
  • animator.GetCurrentAnimatorStateInfo(0).IsName(“locomotion”):得到现在的第0层的动画状态,判断名字是否是 locomotion
  • Physics.Raycast(transform.position + Vector3.up * 0.3f, transform.forward, out hit, 3.1f):从脚本挂载物体的位置往上提高0.3米的位置,向前方发出射线,返回射线碰撞信息给hit,射线的范围可达到3.1米
  • hit.collider.bounds.size.y:射线碰撞体的限制范围(即大小)的尺寸的y值
  • animator.GetCurrentAnimatorStateInfo(0).IsName(“Vault”):得到现在的第0层的动画状态,判断名字是否是 Vault
  • animator.IsInTransition(0):第0层动画是否是在转换中
  • animator.MatchTarget(matchTarget,Quaternion.identity,AvatarTarget.LeftHand,new MatchTargetWeightMask(Vector3.one,0),0.25f,0.45f):
    • matchTarget:匹配的位置
    • Quaternion.identity:旋转状态:无旋转
    • AvatarTarget.LeftHand:匹配的人物位置:人物骨骼的左手
    • new MatchTargetWeightMask(Vector3.one,0):一个权重掩码对象:位置权重为1(x:1,y:1,z:1),旋转权重为0。
    • 0.25f,0.45f:匹配时间由动画的第0.25秒到0.45秒(开始匹配后会有差值运算)

曲线操作方法:

我们可以绑定某个状态机内的变量跟随动画的进行而改变。

在我们的资源中的animation里会有这么一栏

其中的Curves就是曲线的意思,我们给Curves取个名字,在相应动画状态机中的同名变量就会被绑定。

而这个曲线的波动就是随着动画的进行这个变量的取值。

动过下面的预览模式配合给曲线加关键帧就可以调整曲线。

IK动画与状态机分层

动画状态机可以进行分层来决定不同的动作

建立新层后,点击层右边的设置就可以来配置该层

  • Weight是权重
  • Mesh是骨骼遮罩
  • Blending是混合方式:有覆盖——Override,以及添加——Additive

我们可以在Asset资源区新建一个骨骼遮罩——Avatar Mask,然后就是如下的画面

绿色就是可控制,红色就是不可控制,将骨骼遮罩赋值给动画层后,即可实现该层动画只对某部位的操作生效。

比如可以让手去拿起木头。

而实际操作中经常手和木头的位置不匹配,这个时候我们就可以借助反向动力学来解决这个问题。

IK是Inverse Kinematic的缩写,也就是反向动力学。

是根据骨骼的终节点来推算其他父节点的位置的一种方法。

比如通过手的位置推算手腕、胳膊肘的骨骼的位置。”

我们来看示例:

先运行该动画层IK Pass,并且骨骼遮罩允许需要的部分IK

然后我们可以创造空物体来把持位置,比如创造两个左手右手的空物体,移动到这个木头左右

然后就是代码控制

    //每一帧都会调用//每个勾选IK pass的层都会调用,可以通过参数判断哪一层调用了OnAnimatorIKprivate void OnAnimatorIK(int layerIndex){if (layerIndex == 1) {//isHoldLogID为真则执行双手拿木头的动画int weight = animator.GetBool(isHoldLogID) ? 1 : 0;//左手位置以及旋转跟随lefthand的位置和旋转,weight控制权值animator.SetIKPosition(AvatarIKGoal.LeftHand,lefthand.position);animator.SetIKRotation(AvatarIKGoal.LeftHand, lefthand.rotation);animator.SetIKPositionWeight(AvatarIKGoal.LeftHand, weight);animator.SetIKRotationWeight(AvatarIKGoal.LeftHand, weight);//右手位置以及旋转跟随righthand的位置和旋转,weight控制权值animator.SetIKPosition(AvatarIKGoal.RightHand, righthand.position);animator.SetIKRotation(AvatarIKGoal.RightHand, righthand.rotation);animator.SetIKPositionWeight(AvatarIKGoal.RightHand, weight);animator.SetIKRotationWeight(AvatarIKGoal.RightHand, weight);}}

这样一来,我们就可以自由固定双手的位置了


Timeline

这一个技术相对于其他动画系统,最大的区别就是,TimeLine针对多个游戏物体做出的一系列动画,主要用于过场动画的制作,实现电影级的那种分镜效果

这一技术很牛逼哦!

我们可以通过菜单栏中的 Window-》Sequencing-》Timeline,打开 timeline 编辑器

然后就可以选择某游戏物体来创建一个Timeline。

在一个timeline中,我们可以通过拖进来物体来创建轨道,然后就可以随心编辑轨道了。

更多关于timeline

更多关于timeline

利用Timeline可以轻松制作很多炫酷的分镜头

自定义脚本

我们还可以实现自定义扩展

在Asset资源区中找个合适的目录创建一个适用于Timeline的脚本

然后创建第二个脚本——Playable Asset ,示例代码如下:

[System.Serializable] //序列化,让外边能显示
public class TestAssert : PlayableAsset
{[Header("对话框")]public ExposedReference<Text> dialog;[Multiline(3)]public string dialogStr;private Test test = new Test();// Factory method that generates a playable based on this assetpublic override Playable CreatePlayable(PlayableGraph graph, GameObject go){test.dialog = dialog;test.dialogStr = dialogStr;Playable playable = ScriptPlayable<Test>.Create(graph,test);return playable;}
}

其中:ExposedReference:是一个泛型类型,可用于创建对场景对象的引用,以及通过使用上下文对象在运行时解析它们的实际值。ScriptableObject 或 PlayableAsset 等资源可使用它来创建对场景对象的引用。如果不使用ExposeReference,只是Text等类型,无法从那个Unity面板得到引用。

然后创建第一个脚本——Playable Behavior,示例代码如下:

public class Test : PlayableBehaviour
{public ExposedReference<Text> dialog;private Text _dialog;public string dialogStr;// Called when the owning graph starts playingpublic override void OnGraphStart(Playable playable){//Resole:根据 ExposedPropertyResolver 上下文对象,通过解析此引用的值来获取该值。_dialog = dialog.Resolve(playable.GetGraph().GetResolver());}// Called when the state of the playable is set to Playpublic override void OnBehaviourPlay(Playable playable, FrameData info){//文本框显示文字_dialog.gameObject.SetActive(true);_dialog.text = dialogStr;}// Called when the state of the playable is set to Pausedpublic override void OnBehaviourPause(Playable playable, FrameData info){if (_dialog){//播放暂停时关闭文字的显示_dialog.gameObject.SetActive(false);}}
}

设置好之后,我们将Playable Asset拖拽到timeline中,然后设置时间段上的参数,即可实现对应效果。

示例:

Playable Asset是沟通Playable Behavior和Unity timeline编辑界面的桥梁

Playable Behavior才是实现自定义功能的脚本

另外,在Playable Behavior中的几个方法的调用时机分别是:

  • OnGraphStart:当该PlayableBehaviour的PlayableGraph启动时调用,以上面gif中的情况为例,timeline一开始,就会调用四次该方法,不管有没有进入TestAsset区段,timeline一开始就调用了该方法。如果我们中间暂停了,然后又开始,那么还会调用四次该方法。
  • OnGraphStop:该函数在PlayableBehaviour片段停止播放时调用,以上面gif中的情况为例,timeline一结束/暂停,就会调用该方法,如果上面动画播完,就会瞬间调用四次该方法,如果中间动画被暂停,也会调用四次。
  • OnBehaviourPlay:当该PlayableBehaviour的PlayState转换为PlayState.Play时调用,以上面gif中的情况为例,timeline运行进入某个TestAsset区段时,该区段就会调用本方法一次。另外,在某区段中启动timeline,则会调用一次该区段负责的脚本的本方法,如果是空白区段则不会调用。
  • OnBehaviourPause:该函数在PlayableBehaviour片段的PlayState转换为Pause时调用,以上面gif中的情况为例,timeline运行离开某个TestAsset区段时,该区段就会调用本方法一次,注意timeline一开始启动会调用n次来暂停n个区段,例如上图,timeline一启动就会调用四次。另外,在某区段中暂停timeline,则会调用一次该区段负责的脚本的本方法,如果是空白区段则不会调用。
  • PrepareFrame:在该PlayableBehaviour播放的每一帧中调用,以上面gif中的情况为例,timeline运行进入某个TestAsset区段时,该区段对应脚本则会每一帧地调用本方法。

欢迎访问我的博客:is-hash.com

商业转载 请联系作者获得授权,非商业转载 请标明出处,谢谢

Unity中的动画系统和Timeline——笔记相关推荐

  1. Unity中的动画系统

    学习动画系统过程中的一些笔记 概述 Unity 的动画系统基于动画剪辑(Animation Clips)的概念,每个动画剪辑可以被认为是一个单一的线性记录,由动画状态机(Animator Contro ...

  2. 关于Unity中Mecanim动画的动画状态代码控制与代码生成动画控制器

    对于多量的.复杂的.有规律的控制器使用代码生成 动画状态代码控制 1:每个动画状态,比如进入状态,离开状态, 等都有可能需要代码来参与和处理,比如,进入这个动画单元后做哪些事情,来开这个动画单元后做哪 ...

  3. 个人技术总结——Unity中角色动画制作及动画切换逻辑的实现

    这个作业属于哪个课程 软件工程实践2022春-F班 这个作业要求在哪里 软件工程实践总结&个人技术博客 这个作业的目标 课程回顾与总结+个人技术总结 其他参考文献 <Unity2018教 ...

  4. 普元EOS中, 子系统和portal不在同一个域中,使用jquery的jsonp来解决portal跨域访问

    转至元数据起始 [背景] 子系统和portal不在同一个域中且项目中要求不能使用nginx.apache等反向代理软件,故使用jsonp从代码角度解决ajax跨域问题 [实现思路] 通过jquery的 ...

  5. Unity中使用动画状态机控制Spine动画

    下载Spine-Unity 为了在Unity中支持Spine动画,在http://zh.esotericsoftware.com/spine-unity-download/#Download下载spi ...

  6. 【Unity】Unity中开场动画设置(二)

    上一篇文章中对Unity中视频的播放设置是在PC端配置的 经过测试后发现并不能在手机端运行 因为在移动设备上我们需要使用另一种方式来播放视频 注意: 在移动端,unity并不提供MovieTextur ...

  7. 3dmax顶点动画导入unity_Mesh Vertex Animation In Unity | Unity中顶点动画

    最后的效果,左为顶点shader,右为蒙皮 10根骨骼 有些远景动画.小动画,用骨骼蒙皮来做太费了.一个好的办法是用顶点动画.比如在顽皮狗GDC的分享Technical Art Techniques ...

  8. 关于Unity中新版动画系统的使用

    Mecanim动画 1:旧版动画系统只能通过代码来控制动画播放,随着动画种类变多,代码复杂度也会增加,同时动画过渡也需要非常繁琐的代码控制,为了让有经验的动画师开发动画,unity推出了针对人物角色的 ...

  9. unity重定向_关于Unity中Mecanim动画的重定向与动画混合

    应用 一个RPG游戏,里面有100种怪物,每种怪物其实都差不多的,行走,跳跃,攻击,难道动画师要调100次动画吗?其实不需要 Unity抽象出人形动画系统,用Unity简化版的骨骼来进行统一的管理,只 ...

最新文章

  1. 宁波大学计算机王老师,王翀_宁波大学研究生导师信息
  2. 彻底理解JAVA动态代理
  3. oracle ogg常用指令,oracle goldengate日常管理命令
  4. CWinThread
  5. 一瓦同城-给新人第四天培训
  6. 如何ping通服务器的公网IP?
  7. tcpdump抓取无效TCP标志数据包表达式
  8. 在微型计算机中 集成在微处理,在微型计算机中,微处理器的主要功能是进行什么...
  9. JAVA 查找PDF中落款单位所在页码及位置信息
  10. MATLAB写UCB算法,科学网—【RL系列】Multi-Armed Bandit问题笔记——UCB策略实现 - 管金昱的博文...
  11. 【Kali】Kali linux 2021版本GVM无法启动解决方法
  12. foxmail收件箱按照每个人进行划分
  13. 计算机网络配置——路由器的配置
  14. CreateEvent方法详解
  15. 微信搜一搜迈出新的一步,好戏来了
  16. 史上最全 69 道 Spring 面试题和答案
  17. Swift实战(一): 剪子包袱锤ios应用
  18. 月薪5K跟月薪5W的程序员,写出来的代码有什么差别?
  19. 微信发图时找不到在其他软件上刚保存的图片或视频,但是在手机自带相册里面能看到的解决办法,亲测有效
  20. (实习)基线检测时遇到的问题

热门文章

  1. 江苏计算机等级应用考试试题,2010江苏省全国计算机等级考试二级笔试试卷VB理论考试试题及答案...
  2. 浙江大学远程教育平台计算机基础知识,浙江大学远程教育_计算机基础_第3次作业_Word知识题...
  3. 520送花逛街都out了!情商高的Python程序员教你用代码表白
  4. 计算机软件企业会计核算,顺景软件|ERP系统为企业会计核算提供支持
  5. qcqa是什么职位_QA和QC各是什么意思?
  6. 【java后台面经】春招秋招求职大佬面试经验分享
  7. python定时发送信息_Python为我定时发短信
  8. ngff半高无线网卡 Android,M.2 NGFF keyA keyE无线网卡FFC延长接口转PCIE1x PCIE4x 8x 16x
  9. MySQL入门笔记1
  10. 摊余成本法理解(转)