用C#写的协程转换成JavaScript后无法正常工作怎么办

本篇内容主要讲解“用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);

需要修改的有:

  1. 协程函数改用 function* 定义

  2. 删除 $yield 数组的定义以及协程函数最后的返回

  3. 将 $yield.push 替换为 yield 。

===================================================

2015/07/13 22:18 更新,目前已经把这个替换工作做到菜单了,菜单是 JSB | Correct JavaScript Yield code

这个菜单会尝试替换所有在 JSBindingSetting.jsDir 目录下的所有 JavaScript 文件。你只需要在编译 SharpKit 工程后运行一下这个菜单即可,如果有错误会给出提示,如果没错,代码应该可以正常使用了!

目前这个方案算是比较完美了。

用C#写的协程转换成JavaScript后无法正常工作怎么办

下面讲一讲原理。

当我们在 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 后面可接的类型有:

  1. yield return null; // 下一帧调用 MoveNext()

  2. yield return new WWW(...); // WWW

  3. yield return new WaitForSeconds(...); // 等待一定时间

  4. 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后无法正常工作怎么办”有了更深的了解,不妨来实际操作一番吧!这里是创新互联网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!


网站题目:用C#写的协程转换成JavaScript后无法正常工作怎么办
URL网址:http://ybzwz.com/article/ihhois.html