美文网首页UnityUnity干货Unity技术分享
【Unity3D开发】DOTween 插件仿写

【Unity3D开发】DOTween 插件仿写

作者: DamonTo | 来源:发表于2017-05-17 16:08 被阅读334次

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

    项目简介

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

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

    设计参考

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

    效果展示

    开发过程

    (一)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<MonoBehaviour>();
                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<Tween> dotweenList = new List<Tween>();
            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<MonoBehaviour>()[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<MonoBehaviour>()[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);
        }
    }
    

    相关文章

      网友评论

      • 一个当年人:对的,有一个空指针的错误,已转载。
      • LoveWaits_9b9a:控制自身是没有问题的,但是要是用脚本控制别的物体,且物体上没有挂载任何继承MonoBehaviour 的组建就会报空,不知道是不是我哪里弄错了

      本文标题:【Unity3D开发】DOTween 插件仿写

      本文链接:https://www.haomeiwen.com/subject/udbdxxtx.html