【Unity3D开发】DOTween 插件仿写

本文同时发布至我的个人博客,点击进入我的个人博客阅读。本博客供技术交流与经验分享,可自由转载。转载请在评论区或私信简单通知,感谢!

项目简介

​ DOTween是一个用于实现插值动画(补间动画)的 Unity 插件。“插值”这个概念可能一些使用 AE 或 Flash 等动画软件的同学可能有所了解。简单来说,插值动画(补间动画)就是在时间轴上定义两个不同时间的动画关键帧状态后,自动生成两个时间点之间所有帧动画动画制作方法。关于什么是插值动画(补间动画),以及什么是帧动画在这里不做过多的解释,感兴趣的读者可以参考这一篇文章——帧动画和补间动画。

​ 这个项目主要是利用C#中的拓展方法来实现一个简单的DOTween插件效果,实现过程涉及到拓展方法、协程等知识点。

设计参考

​ DOTween是一个开源插件,可以在GitHub上找到这个项目:DOTween - GitHub。官方实现的方式较为复杂,我在这里参考了其架构设计,将项目分成:TweenIDOTweenExtension

效果展示

【Unity3D开发】DOTween 插件仿写_第1张图片

开发过程

(一)Tween.cs:补间动画类

​ 定义一个简单的补间动画类,需要注意的是每个补间动画都包括一个协程。通过使用协程,我们的动画函数就可以实现不在一帧里完成,而是逐帧调用,每帧执行一部分。这符合动画实现的需求。

using UnityEngine;

namespace myTween
{
    public class Tween
    {
        internal string tid;
        internal Vector3 target;
        internal float duration;
        internal bool isPause = false;
        internal bool isAutoKill = true;
        internal Coroutine coroutine = null;
        internal Transform transform = null;
        public delegate void OnComplete();
        internal OnComplete onComplete = null;

        //constructor
        public Tween(string tid, Vector3 target, float duration, Transform transform, Coroutine coroutine)
        {
            this.tid = tid;
            this.target = target;
            this.duration = duration;
            this.transform = transform;
            this.coroutine = coroutine;
            IDOTween.getInstance().Add(this);
        }

        //set the coroutine of tween
        public void setCoroutine(Coroutine coroutine)
        {
            this.coroutine = coroutine;
        }

        public void setAutoKill(bool isAutoKill)
        {
            this.isAutoKill = isAutoKill;
        }

        public void Play()
        {
            isPause = false;
        }

        public void Pause()
        {
            isPause = true;
        }

        public void Kill()
        {
            MonoBehaviour mono = transform.GetComponent();
            mono.StopCoroutine(coroutine);
            IDOTween.getInstance().Remove(this);
        }

        public void runOnComplete()
        {
            if (onComplete != null)
            {
                onComplete();
            }
            if (isAutoKill)
            {
                Kill();
            }
        }

        public void setOnComplete(OnComplete fun)
        {
            onComplete += fun;
        }
    }
}

(二)IDOTween.cs:动画工厂类

​ 工厂类主要通过一个List来储存和管理所有的补间动画对象,同时也定义了一些基础的增删改查的基础函数。主要是注意控制单实例。

using System.Collections.Generic;
using UnityEngine;

namespace myTween {
    public class IDOTween
    {
        private static List dotweenList = new List();
        private static IDOTween dotween = null;

        //控制单实例
        public static IDOTween getInstance()
        {
            if (dotween == null)
            {
                dotween = new IDOTween();
            }
            return dotween;
        }

        public void Add(Tween d)
        {
            dotweenList.Add(d);
        }

        public void Remove(Tween d)
        {
            dotweenList.Remove(d);
        }

        public static void Play(string id)
        {
            foreach (Tween t in dotweenList)
            {
                if (t.tid == id)
                {
                    t.Play();
                }
            }
        }

        public static void Play(Transform transform)
        {
            foreach (Tween d in dotweenList)
            {
                if (d.transform == transform)
                {
                    d.Play();
                }
            }
        }

        public static void PlayAll()
        {
            foreach (Tween d in dotweenList)
            {
                d.Play();
            }
        }

        public static void Pause(string id)
        {
            for (int i = dotweenList.Count - 1; i >= 0; i--)
            {
                if (dotweenList[i].tid == id)
                {
                    dotweenList[i].Pause();
                }
            }
        }

        public static void Pause(Transform transform)
        {
            for (int i = dotweenList.Count - 1; i >= 0; i--)
            {
                if (dotweenList[i].transform == transform)
                {
                    dotweenList[i].Pause();
                }
            }
        }

        public static void PauseAll()
        {
            for (int i = dotweenList.Count - 1; i >= 0; i--)
            {
                dotweenList[i].Pause();
            }
        }

        public static void Kill(string id)
        {
            for (int i = dotweenList.Count - 1; i >= 0; i--)
            {
                if (dotweenList[i].tid == id)
                {
                    dotweenList[i].Kill();
                }
            }
        }

        public static void Kill(Transform transform)
        {
            for (int i = dotweenList.Count - 1; i >= 0; i--)
            {
                if (dotweenList[i].transform == transform)
                {
                    dotweenList[i].Pause();
                }
            }
        }

        public static void KillAll()
        {
            for (int i = dotweenList.Count - 1; i >= 0; i--)
            {
                dotweenList[i].Kill();
            }
        }
    }
}

(三)Extension.cs:拓展方法类

​ 拓展方法类主要是通过C#的拓展方法机制,对MonoBehaviourTransform类拓展控制Tween对象的一系列方法。这样用户只需导入已定义的命名空间myTween就可以拓展这些类方法。

using System.Collections;
using UnityEngine;

namespace myTween {
    public static class Extension
    {
        //MonoBehaviour的DoMove方法拓展
        public static IEnumerator DoMove(this MonoBehaviour mono, Tween tween)
        {
            Vector3 speed = (tween.target - tween.transform.position) / tween.duration;
            for (float f = tween.duration; f >= 0.0f; f -= Time.deltaTime)
            {
                tween.transform.Translate(speed * Time.deltaTime);
                yield return null;
                while (tween.isPause == true)
                {
                    yield return null;
                }
            }
            tween.runOnComplete();
        }

        //Transform的DoMove方法拓展
        public static Tween DoMove(this Transform transform, Vector3 target, float duration)
        {
            MonoBehaviour mono = transform.GetComponents()[0];
            Tween tween = new Tween("DoMove",target, duration, transform,null);
            Coroutine coroutine = mono.StartCoroutine(mono.DoMove(tween));
            tween.setCoroutine(coroutine);
            return tween;
        }

        //MonoBehaviour的DoScale方法拓展
        public static IEnumerator DoScale(this MonoBehaviour mono, Tween tween)
        {
            Vector3 speed = (tween.target - tween.transform.localScale) / tween.duration;
            for (float f = tween.duration; f >= 0.0f; f -= Time.deltaTime)
            {
                tween.transform.localScale = tween.transform.localScale + speed * Time.deltaTime;
                yield return null;
                while (tween.isPause == true)
                {
                    yield return null;
                }
            }
            tween.runOnComplete();
        }

        //Transform的DoScale方法拓展
        public static Tween DoScale(this Transform transform, Vector3 target, float time)
        {
            MonoBehaviour mono = transform.GetComponents()[0];
            Tween tween = new Tween("DoScale", target, time, transform, null);
            Coroutine coroutine = mono.StartCoroutine(mono.DoScale(tween));
            tween.setCoroutine(coroutine);
            return tween;
        }
    }
}

(四)测试脚本及测试

写一个简单的插件测试脚本(注意导入命名空间),在unity中挂载到一个游戏对象中,就可以测试插件的运行情况了:

using UnityEngine;
using myTween;

public class DOTweenTest : MonoBehaviour {

    Tween tween;

    private void Start()
    {
        transform.DoMove(new Vector3(5f, 5f, 5f), 5f);
        transform.DoScale(new Vector3(5f, 5f, 5f), 5f);
    }
}

你可能感兴趣的:(【Unity3D开发】DOTween 插件仿写)