分享一个前几天写的插件,当时为了做多个物体的简单动画(只有移动、旋转、缩放之类的)同时运动效果而写的,说白了算不上什么高级的联动动画,就只是同时控制多个物体协调运动而已,像什么机械类的原理动画展示,类似于齿轮传动机构之类的。
当时用自带的Animation调关键帧动画的时候,由于不停的在多个物体间切换,用过的都知道,那蛋疼的酸爽险些让我直接砸了电脑。所以之后才想着直接用插值动画写个多物体协调运动的插件,当然主要是针对一些简单的动画,量大的动画,以及有可能有数十个物体同时在运动的动画。
好了,先看下比较简单的使用方式:在场景中任意物体上挂载LinkageAnimation脚本,一个LinkageAnimation实例只对应一组动画。
Target Number表示当前动画组中有多少个动画物体,Frame Number表示当前动画组的关键帧数量,一个动画组中的所有联动物体拥有相同的关键帧数量。
Pause:暂停状态,勾选之后动画开启。
Speed:动画速度,值越大越快。
Add Target:添加动画联动物体,新增的物体会自动拥有当前已存在的关键帧数量。
Add Frame:添加关键帧,新增的关键帧会附加给每一个联动物体。
当前选中Frame0,也就是选中了关键帧0,在关键帧0下,可以查看当前关键帧每个联动物体的被记录组件的属性,目前只有Transform组件的属性被动画关键帧记录。
Delete:从动画组中删除某一联动物体,同时删掉他的所有关键帧。
Forbid:是否禁用此条属性,禁用后,在动画组运行其间,此关键帧与他的前后两帧的该属性都不会产生动画效果,默认状态所有属性都被禁用。
Get:从场景中获取物体此时的该属性的值,最好的方式就是在场景中将物体调节到指定的状态,然后新增关键帧,同时获取并保存他的属性。
Delete Frame:选中某一帧时,此按钮为删除此帧。
当完成一个动画组的关键帧制作之后,我们勾选Playing,运行场景就可以看到效果。
这是三个物体的联动动画,共包含五个关键帧:
动画的原理即是数值插值,然后我直接贴出整个LinkageAnimation的源码:
using UnityEngine; using System.Collections.Generic; public class LinkageAnimation : MonoBehaviour { public bool Playing = true; public float Speed = 1.0f; public List<AnimationFrame> Targets; [SerializeField] private int _frameLength = 0; private int _frameIndex = 0; private float _frameLocation = 0.0f; #if UNITY_EDITOR /// <summary> /// 添加联动物体 /// </summary> public void AddAnimationFrame() { if (Targets == null) Targets = new List<AnimationFrame>(); AnimationFrame af = new AnimationFrame(transform); for (int i = 0; i < _frameLength; i++) { af.Frames.Add(new Frame()); } Targets.Add(af); } /// <summary> /// 移除联动物体 /// </summary> public void RemoveAnimationFrame(AnimationFrame frame) { if (Targets == null) return; if (Targets.Contains(frame)) { Targets.Remove(frame); if (Targets.Count <= 0) _frameLength = 0; } } /// <summary> /// 移除联动物体 /// </summary> public void RemoveAtAnimationFrame(int index) { if (Targets == null) return; if (index >= 0 && index < Targets.Count) { Targets.RemoveAt(index); if (Targets.Count <= 0) _frameLength = 0; } } /// <summary> /// 添加关键帧 /// </summary> public void AddFrame() { if (Targets == null || Targets.Count <= 0) return; for (int i = 0; i < Targets.Count; i++) { Targets[i].Frames.Add(new Frame()); } _frameLength += 1; } /// <summary> /// 移除关键帧 /// </summary> public void RemoveAtFrame(int index) { if (Targets == null || Targets.Count <= 0) return; for (int i = 0; i < Targets.Count; i++) { if (index >= 0 && index < Targets[i].Frames.Count) { Targets[i].Frames.RemoveAt(index); } } _frameLength -= 1; } public int FrameLength() { return _frameLength; } #endif private void Update() { UpdateAnimation(); } private void UpdateAnimation() { if (Playing && Targets != null && _frameLength > 1) { if (_frameLocation >= 1.0f) { _frameLocation = 0.0f; _frameIndex += 1; if (_frameIndex >= (_frameLength - 1)) { _frameIndex = 0; } } else { _frameLocation += Time.deltaTime * Speed; } for (int i = 0; i < Targets.Count; i++) { if (Targets[i].Target != null && Targets[i].Frames.Count > 0) UpdateFrame(Targets[i]); } } } private void UpdateFrame(AnimationFrame af) { if (!af.Frames[_frameIndex].PositionDisabled && !af.Frames[_frameIndex + 1].PositionDisabled) af.Target.localPosition = Vector3.Lerp(af.Frames[_frameIndex].Position, af.Frames[_frameIndex + 1].Position, _frameLocation); if (!af.Frames[_frameIndex].RotationDisabled && !af.Frames[_frameIndex + 1].RotationDisabled) af.Target.localRotation = Quaternion.Euler(Vector3.Lerp(af.Frames[_frameIndex].Rotation, af.Frames[_frameIndex + 1].Rotation, _frameLocation)); if (!af.Frames[_frameIndex].ScaleDisabled && !af.Frames[_frameIndex + 1].ScaleDisabled) af.Target.localScale = Vector3.Lerp(af.Frames[_frameIndex].Scale, af.Frames[_frameIndex + 1].Scale, _frameLocation); } }
以及动画帧类:
[System.Serializable] public class AnimationFrame { public Transform Target; public List<Frame> Frames; public AnimationFrame( Transform tf ) { Target = tf; Frames = new List<Frame>(); } } [System.Serializable] public class Frame { public Vector3 Position; public Vector3 Rotation; public Vector3 Scale; public bool PositionDisabled; public bool RotationDisabled; public bool ScaleDisabled; public bool ShowInspector; public Frame() { Position = Vector3.zero; Rotation = Vector3.zero; Scale = Vector3.one; PositionDisabled = true; RotationDisabled = true; ScaleDisabled = true; ShowInspector = false; } }