unity作業——簡單dotween的製作

這次作業,老師讓我們仿製一個簡單的dotween,擴展一下transform中的方法,在這裏我擴展了三個方法,使得例如調用transform.DoTranslate, transform.DoRotate,Transform.DoScale分別實現物體的平移,旋轉和大小的改變。這裏老師上課的網站上重點介紹了要實現這幾個功能,需要用到協程,動態方法和lambda表達式。lambda表達式在這次作業中我還沒有用到,但是協程和動態方法這次是必須要用的。首先我來談一下我對協程的理解。

協程,可以說是僞線程。即它看上去像線程,但它的中斷是顯式的,直到指定的事件發生,由ReActor調度程序從當前斷點繼續執行。 也即是說當我們一般在執行函數調用時,函數會在一幀的時間內完成,若我們不想函數在一幀的時間內完成,而是分步執行,每次只執行一部分,下次執行時由上次執行的最後一句的下一句開始,這時我們就要用到協程。協程定義的方式爲:IEnumerator+函數名(參數列表),同時要在返回語句前加上關鍵字yield,下面我們來看一個具體例子,就是潘老師的網站上所寫的。

IEnumerator Fade() {
        for (float f = 1f; f >= 0; f -= 0.1f) {
                Color c = renderer.material.color;
                c.a = f;
                renderer.material.color = c;
                yield return null;
        }
}
    上述程序表示每當啓動Fade啓動後就會獲得cpu時間,知道yield中斷語句的執行。return null表示下次update事件發生時,自動從下一條語句執行。上述函數是一個漸變的改
變顏色的函數,可以助我們更好的實現顏色的變化。
    啓動協程。
    
void Update() {
        if (Input.GetKeyDown("f")) {
                StartCoroutine("Fade");
        }
}
以上工作就完成了協程的啓動,因爲這次我們的任務是要求在一定的時間內實現對物體Transform屬性的改變,因此必須要用到協程。

    下面是我對擴展方法的理解。
    擴展方法則是在現有的類中增加方法,無需繼承就可以改寫類,這次我們就需要在Transform類中增加三個方法。
    擴展方法的格式爲:public static 返回類型 函數名(this Typeanme class, 參數){}這樣就擴展出了一個方法,以下引用一段微軟官方的對擴展方法的解釋。
    擴展方法使你能夠向現有類型“添加”方法,而無需創建新的派生類型、重新編譯或以其他方式修改原始類型。 擴展方法是一種特殊的靜態方法,但可以像擴展類型上的實例方法一樣進行調用。
    下面是一段老師網站的代碼,幫助我們重點理解一下擴展方法。
      
public static class extend_method_test{
        //This is Extened Method, The first parameter beging with (this Type name, args)
        public static bool NextBool(this System.Random random)
        {
                return random.NextDouble() > 0.5;
        }
}

public class NewBehaviourScript : MonoBehaviour {
        // Use this for initialization
        void Start () {
                System.Random rand = new System.Random();
                print (rand.NextBool ());
        }

        // Update is called once per frame
        void Update () {

        }
} 
    這裏就在系統類System.Random中增加了一個新的方法NextBool,即返回下一個大於0.5的隨機數。
    在有了上述準備知識後,這次作業就比較容易理解了,首先是dotween類的編寫,我們這裏實現第一個效果DoRotate,使得物體能夠在指定的時間內平移一定角度。
    dotween對應的是相應動作的協程,和動作對應的協程一一對應,裏面存儲了關於這個動作的各個信息,其中包括要爲其設置協程。還有兩個比較重要的變量,isPause和autoKill,autoKill
代表的是對應的動作結束後是否自動銷燬,而isPause則意味着顯示決定動作是否暫停和繼續。
public class dotween{
	public string name;    //對應的動作名
	public Coroutine coroutine = null;    // 要添加的協程
	public Transform trans = null;
	public float time;
	public bool isPause = false;
	public bool autoKill = true;
	public Vector3 target;
	public delegate void OnComplete();    // 相應的委託
	public OnComplete onComplete = null;

	public dotween(string s, Transform t, Vector3 v, float ti, Coroutine c) {
		name = s;
		trans = t;
		target = v;
		time = ti;
		//Dotween.getInstance ().Add (this);
	}
	//設置協程
	public void setCoroutine(Coroutine c) {
		coroutine = c;
	}
        //殺死協程,此時只是第一步,我們先求實現一個動作,不添加工廠管理這些動作,將與工廠有關的代碼先註釋掉
	public void Kill() {
		MonoBehaviour mono = trans.GetComponent<MonoBehaviour> ();
		mono.StopCoroutine (coroutine);
		//Dotween.getInstance ().Remove (this);
	}
	public void play() {
		isPause = false;
	}
	public void Pause() {
		isPause = true;
	}
	//設置回調函數,可以設置多個回調函數
	public void setOnComplete(OnComplete OnC) {
		onComplete += OnC;
	}
	public void runOnComplete() {
		if (onComplete != null) {
			onComplete ();
		}
		if (autoKill) {
			Kill ();
		}
	}
	//設置是否殺死自動殺死協程的方法
	public void setAutoKill(bool b) {
		autoKill = b;
	}
}
下面是具體的擴展動作類的實現,這裏我們先擴展一個方法DoRotate。因爲老師網站上已經爲我們寫好了大致框架,我們只需要完成一些具體的功能即可。

	public static class extension_method{
		//在給定的時間內旋轉一定角度
		public static IEnumerator DoRotate(this MonoBehaviour mono, dotween dot) {
			//計算出要旋轉的角度
			Vector3 angle = (dot.target - dot.trans.position) / dot.time;
			for (float f = 0.0f; f <= dot.time; f += Time.deltaTime) {
				dot.trans.Rotate (angle*Time.deltaTime);
				//Debug.Log ("rotate:");
				yield return null;
				//如果isPause變量值爲true,表示動作暫停,則接下來動作不會執行
				while (dot.isPause == true) {
					yield return null;
				}
			}
			//執行完後,進行廣播
			dot.runOnComplete ();
		}
		public static dotween DoRotate(this Transform transform, Vector3 target, float time) {
			MonoBehaviour mono = transform.GetComponents<MonoBehaviour> () [0];
			//新建一個動作dotween
			dotween dot = new dotween ("DoRotate", transform, target, time, null);
			//爲該方法新建協程
			Coroutine coroutine = mono.StartCoroutine (mono.DoRotate (dot));
			//設置協程
			dot.setCoroutine (coroutine);
			return dot;
		}
		
	} 
    然後一個具體的擴展方法就完成了。我們來看一下具體的運動效果


    初始的位置和旋轉角度
    旋轉後的角度,可以看出物體的角度發生了旋轉,我們的擴展方法實現了。
    我們當前只是實現了一個簡單的動作,爲了更好的管理更多動作,我們還要設置一個工廠,管理具體的動作的產生和稅收,下面是工廠類DotweenFactory的實現
    public class DotweenFactory {
	private static List<dotween> dotList = new List<dotween> ();
	private static DotweenFactory dot = null;
	//單實例模式
	public static DotweenFactory getInstance() {
		if (dot == null) {
			dot = new DotweenFactory ();
		}
		return dot;
	}
	//添加一個dotween對象
	public void Add(dotween d) {
		dotList.Add (d);
	}
	//移除一個dotween對象
	public void Remove(dotween d) {
		dotList.Remove (d);
	}
	//暫停所有動作
	public static void PauseAll() {
		int count = dotList.Count - 1;
		for (int i = count; i >= 0; i--) {
			dotList [i].Pause ();
		}
	}
	//暫停特定變量名的動作
	public static void Pause(string s) {
		int count = dotList.Count - 1;
		for (int i = count; i >= 0; i--) {
			if (dotList [i].name == s) {
				dotList [i].Pause ();
					Debug.Log (s, "has been paused");
			}
		}
	}
	//演示所有動作
	public static void playAll() {
		int count = dotList.Count - 1;
		for (int i = count; i >= 0; i--) {
			dotList [i].play ();
		}
	}
	//演示特定方法名的動作
	public static void play(string s) {
		int count = dotList.Count - 1;
		for (int i = count; i >= 0; i--) {
			if (dotList [i].name == s) {
				dotList [i].play ();
					Debug.Log (s, "has been played");
			}
		}
	}
	//殺死所有動作
	public static void KillAll() {
		int count = dotList.Count - 1;
		for (int i = count; i >= 0; i--) {
			dotList [i].Kill ();
		}
	}
	//殺死特定方法名的動作
	public static void Kill(string s) {
		int count = dotList.Count - 1;
		for (int i = count; i >= 0; i--) {
			if (dotList [i].name == s) {
				dotList [i].Kill ();
					Debug.Log (s, "has been killed");
			}
		}
	}
}
    剛纔我們只擴展了一個動作,現在我們來擴展接下來兩個動作,分別是平移和改變大小。代碼如下。
    public static IEnumerator DoTranslate(this MonoBehaviour mono, dotween dot) {
			Vector3 dis = (dot.target - dot.trans.position) / dot.time;
			for (float f = 0.0f; f <= dot.time; f += Time.deltaTime) {
				dot.trans.Translate (dis * Time.deltaTime);
				//Debug.Log ("transform:");
				yield return null;
				while (dot.isPause == true) {
					yield return null;
				}
			}
			dot.runOnComplete ();
		}
		public static dotween DoTranslate(this Transform transform, Vector3 target, float time) {
			MonoBehaviour mono = transform.GetComponents<MonoBehaviour> () [0];
			dotween dot = new dotween ("DoTranslate", transform, target, time, null);
			Coroutine coroutine = mono.StartCoroutine (mono.DoTranslate (dot));
			dot.setCoroutine (coroutine);
			return dot;
		}

		public static IEnumerator DoScale(this MonoBehaviour mono, dotween dot) {
			Vector3 scale = (dot.target - dot.trans.position) / dot.time;
			for (float f = 0.0f; f <= dot.time; f += Time.deltaTime) {
				dot.trans.localScale += scale * Time.deltaTime;
				//Debug.Log ("changeScale:");
				yield return null;
				while (dot.isPause == true) {
					yield return null;
				}
			}
			dot.runOnComplete ();
		}
		public static dotween DoScale(this Transform transform, Vector3 target, float time) {
			MonoBehaviour mono = transform.GetComponents<MonoBehaviour> () [0];
			dotween dot = new dotween ("DoScale", transform, target, time, null);
			Coroutine coroutine = mono.StartCoroutine (mono.DoScale (dot));
			dot.setCoroutine (coroutine);
			return dot;
		}
	} 
接下來是測試代碼,這裏將動作和工廠一起測試。

    初始位置
    結束後
    
    可以看出運動到了我們想要的位置。接下來是全部的代碼
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Dotween.MyDotween;

namespace Dotween.MyDotween{

public class DotweenFactory {
	private static List<dotween> dotList = new List<dotween> ();
	private static DotweenFactory dot = null;
	public static DotweenFactory getInstance() {
		if (dot == null) {
			dot = new DotweenFactory ();
		}
		return dot;
	}
	public void Add(dotween d) {
		dotList.Add (d);
			Debug.Log ("success add");
	}
	public void Remove(dotween d) {
		dotList.Remove (d);
	}
	public static void PauseAll() {
		int count = dotList.Count - 1;
		for (int i = count; i >= 0; i--) {
			dotList [i].Pause ();
		}
	}
	public static void Pause(string s) {
		int count = dotList.Count - 1;
		for (int i = count; i >= 0; i--) {
			if (dotList [i].name == s) {
				dotList [i].Pause ();
					Debug.Log("dot has been paused");
			}
		}
	}
	public static void playAll() {
		int count = dotList.Count - 1;
		for (int i = count; i >= 0; i--) {
			dotList [i].play ();
		}
	}
	public static void play(string s) {
		int count = dotList.Count - 1;
		for (int i = count; i >= 0; i--) {
			if (dotList [i].name == s) {
				dotList [i].play ();
					Debug.Log ("dot has been played");
			}
		}
	}
	public static void KillAll() {
		int count = dotList.Count - 1;
		for (int i = count; i >= 0; i--) {
			dotList [i].Kill ();
		}
	}
	public static void Kill(string s) {
		int count = dotList.Count - 1;
		for (int i = count; i >= 0; i--) {
			if (dotList [i].name == s) {
				dotList [i].Kill ();
					Debug.Log ( "dot has been killed");
			}
		}
	}
}

public class dotween{
	public string name;
	public Coroutine coroutine = null;
	public Transform trans = null;
	public float time;
	public bool isPause = false;
	public bool autoKill = true;
	public Vector3 target;
	public delegate void OnComplete();
	public OnComplete onComplete = null;

	public dotween(string s, Transform t, Vector3 v, float ti, Coroutine c) {
		name = s;
		trans = t;
		target = v;
		time = ti;
		DotweenFactory.getInstance ().Add (this);
	}
	public void setCoroutine(Coroutine c) {
		coroutine = c;
	}
	public void Kill() {
		MonoBehaviour mono = trans.GetComponent<MonoBehaviour> ();
		mono.StopCoroutine (coroutine);
		//Dotween.getInstance ().Remove (this);
	}
	public void play() {
		isPause = false;
	}
	public void Pause() {
		isPause = true;
	}
	
	public void setOnComplete(OnComplete OnC) {
		onComplete += OnC;
	}
	public void runOnComplete() {
		if (onComplete != null) {
			onComplete ();
		}
		if (autoKill) {
			Kill ();
		}
	}
	public void setAutoKill(bool b) {
		autoKill = b;
	}
}

	public static class extension_method{
		public static IEnumerator DoTranslate(this MonoBehaviour mono, dotween dot) {
			Vector3 dis = (dot.target - dot.trans.position) / dot.time;
			for (float f = 0.0f; f <= dot.time; f += Time.deltaTime) {
				dot.trans.Translate (dis * Time.deltaTime);
				//Debug.Log ("transform:");
				yield return null;
				while (dot.isPause == true) {
					yield return null;
				}
			}
			dot.runOnComplete ();
		}
		public static dotween DoTranslate(this Transform transform, Vector3 target, float time) {
			MonoBehaviour mono = transform.GetComponents<MonoBehaviour> () [0];
			dotween dot = new dotween ("DoTranslate", transform, target, time, null);
			Coroutine coroutine = mono.StartCoroutine (mono.DoTranslate (dot));
			dot.setCoroutine (coroutine);
			return dot;
		}
		public static IEnumerator DoRotate(this MonoBehaviour mono, dotween dot) {
			Vector3 angle = (dot.target - dot.trans.position) / dot.time;
			for (float f = 0.0f; f <= dot.time; f += Time.deltaTime) {
				dot.trans.Rotate (angle*Time.deltaTime);
				//Debug.Log ("rotate:");
				yield return null;
				while (dot.isPause == true) {
					yield return null;
				}
			}
			dot.runOnComplete ();
		}
		public static dotween DoRotate(this Transform transform, Vector3 target, float time) {
			MonoBehaviour mono = transform.GetComponents<MonoBehaviour> () [0];
			dotween dot = new dotween ("DoRotate", transform, target, time, null);
			Coroutine coroutine = mono.StartCoroutine (mono.DoRotate (dot));
			dot.setCoroutine (coroutine);
			return dot;
		}
		public static IEnumerator DoScale(this MonoBehaviour mono, dotween dot) {
			Vector3 scale = (dot.target - dot.trans.position) / dot.time;
			for (float f = 0.0f; f <= dot.time; f += Time.deltaTime) {
				dot.trans.localScale += scale * Time.deltaTime;
				//Debug.Log ("changeScale:");
				yield return null;
				while (dot.isPause == true) {
					yield return null;
				}
			}
			dot.runOnComplete ();
		}
		public static dotween DoScale(this Transform transform, Vector3 target, float time) {
			MonoBehaviour mono = transform.GetComponents<MonoBehaviour> () [0];
			dotween dot = new dotween ("DoScale", transform, target, time, null);
			Coroutine coroutine = mono.StartCoroutine (mono.DoScale (dot));
			dot.setCoroutine (coroutine);
			return dot;
		}
	} 

}
test.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Dotween.MyDotween;
public class test : MonoBehaviour {
	dotween dot1,dot2,dot3;
	DotweenFactory dotFact;
	// Use this for initialization
	void Start () {
		//transform.Rotate (Vector3.right * Time.deltaTime);
		dotFact = DotweenFactory.getInstance();
		dot1 = transform.DoRotate (new Vector3 (50, 50, 50), 5.0f);
		dotFact.Add (dot1);
		dot2 = transform.DoTranslate (new Vector3 (3, 4, 5), 5.0f);
		dotFact.Add (dot2);
		dot3 = transform.DoScale (new Vector3 (5, 5, 5), 5.0f);
		dotFact.Add (dot3);
	}

	// Update is called once per frame
	void Update () {
		//transform.Rotate (Vector3.right * Time.deltaTime);
	}
}





    


 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章