分享一个前几天写的插件,当时为了做多个物体的简单动画(只有移动、旋转、缩放之类的)同时运动效果而写的,说白了算不上什么高级的联动动画,就只是同时控制多个物体协调运动而已,像什么机械类的原理动画展示,类似于齿轮传动机构之类的。

 

当时用自带的Animation调关键帧动画的时候,由于不停的在多个物体间切换,用过的都知道,那蛋疼的酸爽险些让我直接砸了电脑。所以之后才想着直接用插值动画写个多物体协调运动的插件,当然主要是针对一些简单的动画,量大的动画,以及有可能有数十个物体同时在运动的动画。

 

好了,先看下比较简单的使用方式:在场景中任意物体上挂载LinkageAnimation脚本,一个LinkageAnimation实例只对应一组动画。

Unity 多物体联动动画_unity

 

Target Number表示当前动画组中有多少个动画物体,Frame Number表示当前动画组的关键帧数量,一个动画组中的所有联动物体拥有相同的关键帧数量。

Pause:暂停状态,勾选之后动画开启。

Speed:动画速度,值越大越快。

Add Target:添加动画联动物体,新增的物体会自动拥有当前已存在的关键帧数量。

Add Frame:添加关键帧,新增的关键帧会附加给每一个联动物体。

当前选中Frame0,也就是选中了关键帧0,在关键帧0下,可以查看当前关键帧每个联动物体的被记录组件的属性,目前只有Transform组件的属性被动画关键帧记录。

Delete:从动画组中删除某一联动物体,同时删掉他的所有关键帧。

Forbid:是否禁用此条属性,禁用后,在动画组运行其间,此关键帧与他的前后两帧的该属性都不会产生动画效果,默认状态所有属性都被禁用。

Get:从场景中获取物体此时的该属性的值,最好的方式就是在场景中将物体调节到指定的状态,然后新增关键帧,同时获取并保存他的属性。

Delete Frame:选中某一帧时,此按钮为删除此帧。

 

当完成一个动画组的关键帧制作之后,我们勾选Playing,运行场景就可以看到效果。

Unity 多物体联动动画_联动动画_02

 

这是三个物体的联动动画,共包含五个关键帧:

 

Unity 多物体联动动画_List_03

 

动画的原理即是数值插值,然后我直接贴出整个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;
}
}