JSBinding + SharpKit / 原理篇:Delegate

 

以 NGUI 的 UIEventListener 为例:

有一个类:

 1 using SharpKit.JavaScript;
 2 using UnityEngine;
 3 using System.Collections;
 4 
 5 [JsType(JsMode.Clr,"../StreamingAssets/JavaScript/SharpKitGenerated/z_temp/test0610.javascript")]
 6 public class test0610 : MonoBehaviour {
 7     public UIButton btn;
 8 
 9     void Start () 
10     {
11         // 在此注册回调
12         UIEventListener.Get(btn.gameObject).onClick = this.OnClick; // (*)
13     }
14 
15     void OnClick(GameObject go)
16     {
17         Debug.Log("onclick ");
18     }
19 }

这个类有一个 public UIButton btn;  变量,可以在 Inspector 里赋值。我们用这个类来响应 btn 的点击事件。重点就是第12行。

 

生成的JS如下:

 1 if (typeof(JsTypes) == "undefined")
 2     var JsTypes = [];
 3 var test0610 = {
 4     fullname: "test0610",
 5     baseTypeName: "UnityEngine.MonoBehaviour",
 6     assemblyName: "SharpKitProj",
 7     Kind: "Class",
 8     definition: {
 9         ctor: function (){
10             this.btn = null;
11             UnityEngine.MonoBehaviour.ctor.call(this);
12         },
13         Start: function (){
14             UIEventListener.Get(this.btn.get_gameObject()).onClick = $CreateDelegate(this, this.OnClick); // (*)
15         },
16         Update: function (){
17         },
18         OnClick: function (go){
19             UnityEngine.Debug.Log$$Object("onclick ");
20         }
21     }
22 };
23 JsTypes.push(test0610);

 

看 JS代码 第14行,将 onClick 赋值为 $CreateDelegate 的返回值

$CreateDelegate 的作用是返回一个函数,具体可以看 jsclr.javascript 文件里这个函数的定义。这里不讨论这个函数的实现细节,只要知道他返回一个函数就可以了。这个函数传到 C# 后就变成一个ID。

 

当然,如果要让这段JS可以正常执行,当然要将 UIEventListener 配置到 JSBindingSettings.classes 数组让他导出,来看一下 onClick 这个字段的C#代码:

 1 public static UIEventListener.VoidDelegate UIEventListener_onClick_GetDelegate_member2_arg0(CSRepresentedObject objFunction)
 2 {
 3     if (objFunction == null || objFunction.jsObjID == 0)
 4     {
 5         return null;
 6     }
 7     UIEventListener.VoidDelegate action = (go) => 
 8     {
 9         JSMgr.vCall.CallJSFunctionValue(0, objFunction.jsObjID, go);
10     };
11     return action;
12 }
13 static void UIEventListener_onClick(JSVCall vc)
14 {
15     if (vc.bGet) {
16         UIEventListener _this = (UIEventListener)vc.csObj;
17         var result = _this.onClick;
18         JSMgr.vCall.datax.setObject((int)JSApi.SetType.Rval, result);
19     }
20     else {
21         UIEventListener _this = (UIEventListener)vc.csObj;
22         _this.onClick = JSDataExchangeMgr.GetJSArg<UIEventListener.VoidDelegate>(()=>{
23     if (JSApi.isFunctionS((int)JSApi.GetType.Arg))
24         return UIEventListener_onClick_GetDelegate_member2_arg0(JSApi.getFunctionS((int)JSApi.GetType.Arg));
25     else
26         return (UIEventListener.VoidDelegate)vc.datax.getObject((int)JSApi.GetType.Arg);
27 })
28 ;
29     }
30 }

第22行在赋值 onClick 字段。因为 onClick 是 Delegate,所以赋值也要给他一个 Delegate,这个 Delegate 是由函数 UIEventListener_onClick_GetDelegate_member2_arg0 返回的(第1行)。

第24行首先调用 JSApi.getFunctionS(..) 获取 JS 函数 ID。CSRepresentedObject 只是对 JS 对象的一个封装而已。UIEventListener_onClick_GetDelegate_member2_arg0 拿到这个函数ID后,

构造了一个 UIEventListener.VoidDelegate 类型的 Delegate ,最终赋值给了 UIEventListener.onClick。

 

以上是比较简单的情况:将 JS 函数存储在 C#。

再来看一种情况,将 C# 函数存储在 JS。

// C#

1 TweenEasingCallback func = TweenEasingFunctions.GetFunction(this.EaseType );
2 
3 transform.TweenPosition()
4 .SetEndValue( transform.position + ( Vector3.right * 9f ) )
5 .SetDelay( 0.5f, false )
6 .SetDuration( 1.33f )
7 .SetEasing( func )
8 .SetLoopType( TweenLoopType.Loop )
9 .Play();

// JS代码

1 var func = DaikonForge.Tween.TweenEasingFunctions.GetFunction(this.EaseType); // get a delegate from C# (1)
2 TweenTransformExtensions.TweenPosition$$Transform(this.get_transform())
3     .SetEndValue(UnityEngine.Vector3.op_Addition(this.get_transform().get_position(), (UnityEngine.Vector3.op_Multiply$$Vector3$$Single(UnityEngine.Vector3.get_right(), 9))))
4     .SetDelay$$Single$$Boolean(0.5, false)
5     .SetDuration(1.33)
6     .SetEasing(func) // give back to C# (2)
7     .SetLoopType(1)
8     .Play();

 

这种情况下也是要事先将 TweenEasingFunctions 配置到 JSBindingSettings.classes,让他导出,JS才可以使用。

// TweenEasingFunctions.GetFunction 的绑定代码:

 1 static bool TweenEasingFunctions_GetFunction__EasingType(JSVCall vc, int argc)
 2 {
 3     int len = argc;
 4     if (len == 1) 
 5     {
 6         DaikonForge.Tween.EasingType arg0 = (DaikonForge.Tween.EasingType)JSApi.getEnum((int)JSApi.GetType.Arg);
 7         JSMgr.vCall.datax.setObject((int)JSApi.SetType.Rval, DaikonForge.Tween.TweenEasingFunctions.GetFunction(arg0));
 8     }
 9 
10     return true;
11 }

可以看到,当 JS 从 C# 获得一个 delegate 时,会调用 JSDataExchange.setObject 函数。

这里不贴出 setObject 的具体代码,请查看源代码。

在 setObject 函数中,会判断,如果这个对象是个 delegate,那么就创建一个 JSRepresentedObject 对象返回给 JS,同时保存这2者的对应关系。

JSRepresentedObject 的定义在 StreamingAssets/JavaScript/SharpKit/myclrhandler.javascript 最前面。

那么,JS拿到这个对象后,是无法使用的。因为他是C#这边的函数。他只能把这个东西再还给C#。请看上面JS代码标记 (2) 那一句。当他返回给C#时,C#可以根据之前存储的对应关系找到 Delegate。

 

 

 

返回首页:

Unity代码热更新方案 JSBinding + SharpKit 首页

posted on 2015-06-11 10:08  AnswerWinner  阅读(1161)  评论(0编辑  收藏  举报

导航