Unity3DGame学习笔记(7):DOTween

xiaoxiao2021-02-28  93

实验任务:

这次我们要实现对一个动画插件Dotween的仿写。 它在 Specific settings 中 transform.DoMove 返回 Tween 对象,而我们要实现该对象,实现对动作的持续管理。在仿写前,让我们先了解一下什么是Dotween。Dotween是一款unity插值动画插件,unity里面做插值动画的插件有许多,比较常见的有itween、hotween、dotween。dotween插件在灵活性、稳定性、易用性上都十分突出。

实验需求分析:

在这次实验中我们通过协程和扩展方法来实现插值动画的实现。其中,tween对象实现动作属性的储存,pause,kill,play,complete,restart动作的管理以及实现回调,而后通过DOTween实现tween对象的管理以及用户调用PauseAll,PlayAll等方法的实现。tween对象与动作协程是一一对应关系,对tween对象的管理也就实现了对动作协程的管理。 根据官方文档的描述,我们知道实现DoMove等动作,需要两个基本类,动作类tween以及动作管理类DOTween。 其中tween类描述如下: 动作管理类控制方法描述如下:

UML图:

代码:

1.tween类: tween类储存了动作的各种信息,如目标位置,动作限时等。每一个tween对象对应一个动作协程,通过tween内的Pause,Play等方法来控制动作的播放与暂停。我们只需在transform扩展方法中新建动作协程时,new一个tween对象管理动作对应协程,再把tween放进DOTween中进行管理。 using System.Collections; using System.Collections.Generic; using UnityEngine; public class tween { public string tweenType; //记录tween动作类型 public string id; //用于过滤 public int loops; //记录循环次数 public int currentLoop; //记录tween已经执行了多少次循环 public Transform transform; //记录动作对象的当前transform属性 public Vector3 originalPosition; //记录对象起始位置 public Vector3 originalRotation; //记录对象起始rotation public Vector3 originalScale; //记录对象起始scale public Vector3 target; //记录目标position,rotation或者scale public float time; //记录动作限时 public bool isPause; //记录tween是否停止 public bool autoKill; //记录tween是否自动被杀死 public Coroutine coroutine; //记录tween对应的协程 public delegate void Callback(); //回调函数的委托 public Callback onComplete; //完成时的回调 public Callback onKill; //被杀死时的回调 public Callback onPause; //停止时的回调 public tween(string type, Transform trans, Vector3 tar, float ti) { tweenType = type; transform = trans; target = tar; time = ti; //设置特殊值 originalPosition = new Vector3 (trans.position.x, trans.position.y, trans.position.z); originalRotation = new Vector3 (trans.rotation.x, trans.rotation.y, trans.rotation.z); originalScale = new Vector3 (trans.localScale.x, trans.localScale.y, trans.localScale.z); //设置起始transform,在Restart等时候用 id = type; loops = 1; currentLoop = 0; isPause = false; autoKill = true; coroutine = null; onComplete = null; //设置默认值 DOTween.getInstance ().Add (this); //加进队列用于管理 } //一下Set前缀的方法用于设置属性,为了能够像官方一样链式调用,就返回tween自己 public tween SetLoops(int l) { loops = l; return this; } //设置循环次数 public tween SetId(string i) { id = i; return this; } //设置过滤信息 public tween SetCoroutine(Coroutine c) { coroutine = c; return this; } //设置对应协程 public tween SetAutoKill(bool auto) { autoKill = auto; return this; } //设置是否被自动杀死 public tween SetOnComplete(Callback c) { onComplete += c; return this; } //设置完成时的回调函数 public tween SetOnKill(Callback c) { onKill += c; return this; } //设置被杀死时的回调函数 public tween SetOnPause(Callback c) { onPause += c; return this; } //设置停止时的回调函数 public void Pause() { isPause = true; } //停止 public void Play() { isPause = false; } //播放 public void Restart() { ResetPosition (); ResetRotation (); ResetScale(); Play (); } //重启所有 public void ResetPosition() { transform.position = new Vector3 (originalPosition.x, originalPosition.y, originalPosition.z); } //重启position的动作 public void ResetRotation() { transform.rotation = Quaternion.Euler(new Vector3 (originalRotation.x, originalRotation.y, originalRotation.z)); } //重启rotation的动作 public void ResetScale() { transform.localScale = new Vector3 (originalScale.x, originalScale.y, originalScale.z); } //重启scale的动作 public void Complete() { if (tweenType == "DoMove") { transform.position = target; } else if (tweenType == "DoRotate") { transform.rotation = Quaternion.Euler (target); } else if (tweenType == "DoScale") { transform.localScale = target; } else { Debug.Log ("Wrong typeName!"); } OnComplete (); } //立即完成tween动作 public void Kill() { MonoBehaviour mono = transform.GetComponent<MonoBehaviour> (); mono.StopCoroutine (coroutine); DOTween.getInstance ().Remove (this); } //杀死tween public void OnComplete() { if (onComplete != null) { onComplete (); } if (autoKill) { Kill (); } } //完成时的回调函数 public void OnKill() { if (onKill != null) { onKill (); } } //杀死时的回调函数 public void OnPause() { if (onPause != null) { onPause (); } } //停止时的回调函数 } 2.DOTween类: DOTween类是管理整个场景tween对象的地方,每一次new一个tween,都要将其放进DOTween的单实例中,进行统一管理。当动作完成时,根据autoKill可以自动释放一个tween,删除完成的动作。用户也可以显示调用DOTween的静态函数,如PauseAll停止所有动作,PlayAll播放所有动作等。 using System.Collections; using System.Collections.Generic; using UnityEngine; public class DOTween { private static DOTween _instance; //DOTween类的单实例 private static List<tween> tweenList = new List<tween>(); //管理tween对象的链表 //初始化一个DOTween的基本属性 public static void Init() { _instance = new DOTween (); } //获得DOTween的单实例 public static DOTween getInstance() { if (_instance == null) { Init (); } return _instance; } //为tween的链表加入新的tween public void Add(tween newTween) { tweenList.Add (newTween); } //从链表中删除kill的tween public void Remove(tween oldTween) { tweenList.Remove (oldTween); } /*public static int getTweenSize() { return tweenList.Count; }*/ //停止所有的tween public static void PauseAll() { foreach (tween t in tweenList) { t.Pause (); } } //通过filter过滤停止指定的tween public static void Pause(string filter) { foreach (tween t in tweenList) { if (t.id == filter) { t.Pause (); } } } //通过transform停止指定位置的tween public static void Pause(Transform trans) { foreach (tween t in tweenList) { if (t.transform == trans) { t.Pause (); } } } //杀死所有tween public static void KillAll() { foreach (tween t in tweenList) { t.Kill (); } } //通过filter过滤杀死指定的tween public static void Kill(string filter) { foreach (tween t in tweenList) { if (t.id == filter) { t.Kill (); } } } //通过transform停止指定位置的tween public static void Kill(Transform trans) { foreach(tween t in tweenList) { if (t.transform == trans) { t.Kill (); } } } //播放所有tween public static void PlayAll() { foreach (tween t in tweenList) { t.Play (); } } //通过filter过滤播放制定的tween public static void Play(string filter) { foreach (tween t in tweenList) { if (t.id == filter) { t.Play (); } } } //通过transform播放指定位置的tween public static void Play(Transform trans) { foreach (tween t in tweenList) { if (t.transform == trans) { t.Play (); } } } //反转所有tween的isPause属性 public static void TogglePauseAll() { foreach (tween t in tweenList) { if (t.isPause) { t.Play (); } else { t.Pause (); } } } //通过filter过滤反转指定的tween的isPause public static void TogglePause(string filter) { foreach (tween t in tweenList) { if (t.id == filter) { if (t.isPause) { t.Play (); } else { t.Pause (); } } } } //通过transform反转指定位置的tween属性 public static void TogglePause(Transform trans) { foreach (tween t in tweenList) { if (t.transform == trans) { if (t.isPause) { t.Play (); } else { t.Pause (); } } } } //重启所有tween public static void RestartAll() { foreach (tween t in tweenList) { t.currentLoop = 0; t.Restart (); } } //通过filter过滤重启指定的tween public static void Restart(string filter) { foreach (tween t in tweenList) { if (t.id == filter) { t.currentLoop = 0; t.Restart (); } } } //通过transform重启指定位置的tween public static void Restart(Transform trans) { foreach (tween t in tweenList) { if (t.transform == trans) { t.currentLoop = 0; t.Restart (); } } } //立即完成所有tween public static void CompleteAll() { foreach (tween t in tweenList) { t.Complete (); } } //通过filter过滤完成指定的tween public static void Complete(string filter) { foreach (tween t in tweenList) { if (t.id == filter) { t.Complete (); } } } //通过transform完成指定位置的tween public static void Complete(Transform trans) { foreach (tween t in tweenList) { if (t.transform == trans) { t.Complete (); } } } } 3.扩展方法: 根据老师给的样例代码,我们可以利用写好的tween与DOTween进行方法扩展。 using System.Collections; using System.Collections.Generic; using UnityEngine; public class ExtensionMethods {} namespace MyExtensionMethods { public static class MyExtensions { public static IEnumerator DoMove(this MonoBehaviour mono, tween myTween) { //外层循环决定tween的循环次数 for (; myTween.currentLoop < myTween.loops; myTween.currentLoop++) { Debug.Log ("i = " + myTween.currentLoop); Vector3 distance = (myTween.target - myTween.transform.position) / myTween.time; //计算每帧移动的距离 for (float f = myTween.time; f >= 0.0f; f -= Time.deltaTime) { //just like call update() Debug.Log ("Move"); myTween.transform.Translate (distance * Time.deltaTime); //移动 yield return null; while (myTween.isPause == true) { Debug.Log ("Move Pause"); yield return null; } //如果停止,将停在while中,下一帧不移动 } if (myTween.currentLoop < myTween.loops - 1) { myTween.ResetPosition (); } } myTween.OnComplete (); //完成后执行回调函数。 } public static tween DoMove(this Transform transform, Vector3 target, float time) { MonoBehaviour mono = transform.GetComponents<MonoBehaviour> () [0]; tween myTween = new tween ("DoMove", transform, target, time); Coroutine coroutine = mono.StartCoroutine (mono.DoMove(myTween)); myTween.SetCoroutine (coroutine); return myTween; } /* 以下的DoRotate和DoScale方法的思想与DoMove类似,代码很相似,主要在要改变的属性上有所不用, 如果还要扩展其他方法,步骤也类似。 */ public static IEnumerator DoRotate(this MonoBehaviour mono, tween myTween) { for (; myTween.currentLoop < myTween.loops; myTween.currentLoop++) { Vector3 angle = (myTween.target - myTween.transform.rotation.eulerAngles) / myTween.time; //Debug.Log ("angle = " + angle); for (float f = myTween.time; f >= 0.0f; f -= Time.deltaTime) { //just like call update() Debug.Log ("Rotate"); myTween.transform.Rotate (angle * Time.deltaTime); yield return null; while (myTween.isPause == true) { Debug.Log ("Rotate Pause"); yield return null; } } if (myTween.currentLoop < myTween.loops - 1) { myTween.ResetRotation (); } } myTween.OnComplete (); } public static tween DoRotate(this Transform transform, Vector3 target, float time) { MonoBehaviour mono = transform.GetComponents<MonoBehaviour> () [0]; tween myTween = new tween ("DoRotate", transform, target, time); Coroutine coroutine = mono.StartCoroutine (mono.DoRotate(myTween)); myTween.SetCoroutine (coroutine); return myTween; } public static IEnumerator DoScale(this MonoBehaviour mono, tween myTween) { for (; myTween.currentLoop < myTween.loops; myTween.currentLoop++) { Vector3 scale = (myTween.target - myTween.transform.localScale) / myTween.time; for (float f = myTween.time; f >= 0.0f; f -= Time.deltaTime) { //just like call update() Debug.Log ("Scale"); myTween.transform.localScale += scale * Time.deltaTime; yield return null; while (myTween.isPause == true) { Debug.Log ("Scale Pause"); yield return null; } } if (myTween.currentLoop < myTween.loops - 1) { myTween.ResetScale (); } } myTween.OnComplete (); } public static tween DoScale(this Transform transform, Vector3 target, float time) { MonoBehaviour mono = transform.GetComponents<MonoBehaviour> () [0]; tween myTween = new tween ("DoScale", transform, target, time); Coroutine coroutine = mono.StartCoroutine (mono.DoScale(myTween)); myTween.SetCoroutine (coroutine); return myTween; } } } 4.测试函数: 测试函数主要用来新建动作以及进行DOTween的函数控制。 using UnityEngine; using System.Collections; using MyExtensionMethods; public class dotweenTest : MonoBehaviour { private tween myTween; // Use this for initialization void Start () { DOTween.Init (); myTween = transform.DoMove (new Vector3 (15.0f, 0f, 0f), 3.0f).SetLoops(3); transform.DoRotate (new Vector3(150f, 0f, 0f), 3.0f).SetLoops(1); transform.DoScale (new Vector3 (3f, 3f, 3f), 3.0f).SetLoops(1); } // Update is called once per frame void Update () { //Debug.Log ("tween Size = " + DOTween.getTweenSize ()); if (Input.GetKeyDown ("f")) { Debug.Log ("f down PauseAll"); DOTween.PauseAll (); } if (Input.GetKeyDown ("g")) { Debug.Log ("g down PlayAll"); DOTween.PlayAll (); } if (Input.GetKeyDown ("h")) { Debug.Log ("h down RestartAll"); DOTween.RestartAll (); } if (Input.GetKeyDown ("j")) { Debug.Log ("j down CompleteAll"); DOTween.CompleteAll (); } if (Input.GetKeyDown ("a")) { Debug.Log ("a down Pause DoScale"); DOTween.Pause("DoScale"); } } }

实验效果:

将测试代码挂在一个Cube上进行实验,其中位移为循环执行,扩大与旋转是一次执行。
转载请注明原文地址: https://www.6miu.com/read-32763.html

最新回复(0)