這次作業,老師讓我們仿製一個簡單的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); } }