微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

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

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐