Unity3DGame学习笔记(7):DOTween

实验任务:

这次我们要实现对一个动画插件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上进行实验,其中位移为循环执行,扩大与旋转是一次执行。



  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值