原地址:http://www.xuanyusong.com/archives/3088

这两天一直在研究CSLight,目前Unity热更新的方式有两种,一种是ulua这个网上的例子已经很多了。还有一种就是CSLight。其实我更希望CSLight可以趋向成熟,因为它的语法就是C#,但是有些C#的标准语法用不了。这两天我学习的做了一个例子,也把我遇到的坑记录一下。

1.在github上下载CSLight,当我把DLL拖进项目的时候会报错。原因是CSLight的dll和NGUI的冲突了,所以我直接把他的core文件夹代码全部拷贝在我的工程里面。

2.脚本可以直接就创建成.cs文件,这样可以利用unity的语法提示。李总真是太聪明了哈哈。

3.脚本与类之间传递参数。。如下脚本所示,调用脚本UIMain中的Start()方法,并且将名子作为参数传递了进去。

 
1
2
3
4
5
6
7
8
9
10
11
12
13
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System;
 
public class UICommon : MonoBehaviour {
 
    void Start ()
    {
        ScriptMgr.Instance.LoadProject();
        ScriptMgr.Instance.Execute("UIMain.Start(\""+name+"\");");
    }
}

ScriptMagr是李总封装的脚本管理类,是一个静态类。因为我们在脚本中可能会用到一些数据对象,需要提前注册一下,每次打开界面都去注册一下显然不太好。LoadProject()等于就是开游戏的时候注册一下,以后直接就去用。

 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System;
 
 
/// <summary>
/// 这个类实现脚本的Logger接口,脚本编译时的信息会从Log输出出来
/// </summary>
class ScriptLogger : CSLE.ICLS_Logger
{
 
    public void Log(string str)
    {
        UnityEngine.Debug.Log(str);
    }
 
    public void Log_Error(string str)
    {
        Debug.LogError(str);
    }
 
    public void Log_Warn(string str)
    {
        Debug.LogWarning(str);
    }
}
 
public class ScriptMgr
{
    /// <summary>
    /// ScriptMgr用单例模式,主要是为了提供C#Light Env的初始化
    /// </summary>
    public static ScriptMgr Instance
    {
        get
        {
            if (g_this == null)
                g_this = new ScriptMgr();
            return g_this;
 
        }
    }
    #region forInstance
    static ScriptMgr g_this;
    public CSLE.CLS_Environment env
    {
        get;
        private set;
    }
    private ScriptMgr()
    {
        env = new CSLE.CLS_Environment(new ScriptLogger());
        env.logger.Log("C#LightEvil Inited.Ver=" + env.version);
 
        RegTypes();
    }
    #endregion
 
 
    /// <summary>
    /// 这里注册脚本有权访问的类型,大部分类型用RegHelper_Type提供即可
    /// </summary>
    void RegTypes()
    {
        //大部分类型用RegHelper_Type提供即可
        env.RegType(new CSLE.RegHelper_Type(typeof(Vector2)));
        env.RegType(new CSLE.RegHelper_Type(typeof(Vector3)));
        env.RegType(new CSLE.RegHelper_Type(typeof(Vector4)));
        env.RegType(new CSLE.RegHelper_Type(typeof(Time)));
 
        env.RegType(new CSLE.RegHelper_Type(typeof(Debug)));
        env.RegType(new CSLE.RegHelper_Type(typeof(GameObject)));
        env.RegType(new CSLE.RegHelper_Type(typeof(Component)));
        env.RegType(new CSLE.RegHelper_Type(typeof(UnityEngine.Object)));
        env.RegType(new CSLE.RegHelper_Type(typeof(Transform)));
        env.RegType(new CSLE.RegHelper_Type(typeof(Resources)));
 
        //对于AOT环境,比如IOS,get set不能用RegHelper直接提供,就用AOTExt里面提供的对应类替换
        env.RegType(new CSLE.RegHelper_Type(typeof(int[]), "int[]"));//数组要独立注册
        env.RegType(new CSLE.RegHelper_Type(typeof(List<int>), "List<int>"));//模板类要独立注册
 
 
 
        //每一种回调类型要独立注册
        env.RegDeleType(new CSLE.RegHelper_DeleAction("Action")); //unity 用的dotnet 2.0 没有Action
        env.RegDeleType(new CSLE.RegHelper_DeleAction<int>("Action<int>")); ;
        env.RegDeleType(new CSLE.RegHelper_DeleAction<GameObject>("Action<GameObject>")); ;
 
        env.RegType(new CSLE.RegHelper_Type(typeof(Rect)));
        env.RegType(new CSLE.RegHelper_Type(typeof(PrimitiveType)));
        env.RegType(new CSLE.RegHelper_Type(typeof(UICommonEvent)));
 
 
        env.RegType(new CSLE.RegHelper_Type(typeof(UISprite)));
    }
 
    public bool projectLoaded
    {
        get;
        private set;
    }
    public void LoadProject()
    {
        if (projectLoaded) return;
        try
        {
            string[] files = System.IO.Directory.GetFiles(Application.streamingAssetsPath, "*.cs", System.IO.SearchOption.AllDirectories);
            Dictionary<string, IList<CSLE.Token>> project = new Dictionary<string, IList<CSLE.Token>>();
            foreach (var v in files)
            {
                var tokens = env.tokenParser.Parse(System.IO.File.ReadAllText(v));
                project.Add(v, tokens);
            }
            env.Project_Compiler(project, true);
            projectLoaded = true;
        }
        catch (Exception err)
        {
 
            Debug.LogError("编译脚本项目失败,请检查" + err.ToString());
        }
    }
    public void Execute(string code)
    {
        var content = env.CreateContent();
 
 
        try
        {
            var tokens = env.ParserToken(code);
            var expr = env.Expr_CompilerToken(tokens);
            expr.ComputeValue(content);
        }
        catch (Exception err)
        {
            var dumpv = content.DumpValue();
            var dumps = content.DumpStack(null);
            var dumpSys = err.ToString();
            Debug.LogError(dumpv + dumps + dumpSys);
        }
    }
}

大家注意看RegTypes()里面的注册方法。把你的脚本中用到的类,可以是unity提供的类,可以是NGUI提供的类,也可以是你自己封装的类都在这里注册一下,只有注册了你的脚本里才能使用这些方法。

假如界面Prefab在Assetbundle里面热更新了,那么脚本也对应需要更新,比如之前的界面只有一个按钮,那么新更新一个界面有两个按钮了,那么需要给新增加的按钮加监听事件。先看看下面可以热更新的这条脚本。

 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
using UnityEngine;
using System.Collections;
 
public class UIMain  {
 
    static GameObject button ;
    //UICommon里面调用脚本的Start方法,并且传入了名子的字符串
    static void Start (string root)
    {
        Transform rootUI = GameObject.Find(root).transform;
        //在Resources下面读取资源或者 Assetbundle来读取,并且实例化在场景视图中。这一句操作我觉得也可以在Unity的脚本中完成
        GameObject prefab  = Resources.Load("UIMain") as GameObject;
        GameObject gameObject = Object.Instantiate(prefab) as GameObject;
        //放在UIPanel下面,让坐标在原点
        gameObject.transform.parent = rootUI;
        gameObject.transform.localPosition = Vector3.zero;
        gameObject.transform.localScale = Vector3.one;
 
        Transform    transform = gameObject.transform;
        //找到UIPrefab下面的一个Sprite并且修改一下Sprite的名子。
        UISprite sprite = transform.Find("Sprite").GetComponent("UISprite")as UISprite;
        sprite.spriteName ="Glow";
        sprite.transform.localPosition = new Vector3 (10,100,0);
        sprite.MakePixelPerfect();
 
        //获取按钮对象,并且增加按钮的监听
        button = transform.Find("Button").gameObject;
        UICommonEvent.onClick +=Click;
        UICommonEvent.AddOnClick(button);
    }
    
    //当按钮点击的时候在脚本中得到回调
    static void Click(GameObject go)
    {
        if(go.name == button.name){
            Debug.Log("雨松MOMO提示,您点击了这个按钮喔");
        }
    }
}

代码写完你会发现几乎和C#的脚本万全一样,但是有几个比较恶心的地方。

1.不支持范型。

热更新的这样的代码就不能直接使用了。

Resources.Load<GameObject>

gameObject.GetComponent<UISprite>

2.不支持typeof()关键字

3.不支持代理事件。

用NGUI做界面,可能里面用了大量的UIEventListener ,比如按钮、精灵的位移动画等等。这种东西如果硬要在脚本里面写太蛋疼了。我觉得最好还是把代理相关的东西拿出来。

还有就是做界面的时候可能会用到一些定时器,如果用代理来做的话也需要改改,总之向这种delegate回调的地方应该都要封装成方法,然后在回调进脚本里面。

在上面的代码中,我在处理按钮的点击事件的时候。如下代码所示,我写了一条Unity的脚本,在热更新的脚本里面,通过类名.就可以直接访问方法并且传递参数。UIEventListener监听到事件以后,在回调一下热更新脚本中的方法。

 
1
2
3
4
5
6
7
8
9
class UICommonEvent{
    public static event Action<GameObject> onClick;
    static public void AddOnClick(GameObject button)
    {
        UIEventListener.Get(button).onClick =delegate(GameObject go) {
            onClick(go);
        };
    }
}

注:我也不是CSLight的高手,也是最近开始学。感谢作者给我了很大帮助,他的游戏项目中大量的使用CSLight。据说效率还可以,这两天我在好好测试一下它的效率,希望有经验的朋友可以分享一些。谢谢啦。

最后本文下载:http://pan.baidu.com/s/1c0Ehn0G

CSLight研究院之学习笔记结合NGUI(一)的更多相关文章

  1. iTween研究院之学习笔记Move移动篇

             最近项目中需要加入一些模型移动的小动画,学习过程中发现了iTween这个类库.它主要的功能就是处理模型从起始点到结束点之间运动的轨迹.(移动,旋转,音频,路径,摄像机等)它是一个开源 ...

  2. iTween研究院之学习笔记Move移动篇(一)

    http://www.xuanyusong.com/archives/2052 iTween.MoveTo(): 让模型移动到一个位置,它的底层函数是通过动态的修改模型每一帧的transform.po ...

  3. NGUI学习笔记(一)UILabel介绍

    来个前言: 作为一个U3D程序员,自然要写一写U3D相关的内容了.想来想去还是从UI开始搞起,可能这也是最直观同时也最重要的部分之一了.U3D自带的UI系统,也许略坑,也没有太多介绍的价值,那么从今天 ...

  4. NGUI学习笔记汇总

    NGUI学习笔记汇总,适用于NGUI2.x,NGUI3.x 一.NGUI的直接用法 1. Attach a Collider:表示为NGUI的某些物体添加碰撞器,如果界面是用NGUI做的,只能这样添加 ...

  5. NGUI 学习笔记实战之二——商城数据绑定(Ndata)

    上次笔记实现了游戏商城的UI界面,没有实现动态数据绑定,所以是远远不够的.今天采用NData来做一个商城. 如果你之前没看过,可以参考上一篇博客   NGUI 学习笔记实战——制作商城UI界面  ht ...

  6. NGUI 学习笔记实战——制作商城UI界面

    http://www.cnblogs.com/chongxin/p/3876575.html Unity3D的uGUI听说最近4.6即将推出,但是目前NGUI等UI插件大行其道并且已经非常成熟,所以我 ...

  7. NGUI学习笔记(五):缓动

    在Unity3D中可以使用自带的Animation制作任意形式的动画,不过我们这篇笔记主要是学习和使用NGUI提供的Tween动画.NGUI提供的Tween库功能较为简单,主要是用来实现NGUI自身需 ...

  8. opencv学习笔记(七)SVM+HOG

    opencv学习笔记(七)SVM+HOG 一.简介 方向梯度直方图(Histogram of Oriented Gradient,HOG)特征是一种在计算机视觉和图像处理中用来进行物体检测的特征描述子 ...

  9. Unity3D之UGUI学习笔记(三):EventSystem

    在UGUI中,EventSystem实现了所有关于交互方面的功能,和NGUI不一样的地方是,我们终于可以摆脱添加Box Collider了! 下面我们来学习一下. 对于按钮来说,直接有onClick的 ...

随机推荐

  1. Python核心编程--学习笔记--1--Python简介

    本章介绍了Python的背景知识,包括什么是Python.Python的起源以及Python的一些关键特性. 1 什么是Python Python是一门优雅而健壮的编程语言,它继承了传统编译语言的强大 ...

  2. autolayout 总结

    hasAmbiguousLayoutexerciseAmbiguityInLayout_autolayoutTracerecursiveDescription 第一步:更新约束,可以被认为是一个“计量 ...

  3. wpf mvvm MenuItem的Command事件

    这是一个事件的辅助类,可以通过它实现MenuItem的Command事件 public class MyCommands : Freezable, ICommand, ICommandSource { ...

  4. Android--启动系统的剪切图像功能并返回结果

    直接上代码: //启动裁剪图片 private void cropPhotoUri(Uri uri){ Intent intent = new Intent("com.android.cam ...

  5. android开发中系统自带语音模块的使用

    android开发中系统自带语音模块的使用需求:项目中需要添加语音搜索模块,增加用户体验解决过程:在网上搜到语音搜索例子,参考网上代码,加入到了自己的项目,完成产品要求.这个问题很好解决,网上能找到很 ...

  6. JVM学习总结五——性能监控及故障处理工具

    之前扯了四篇理论,这一篇终于可以动动手了.本篇我们将介绍JVM常用的一些工具,这些工具将是我们监控JVM状态.处理故障和调优分析的利器. 不过在开始之前,我还是要先车扯两句:工具终归只是帮助我们我们处 ...

  7. SQL Server数据库学习笔记-设计表时应该考虑的因素

    设计数据库其实就是设计数据库中的表.到底要注意些什么才能够设计好一个数据库呢?一个宗旨,8个建议. 一个宗旨“尽量少的表,每个表中尽量少的列,合理的表结构”. 8个建议: 第一个,首先要考虑的是咱们这 ...

  8. [转]ubuntu错误解决E: Sub-process /usr/bin/dpkg returned an error code (1)

    [转]ubuntu错误解决E: Sub-process /usr/bin/dpkg returned an error code (1) http://yanue.net/post-123.html ...

  9. EF6 在原有数据库中使用 CodeFirst 总复习(三、重建迁移)

    本来原来学的时候,挺顺利的,没想到再次使用,还是遇到很多问题,导致更新失败,所以,只能重建迁移,免得看着乱乱的. 一.删除迁移,将数据恢复到(一)结束状态 1.删除文件夹 2.删除表 3.删除列 4. ...

  10. SVN基于一个branch创建新branch

    在本地现有Branch(Checkout出来的目录)上,右键SVN,选择Branch/Tags,选择目录.比如https://sadcsc01.pmmr.com/web/Jaguar/branches ...