Unity 多物体联动动画

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


当时用自带的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;
    }
}


你可能感兴趣的:(unity,联动动画)