本篇内容主要讲解“用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后无法正常工作怎么办”有了更深的了解,不妨来实际操作一番吧!这里是亿速云网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!
亿速云「云服务器」,即开即用、新一代英特尔至强铂金CPU、三副本存储NVMe SSD云盘,价格低至29元/月。点击查看>>
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。
原文链接:https://my.oschina.net/u/4589456/blog/4614950