您好,登錄后才能下訂單哦!
本篇內容主要講解“用C#寫的協程轉換成JavaScript后無法正常工作怎么辦”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學習“用C#寫的協程轉換成JavaScript后無法正常工作怎么辦”吧!
先說結論吧:用C#寫的協程轉換成 JavaScript 后,無法正常工作,必須要手動修改一點點代碼。
以下是 TestCoroutine.cs 代碼:
1 [JsType(JsMode.Clr,"../../../StreamingAssets/JavaScript/SharpKitGenerated/JSBinding/Samples/Coroutine/TestCoroutine.javascript")] 2 public class TestCoroutine : MonoBehaviour { 3 4 // Use this for initialization 5 void Start () 6 { 7 StartCoroutine(DoTest()); 8 } 9 10 // Update is called once per frame 11 void Update () 12 { 13 14 } 15 void LateUpdate() 16 { 17 jsimp.Coroutine.UpdateMonoBehaviourCoroutine(this); 18 } 19 IEnumerator WaitForCangJingKong() 20 { 21 yield return new WaitForSeconds(2f); 22 } 23 IEnumerator DoTest() 24 { 25 // test null 26 Debug.Log(1); 27 yield return null; 28 29 // test WaitForSeconds 30 Debug.Log(2); 31 yield return new WaitForSeconds(1f); 32 33 // test WWW 34 WWW www = new WWW("file://" + Application.dataPath + "/JSBinding/Samples/Coroutine/CoroutineReadme.txt"); 35 yield return www; 36 Debug.Log("Text from WWW: " + www.text); 37 38 // test another coroutine 39 yield return StartCoroutine(WaitForCangJingKong()); 40 Debug.Log("Wait for CangJingKong finished!"); 41 } 42 }
這是 SharpKit 編譯后的代碼:
1 if (typeof(JsTypes) == "undefined") 2 var JsTypes = []; 3 var TestCoroutine = { 4 fullname: "TestCoroutine", 5 baseTypeName: "UnityEngine.MonoBehaviour", 6 assemblyName: "SharpKitProj", 7 Kind: "Class", 8 definition: { 9 ctor: function (){ 10 UnityEngine.MonoBehaviour.ctor.call(this); 11 }, 12 Start: function (){ 13 this.StartCoroutine$$IEnumerator(this.DoTest()); 14 }, 15 Update: function (){ 16 }, 17 LateUpdate: function (){ 18 jsimp.Coroutine.UpdateMonoBehaviourCoroutine(this); 19 }, 20 WaitForCangJingKong: function (){ 21 var $yield = []; 22 $yield.push(new UnityEngine.WaitForSeconds.ctor(2)); 23 return $yield; 24 }, 25 DoTest: function (){ 26 var $yield = []; 27 UnityEngine.Debug.Log$$Object(1); 28 $yield.push(null); 29 UnityEngine.Debug.Log$$Object(2); 30 $yield.push(new UnityEngine.WaitForSeconds.ctor(1)); 31 var www = new UnityEngine.WWW.ctor$$String("file://" + UnityEngine.Application.get_dataPath() + "/JSBinding/Samples/Coroutine/CoroutineReadme.txt"); 32 $yield.push(www); 33 UnityEngine.Debug.Log$$Object("Text from WWW: " + www.get_text()); 34 $yield.push(this.StartCoroutine$$IEnumerator(this.WaitForCangJingKong())); 35 UnityEngine.Debug.Log$$Object("Wait for CangJingKong finished!"); 36 return $yield; 37 } 38 } 39 }; 40 JsTypes.push(TestCoroutine);
注意看 DoTest 函數和 WaitForCangJingKong 函數,他們都是協程函數。SharpKit 對其中的 yield 代碼翻譯成一個 $yield 數組,每一個 yield 指令都加到 $yield 數組中。
這樣使得我們無法與 JavaScript 的 yield 對接。這就是為什么協程編譯成 JavaScript 代碼后無法直接使用的原因。
目前,需要做點小修改就可以運行了,以下是修改過的 JavaScript 文件:
1 if (typeof(JsTypes) == "undefined") 2 var JsTypes = []; 3 var TestCoroutine = { 4 fullname: "TestCoroutine", 5 baseTypeName: "UnityEngine.MonoBehaviour", 6 assemblyName: "SharpKitProj", 7 Kind: "Class", 8 definition: { 9 ctor: function (){ 10 UnityEngine.MonoBehaviour.ctor.call(this); 11 }, 12 Start: function (){ 13 this.StartCoroutine$$IEnumerator(this.DoTest()); 14 }, 15 Update: function (){ 16 }, 17 LateUpdate: function (){ 18 jsimp.Coroutine.UpdateMonoBehaviourCoroutine(this); 19 }, 20 WaitForCangJingKong: function* (){ 21 22 yield (new UnityEngine.WaitForSeconds.ctor(2)); 23 24 }, 25 DoTest: function* (){ 26 27 UnityEngine.Debug.Log$$Object(1); 28 yield (null); 29 UnityEngine.Debug.Log$$Object(2); 30 yield (new UnityEngine.WaitForSeconds.ctor(1)); 31 var www = new UnityEngine.WWW.ctor$$String("file://" + UnityEngine.Application.get_dataPath() + "/JSBinding/Samples/Coroutine/CoroutineReadme.txt"); 32 yield (www); 33 UnityEngine.Debug.Log$$Object("Text from WWW: " + www.get_text()); 34 yield (this.StartCoroutine$$IEnumerator(this.WaitForCangJingKong())); 35 UnityEngine.Debug.Log$$Object("Wait for CangJingKong finished!"); 36 37 } 38 } 39 }; 40 JsTypes.push(TestCoroutine);
需要修改的有:
協程函數改用 function* 定義
刪除 $yield 數組的定義以及協程函數最后的返回
將 $yield.push 替換為 yield 。
===================================================
2015/07/13 22:18 更新,目前已經把這個替換工作做到菜單了,菜單是 JSB | Correct JavaScript Yield code
這個菜單會嘗試替換所有在 JSBindingSetting.jsDir 目錄下的所有 JavaScript 文件。你只需要在編譯 SharpKit 工程后運行一下這個菜單即可,如果有錯誤會給出提示,如果沒錯,代碼應該可以正常使用了!
目前這個方案算是比較完美了。
下面講一講原理。
當我們在 C# 中使用 MonoBehaviour.StartCoroutine 函數時,傳遞給他代表協程函數的 IEnumerator(后面簡稱 IE)。之后是由 Unity 內部決定何時調用 IE.MoveNext()。而這部分的源代碼我們是無法得到的。
在 JavaScript 端寫了一個模擬 Unity 功能的協程管理器。文件是:
StreamingAssets/JavaScript/Manual/UnityEngine_MonoBehaviour.javascript
(后面簡稱 B)。
這里順便提一下,當你導出 MonoBehaviour 類時,會產生
StreamingAssets/JavaScript/Generated/UnityEngine_MonoBehaviour.javascript
文件,簡稱A。B 和 A 的關系是,在includes.javascript 中,包含順序是先 A 后 B,B重寫了一些 A 的函數,并增加了一些內部函數。目前重寫的函數只有 StartCoroutine$$IEnumerator 和 StartCoroutine$$String。增加的函數有 $UpdateAllCoroutines,$updateCoroutine等等,這些就是協程管理器的內容。以下貼出代碼(可能不是最新的):
1 _jstype = undefined; 2 for (var i = 0; i < JsTypes.length; i++) { 3 if (JsTypes[i].fullname == "UnityEngine.MonoBehaviour") { 4 _jstype = JsTypes[i]; 5 break; 6 } 7 } 8 9 if (_jstype) { 10 _jstype.definition.StartCoroutine$$String = function(a0/*String*/) { 11 if (this[a0]) 12 { 13 var fiber = this[a0].call(this); 14 return this.$AddCoroutine(fiber); 15 } 16 } 17 _jstype.definition.StartCoroutine$$IEnumerator = function(a0/*IEnumerator*/) { 18 return this.$AddCoroutine(a0); 19 } 20 21 // 22 // Coroutine Scheduler 23 // 24 // REFERENCE FROM 25 // 26 // Coroutine Scheduler: 27 // http://wiki.unity3d.com/index.php/CoroutineScheduler 28 // 29 // JavaScript yield documents: 30 // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/yield 31 // 32 33 // fiber 類似于 C# 的 IEnumerator 34 _jstype.definition.$AddCoroutine = function (fiber) { 35 var coroutineNode = { 36 $__CN: true, // mark this is a coroutine node 37 prev: undefined, 38 next: undefined, 39 fiber: fiber, 40 finished: false, 41 42 waitForFrames: 0, // yield null 43 waitForSeconds: undefined, // WaitForSeconds 44 www: undefined, // WWW 45 waitForCoroutine: undefined, // Coroutine 46 }; 47 48 if (this.$first) { 49 coroutineNode.next = this.$first; 50 this.$first.prev = coroutineNode; 51 }; 52 53 this.$first = coroutineNode; 54 // NOTE 55 // return coroutine node itself! 56 return coroutineNode; 57 } 58 59 // this method is called from LateUpdate 60 _jstype.definition.$UpdateAllCoroutines = function (elapsed) { 61 // cn is short for Coroutine Node 62 var cn = this.$first; 63 while (cn != undefined) { 64 // store next coroutineNode before it is removed from the list 65 var next = cn.next; 66 var update = false; 67 68 if (cn.waitForFrames > 0) { 69 cn.waitForFrames--; 70 if (cn.waitForFrames <= 0) { 71 waitForFrames = 0; 72 this.$UpdateCoroutine(cn); 73 } 74 } 75 else if (cn.waitForSeconds) { 76 if (cn.waitForSeconds.get_finished(elapsed)) { 77 cn.waitForSeconds = undefined; 78 this.$UpdateCoroutine(cn); 79 } 80 } 81 else if (cn.www) { 82 if (cn.www.get_isDone()) { 83 cn.www = undefined; 84 this.$UpdateCoroutine(cn); 85 } 86 } 87 else if (cn.waitForCoroutine) { 88 if (cn.waitForCoroutine.finished == true) { 89 cn.waitForCoroutine = undefined; 90 this.$UpdateCoroutine(cn); 91 } 92 } 93 else { 94 this.$UpdateCoroutine(cn); 95 } 96 cn = next; 97 } 98 } 99 100 _jstype.definition.$UpdateCoroutine = function (cn) { // cn is short for Coroutine Node 101 var fiber = cn.fiber; 102 var obj = fiber.next(); 103 if (!obj.done) { 104 var yieldCommand = obj.value; 105 // UnityEngine.Debug.Log$$Object(JSON.stringify(yieldCommand)); 106 if (yieldCommand == null) { 107 cn.waitForFrames = 1; 108 } 109 else { 110 if (yieldCommand instanceof UnityEngine.WaitForSeconds.ctor) { 111 cn.waitForSeconds = yieldCommand; 112 } 113 else if (yieldCommand instanceof UnityEngine.WWW.ctor) { 114 cn.www = yieldCommand; 115 } 116 else if (yieldCommand.$__CN === true/*yieldCommand.toString() == "[object Generator]"*/) { 117 cn.waitForCoroutine = yieldCommand; 118 } 119 else { 120 throw "Unexpected coroutine yield type: " + yieldCommand.GetType(); 121 } 122 } 123 } 124 else { 125 // UnityEngine.Debug.Log$$Object("cn.finished = true;"); 126 cn.finished = true; 127 this.$RemoveCoroutine(cn); 128 } 129 } 130 131 _jstype.definition.$RemoveCoroutine = function (cn) { // cn is short for Coroutine Node 132 if (this.$first == cn) { 133 this.$first = cn.next; 134 } 135 else { 136 if (cn.next != undefined) { 137 cn.prev.next = cn.next; 138 cn.next.prev = cn.prev; 139 } 140 else if (cn.prev) { 141 cn.prev.next = undefined; 142 } 143 } 144 cn.prev = undefined; 145 cn.next = undefined; 146 } 147 }
目前支持的 yield return 后面可接的類型有:
yield return null; // 下一幀調用 MoveNext()
yield return new WWW(...); // WWW
yield return new WaitForSeconds(...); // 等待一定時間
yield return new StartCoroutine(...); // 串連另一個協程
C# 協程和 JavaScript 協程有一個區別:C#是協程初始就調用了 MoveNext(),JavaScript 需要初始調用 next() 才能和 C# 匹配(現在沒有調用,因為一幀的時間也挺快的,效果差不多一樣)。
另外,看前面的 C# 代碼有這樣的代碼:
1 void LateUpdate() 2 { 3 jsimp.Coroutine.UpdateMonoBehaviourCoroutine(this); 4 }
現在因為我們自己要管理協程,所以需要有一個 Update 入口。如果想讓 JavaScript 協程正常工作,必須在某個地方調用協程管理器的 Update。現在我是把他放在 LateUpdate 函數中,如果你想換地方,也是可以的。
在 C# 中,jsimp.Coroutine.UpdateMonoBehaviourCoroutine 函數并不做任何事情,只有當運行 JavaScript 版本時,才有做事情。JavaScript 的實現是在這個文件中:
StreamingAssets/JavaScript/JSImp/Coroutine.javascript
1 if (typeof(JsTypes) == "undefined") 2 var JsTypes = []; 3 var jsimp$Coroutine = { 4 fullname: "jsimp.Coroutine", 5 baseTypeName: "System.Object", 6 staticDefinition: { 7 UpdateMonoBehaviourCoroutine: function (mb){ 8 mb.$UpdateAllCoroutines(UnityEngine.Time.get_deltaTime()); 9 } 10 }, 11 assemblyName: "SharpKitProj", 12 Kind: "Class", 13 definition: { 14 ctor: function (){ 15 System.Object.ctor.call(this); 16 } 17 } 18 }; 19 20 // replace old Coroutine 21 jsb_ReplaceOrPushJsType(jsimp$Coroutine);
這個文件同樣在 includes.javascript 中進行了包含。
看第8行,調用了 $UpdateAllCoroutines 函數更新協程管理器。
對于 WaitForSeconds 類,C#中并沒有暴露任何接口。我們無法判斷一個 WaitForSeconds 是否時間已到。所以我又自定義了這個類,來達到這個目的。
文件是:StreamingAssets/JavaScript/Manual/UnityEngine_WaitForSeconds.javascript
1 _jstype = undefined; 2 for (var i = 0; i < JsTypes.length; i++) { 3 if (JsTypes[i].fullname == "UnityEngine.WaitForSeconds") { 4 _jstype = JsTypes[i]; 5 break; 6 } 7 } 8 9 if (_jstype) { 10 11 _jstype.definition.ctor = function(a0) { 12 this.$totalTime = a0; 13 this.$elapsedTime = 0; 14 this.$finished = false; 15 } 16 17 _jstype.definition.get_finished = function(elapsed) { 18 if (!this.$finished) { 19 this.$elapsedTime += elapsed; 20 if (this.$elapsedTime >= this.$totalTime) { 21 this.$finished = true; 22 } 23 } 24 return this.$finished; 25 } 26 }
這個文件也很簡單,只是記錄初始時的時間,后面更新時時間進行遞增。get_finished() 函數被協程管理器用于判斷時間是否已到。
到此,相信大家對“用C#寫的協程轉換成JavaScript后無法正常工作怎么辦”有了更深的了解,不妨來實際操作一番吧!這里是億速云網站,更多相關內容可以進入相關頻道進行查詢,關注我們,繼續學習!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。