菜鸟学习 - Unity中的热更新 - Lua和C#通信

孙广东 2015-4-6

热更新我是个菜鸟,感谢网上的各位的奉献,这次又当一回搬运工。

准备:

1、了解Lua的语法

推荐书籍《Lua程序设计 第二版》

2、使用ULua插件进行通信

尽量早上真机。因为Bug问题特别多。

大杂烩:

更新LUa其实也是更新资源。 Lua被看作一个资源么。Lua代码都是运行时才编译的,不运行的时候就如同一张图片、一段音频一样,都是文件资源;所以更新逻辑只需要更新脚本,不需要再编译,因而Lua能轻松实现“热更新”。运行效率由于使用反射,所以成为它与生俱来的诟病。目前有对他的改进,像C#ToLua、Slua。但是Ulua是很成熟的,这个对于项目开发我认为暂时比性能重要!很多人在用,很多坑都已经被人采完。个人比较看好Slua的发展。当然了还有李总的L#走的就不是Lua的路。

网上还有说nLua的,我是新手不了解。 看看权威的解释:

ulua包含两种c模式(luajit版+原生luavm版),加之tolua c#提供了直接访问渠道。所以追求效率的,请选用ulua。但是ulua因为底层使用luajit,而luajit目前不能在WP平台使用,所以如果ulua支持WP平台需要第二种原生luavm的底层库。

nlua包含2种模式(KeraLua c版本)(KopiLuac#版本),它支持全平台,因为c版本底层用的原始的luavm(非luajit)。但是缺少tolua c#的支持,因此效率略低于ulua,但是支持WP平台(其他平台也支持)。

插件:

下载地址

链接:http://pan.baidu.com/s/1dDjINLn

密码: f29h

ULua = Lua + LuaJit(解析器、解释器) +LuaInterface(与C#通信用这个);

DLL放在 Plugins文件夹下,如果导入后报错,就重启Unity

主要内容:

先感谢这篇博文的分享者:

http://www.unitymanual.com/blog-27966-2568.html

Ulua的使用流程一般为:

实例化LuaState对象(newLuaState())  à  加载Lua代码(LuaState.DoString(string))à调用Lua代码中的方法(GetFunction(string),LuaFunction.callFunction(string))。其中加载Lua代码这一块,可以直接赋一段Lua代码字符串,也可以指定一个Lua脚本文件。为了热更新,应当采用第二种加载方法,即创建一个Lua脚本文件。由于Unity不支持扩展名为lua的文件,所以可将Lua脚本扩展名定为txt(纯文本文件),并用unity的TextAsset列表负责记录所有脚本文件。建议列表中给每个脚本搭配一个string类型的ID,这样凭此ID即可加载正确的lua脚本;另外在LuaState类中新增一个String类型的public成员,赋值为该ID。这样一旦某个Lua脚本在运行时报错,可根据输出的ID值判断是哪个Lua脚本有错误。

关于Lua里的预处理:

luanet.load_assembly("Assembly-CSharp")
luanet.load_assembly(‘UnityEngine‘)
Vector2 = luanet.import_type(‘UnityEngine.Vector2‘)
Vector3 = luanet.import_type(‘UnityEngine.Vector3‘)
GameObject = luanet.import_type(‘UnityEngine.GameObject‘)
luanet.import_type(‘System.Collections.Generic.List‘)
Debug = luanet.import_type(‘UnityEngine.Debug‘)

这些都是常用的Lua预处理,建议单独写个Lua脚本记录这些,以后加载其他Lua脚本前都先加载一下这个脚本。自定义C#类也是可以import的,import操作是Lua调用C#的前提。当然在C#中实例化的LuaState也可以预定义一些Lua全局变量,这都是在C#里完成的,比如:

void SetLuaData(LuaState lua)
{
       lua["transform"] = transform;
       lua["gameObject"] = gameObject;
}

预定义了两个变量,一个是transform,一个是gameObject。

关于Lua里的全局变量:

Lua里所有的字符串,如果不是关键字或者运算符,就都是变量;这些变量中,凡是没用local关键字修饰的,就是全局变量,反之,则是局部变量。每个LuaState对象,当它加载过Lua代码以后,它里面定义的全局变量,在这个对象生命期内是一直存在的。如果两次调用这个LuaState的某方法,第一次将某全局变量进行了修改,那么第二次,这个全局变量会在第一次修改的基础上继续修改。

关于Lua在Unity的适用范围:

虽然Lua可以负责Unity工程的任何模块,但是出于对游戏性能的考虑,尽量少的低频率的调用Lua,比如尽量少在Update函数中调用Lua、循环利用已经实例化过的LuaState避免浪费资源。对于那些不需要高效运算的模块,比如UI部分,就可以放心大胆的使用Lua。

关于Lua与NGUI的适配:

调用Lua的主要方式就是callFunction,而对于NGUI来说,一般都是按钮触发某C#脚本的函数,那么如何用按钮触发Lua的函数呢?这就需要有个C#脚本作为“中介”,这个“中介”需要有自己的LuaState实例,当执行“中介”的某个方法时,由该方法调用callFunction。至于具体调用Lua脚本的哪个方法,可以通过传参的方式告诉这个“中介”——修改NGUI的ButtonMessage类,加入新的public string LuaFunctionName成员,以后由它来制定要调用的Lua方法名就好了。而通过NGUI的UIButton等调用C#的方法,也是同理的(NGUI3.5.6版以后,可以给UIButton等等组件的触发方法添加参数了)。

关于Lua与SimpleJSON的适配:

SimpleJSON是一个开源的JSON库,这个详见之前的博客。由于Lua语言无法理解C#里面的属性,所以凡是SimpleJSON里的public A{get,set}这种,要新写一些接口函数返回它们的取值,以供Lua调用。而且Lua调用C#里经过很多重载的函数,也经常判断错误(这是个bug?),所以干脆多弄一些函数名,避免调用错误。

关于Lua与Lua之间的调用:

在Unity中使用Ulua,想要让两个Lua脚本彼此调用是很难的,需要通过C#作为“中介”,而且Lua不支持C#的泛型,所以不能用GetComponnet<类名>()的形式,只能用GetComponnet (“类名”)的形式去获取组件。两个Lua脚本相互传参也很麻烦,因为有了C#脚本作为“中介”,所以不能传tabel类型的参数,我的做法是将若干参数拼成一个json字符串传递过去,另一方再解json包。感觉有点蛋疼。不过这种情况并不多见,都是可以避免的(比如一个模块只用一个大Lua脚本,各自独立减少沟通)。

关于Lua编辑器:

个人使用的是notepad++,也有很多人用sublime,代码折叠这块notepad++更好一些,而sublime在功能上貌似更强大一些。   unity项目开发基本使用VS系列。所以

BabeLua是一款基于VS2012/2013(简称VS)的免费开源的Lua集成开发环境,在Lua编辑和调试方面,具有如下功能和特性:

●Lua语法高亮

●语法检查

●自动补全

●一键注释

●格式化代码

●自定义代码折叠

●工程管理

●快速搜索和跳转

●文件大纲

●注入宿主程序内对Lua脚本进行调试

●设置断点观察变量值

●查看堆栈信息

版本更新日志

https://babelua.codeplex.com/

或者 开源的Lua轻量级编辑器:ZeroBraneStudio

Unity编程的亲们,有没有在Unity运行状态下修改并保存了脚本,然后切回来Unity直接卡死的惨痛经历?使用Lua,你将告别这一现象,有木有心动呢?

接下来感谢“小阿哥”的视频分享,虽然内容讲的支离破碎。

  1. C#如何访问Lua中的属性

1) .C#如何访问LUA中的属性?

2) .C#如何访问LUA中的函数?

3) .C#如何访问LUA中的表?

test0.lua文件

--[[
 @author:小阿哥
 @des:测试C#访问LUA的一些东东
 @date:2015-03-21
--]]
config={name="小阿哥",age=24,qq="247124629"};

Name="小阿哥";
Age=24;
isBoy=true;

function PrintFromLua(a)
  print("打印信息。我来息LUA...CS传的参数的值:",a);
  return 200;
end

print("test0.lua执行完毕..");

Main.cs文件     随便挂在一个对象上测试吧!

using UnityEngine;
using System.Collections;
using LuaInterface;
/**
 * @author:小阿哥
 * @des:讲通信基础版
 * @date:2015-03-21
 */
public class Main : MonoBehaviour {

    private static Main instance;
    public string ss;
    public TextAsset tt;

	// Use this for initialization
	void Start () {
        instance=this;
	}

    public static Main GetInstance()
    {
        return instance;
    }

    //C#调用LUA
    void testCSharp_GoLua()
    {
        LuaState lua=new LuaState();
        lua.DoString("print ‘hello world‘");
    }

    //C#调用LUAFile
    void testCSharp_GoLuaFile()
    {
        LuaState lua=new LuaState();
        lua.DoFileFromAge(this,"Test0.lua");
    }

    //C#调用LUAInfor
    void testCSharp_GoLuaInfor()
    {
        LuaState lua=new LuaState();
        lua.DoFileFromAge(this,"Test0.lua",delegate(object[] obj) {
            //访问LUA中的表
            LuaTable configTable=lua.GetTable("config");
            Debug.Log("name:"+configTable["name"]);
            Debug.Log("age:"+configTable["age"]);
            Debug.Log("qq:"+configTable["qq"]);
            //访问LUA中的基础属性
            Debug.Log("Name:"+lua.GetString("Name"));
            Debug.Log("Age:"+lua.GetNumber("Age"));
            Debug.Log("isBoy:"+lua["isBoy"]);
            //访问Lua中的函数
            LuaFunction luaFun=lua.GetFunction("PrintFromLua");
            if(luaFun!=null)
            {
                System.Object[] obResult=luaFun.Call(100);
                Debug.Log("obResult:"+obResult[0]);
            }
       });
    }
    void OnGUI()
    {
        if(GUILayout.Button("第一节.C#调用LUA"))
        {
            testCSharp_GoLua();
        }

        if(GUILayout.Button("第二节.C#调用LUA File"))
        {
            testCSharp_GoLuaFile();
        }

        if(GUILayout.Button("第三节.C#调用LUA 信息"))
        {
            testCSharp_GoLuaInfor();
        }
    }
}
  1. Lua如何访问C#中的属性

1) .LUA如何访问Unity提供的对象?

a.如何new系统对象?

b.如何访问对象的属性?

c.如何访问对象的函数?

2) .LUA如何访问在C#中自定义的对象?

a.如何new自定义对象?

b.如何访问对象的属性?

c.如何访问对象的函数?

Hero.cs文件

using UnityEngine;
using System.Collections;

/**
 * @author:小阿哥
 * @des:讲通信基础版
 * @date:2015-03-21
 */
public class Hero{

    private string _name;
    private int _hp,_ap,_mp;

    public Hero(string name,int hp,int ap,int mp)
    {
        this._name=name;
        this._hp=hp;
        this._ap=ap;
        this._mp=mp;
    }
    public int Hp
    {
        get
        {
            return this._hp;
        }
    }

    public int Ap
    {
        get
        {
            return this._ap;
        }
    }

    public int Mp
    {
        get
        {
            return this._mp;
        }
    }

    public void PrintInfor()
    {
        Debug.Log("hp:"+this._hp+"___ap:"+this._ap+"___mp:"+this._mp);
    }

    public int AddHp(int add)
    {
        Debug.Log("Lua调用CS的AddHp Lua传的参数 add:"+add);
        this._hp+=add;
        return _hp;
    }

    public int AddAp(int add)
    {
        Debug.Log("Lua调用CS的AddAp Lua传的参数 add:"+add);
        this._ap+=add;
        return _ap;
    }

    public int AddMp(int add)
    {
        Debug.Log("Lua调用CS的AddMp Lua传的参数 add:"+add);
        this._mp+=add;
        return _mp;
    }

    public void desroy()
    {
        this._name=null;
    }
}

在上一个Main.cs文件中添加代码:

    //lua调用C#
    void testLua_GoCSharp()
    {
        LuaState lua=new LuaState();
        lua.DoFileFromAge(this,"test1.lua");
    }
    void OnGUI()
    {
        if(GUILayout.Button("第四节.LUA调用C# 信息"))
        {
            testLua_GoCSharp();
        }
    }

test1.cs文件

--[[
 @author:小阿哥
 @des:测试LUA访问C#的一些东东
 @date:2015-03-21
--]]

--访问C#中的系统的对象
luanet.load_assembly("UnityEngine");
GameObject=luanet.import_type("UnityEngine.GameObject");
Vector3=luanet.import_type("UnityEngine.Vector3");

local newObj=GameObject(‘ageObj‘);
--访问C#中的系统的对象的函数
newObj:addComponent("Animation");
--访问C#中的系统的对象的属性
newObj.transform.position=Vector3(10,20,30);

--访问C#中的自定义的对象
Hero=luanet.import_type("Hero");
--newC#中的自定义的对象
local heroa=Hero("小阿哥",100,200,300);
--访问C#中的自定义的对象的函数
heroa:PrintInfor();

--访问C#中的自定义的对象的函数 来回传值
print("LUA hp:",heroa:AddHp(10));
print("LUA ap:",heroa:AddAp(20));
print("LUA mp:",heroa:AddMp(30));

print("test1.lua执行完毕..");

3、Demo例子


所有的业务逻辑放在Lua这面,CS那面通过LuaInterface对其进行调用。Lua是脚本语言,需要宿主语言(C#)。

1) .封装Lua版的MonoBehaviour

2) .接下来所有的业务逻辑均使用LUA进行编写

Demo.lua 文件:

--[[
 @author:小阿哥
 @des:主要使用LUA的进行编码
 @date:2015-03-21
--]]
luanet.load_assembly("UnityEngine");
GameObject=luanet.import_type("UnityEngine.GameObject");
Vector3=luanet.import_type("UnityEngine.Vector3");
PrimitiveType=luanet.import_type("UnityEngine.PrimitiveType");

local boxGameObj;

function Start()
  boxGameObj=GameObject.CreatePrimitive(PrimitiveType.Cube);
  boxGameObj.name="ageBox100";
end

function Update()
end

function FixedUpdate()
  boxGameObj.transform:Rotate(Vector3.up,5);
end

function OnGUI()
    --print("OnGUI..");
end

function OnDestroy()
    --print("OnDestroy..");
end

using UnityEngine;
using System.Collections;
using LuaInterface;

Demo.cs文件       挂在摄像机上做测试就行!

/**
 * @author:小阿哥
 * @des:测试小例子
 * @date:2015-03-21
 */
public class Demo : MonoBehaviour {

    private LuaState lua;

    private LuaFunction funStart;
    private LuaFunction funUpdate;
    private LuaFunction funFixedUpdate;
    private LuaFunction funOnGUI;
    private LuaFunction funOnDestroy;

	// Use this for initialization
	void Start () {
        lua=new LuaState();
        lua.DoFileFromAge(this,"Demo.lua",delegate(System.Object[] obj) {
            funStart=lua.GetFunction("Start");
            funUpdate=lua.GetFunction("Update");
            funFixedUpdate=lua.GetFunction("FixedUpdate");
            funOnGUI=lua.GetFunction("OnGUI");
            funOnDestroy=lua.GetFunction("OnDestroy");

            if(funStart!=null)
            {
                funStart.Call();
            }
        });

	}

	// Update is called once per frame
	void Update () {
        if(funUpdate!=null)
        {
            funUpdate.Call();
        }
	}

    void FixedUpdate()
    {
        if(funFixedUpdate!=null)
        {
            funFixedUpdate.Call();
        }
    }

    void OnGUI()
    {
        if(funOnGUI!=null)
        {
            funOnGUI.Call();
        }
    }

    void OnDestroy()
    {
        if(funOnDestroy!=null)
        {
            funOnDestroy.Call();
        }
    }
}

最后感谢 骏擎网络 的突出贡献吧

游戏已经开发到后期,如何接入ulua?

(1)活动

(2)计时器(单位秒)驱动lua的update

(3)网络管理留给lua能跟服务器交互的接口(现在未必用得到的)。活动这部分变数最大,很多问题上线前是无法预知的,比如上线如果发生数据不理想,或者非常火爆,这些情况无法预知,根据这些情况做活动调整,这些很容易有更新需求。而且未必前期都能想到坐进去。运营策划都是要根据在线运营情况做未知的活动调整。还有一部分我称之为,程序给自己留的后路,如果绝大部分都是c#的话,很有可能产生上线后产生bug,比如:新手引导,在什么地方卡住了等。客户端启动一个计时器,驱动lua的一个onTimer,在里面根据游戏运行情况,动态调整对游戏的控制。还有就是多给自己留一个协议的接口给lua备份用。

最新版ulua+cstolua的性能速度超slua N倍

最新版的ulua+cstolua,效率已经火箭式提升,早已阔别三日当刮目相看了,大家关心的GC问题已经彻底消除,GC早已将为0。加上阿萌已经将众多使用频繁的Unity类用lua重写,这样完全消除了P/Invoke的代价(不知道slua会不会发现了,再次山寨),甚至cstolua都把string都做了优化,可以在lua中转换(极限了),综上所述,ulua已经是当前最快的,没有之一!!!

请直接下载uLua_v1.08.zip,集成cstolua、protobuf-lua-gen、ios64、Intel x86, cjson, pbc, lpeg, sqlite3等支持,从网络协议到正则表达式,再到数据库,ulua已经为在unity lua环境开发集成了一个既兼顾效率(都是成熟的原生C的库)又完善(一应俱全)的稳定开发平台,使您开发从小游戏到大型网游都有相应的方案支持!!这也是ulua所特有的哦~~


时间: 2024-10-15 06:18:26

菜鸟学习 - Unity中的热更新 - Lua和C#通信的相关文章

菜鸟学习 - Unity中的热更新 - LuaInterface用户指南

[由于学习,所以翻译!] 1.介绍 LuaInterface 是 Lua 语言和 Microsoft.NET 平台公共语言运行时 (CLR) 之间的集成库. 非常多语言已经有面向 CLR 编译器和 CLR 实现,已经存在为微软windows. BSD 操作系统和 Linux 操作系统. Lua是一个为扩展应用程序而设计的编程语言,解释运行,非常容易嵌入的库.具体的信息能够參考Lua'sreference manual. 以下的部分介绍如何编译和安装LuaInterface.第3部分包括了在CLR

菜鸟学习 - Unity中的热更新 - 更新思路

我们游戏对资源的更新并没有使用AssetBundle. 而是采用了下面的解决方案(客户端C# 和服务器 Java的约定 ,传输协议都是json): 第一步:客户端与服务器约定一个版本号.第二步:客户端向服务器发送本地的一个版本号(不同的玩家,本地的版本号的信息肯定也不一样),服务器比对最新的版本号与客户端本地的版本号,过滤出需要更新的文件列表.把最新的版本号和需要更新的文件列表发送给客户端.第三步:客户端下载需要更新的文件列表,直到下载完毕,然后重写本地的版本号. 对脚本的更新也是采用成熟的Ul

Unity官方公布热更新方案性能对比

孙广东  2016.3.11 Unity应用的iOS热更新 作者:丁治宇 Unity TechnologiesChina Agenda ?  什么是热更新 ?  为何要热更新 ?  如何在iOS 上对Unity 应用进行热更新 ?  支持Unity iOS 热更新的各种Lua 插件的对比 什么是热更新 ? 广义定义 ? 无需关闭服务器,不停机状态下修复漏洞,更新资源等,重点是更新逻辑代码. ? 狭义定义( iOS热更新) ? 无需将代码重新打包提交至AppStore,即可更新客户端的执行代码,即

Unity官方发布热更新方案性能对照

孙广东  2016.3.11 Unity应用的iOS热更新 作者:丁治宇 Unity TechnologiesChina Agenda ?  什么是热更新 ?  为何要热更新 ?  怎样在iOS 上对Unity 应用进行热更新 ?  支持Unity iOS 热更新的各种Lua 插件的对照 什么是热更新 ? 广义定义 ? 无需关闭server,不停机状态下修复漏洞,更新资源等,重点是更新逻辑代码. ? 狭义定义( iOS热更新) ? 无需将代码又一次打包提交至AppStore,就可以更新clien

Unity实现c#热更新方案探究(一)

最近研究了一下如何在unity中实现c#的热更新,对于整个DLL热更新的过程和方案有一个初步的了解,这儿就写下来,便于后续的深入调查和方案选择. 一.C# DLL的动态加载和卸载 既然要热更新,那么就是动态的加载c#的DLL,所以第一步就是研究如何实现DLL的动态加载和卸载. 在CLR Via C#中,对于DLL的加载有详细的讲解,这儿就不再长篇幅的讲解整个过程,简单的来说,在C#的工程中,都会生成一个默认的程序域appDomain,就叫做DefaultAppDomain吧,在这个程序域的基础上

热更新--lua

参考链接:https://www.cnblogs.com/imteach/p/11267312.html 介绍: Lua的几个变种:XLua.ToLua(原uLua)和Slua都可以做Unity热更,而ToLua更是提供了一个简易的热更框架--LuaFramework_UGUI ToLua搭建了一个Lua语言与Unity中c#语言沟通的桥梁,借助ToLua,你可以在C#语言中调用Lua方法,也可以在Lua语言中调用C#方法. 而LuaFramework_UGUI则是基于ToLua的这种能力实现的

Unity实现c#热更新方案探究(二)

一.IOS对DLL热更新的禁止 紧接上文,继续对C#热更新的研究.上文中,已经说了如何基于appDomain来实现对DLL的加载和卸载,进一步,可以在unity工程中,将Dll打包成资源,通过Assembly.Load的方式加载DLL来实现更新.那么为什么IOS中就不能这样操作了呢? 推荐阅读文章: Mono为何能跨平台?聊聊CIL(MSIL) - 陈嘉栋 - 博客园 偷了我的热更新?Mono,JIT,iOS 这两篇文章,对整个IOS不能热更新的缘由,有详细的讲解,对于Mono的JIT编译模式和

[Unity热更新]ulua学习笔记01:一个小坑

看了一下以前写的文章,发现都好水啊..我也想写出能真正帮到别人的文章,但没办法啊,自己的脑子太笨了..希望可以通过不断学习,终有一天能写出点好文章! 最近在学习unity中的热更新,既然大神都说ulua效率最高(就现在来说),那就学习ulua吧!首先要下载的是ulua的包,现在的最新版本是1.08,而且根据官网上说的,网上流行的1.03/1.05含有大量的bug,所以我选择了最新的版本,谁知道就掉进了第一个坑! 因为是菜鸟,所以先复制一下网上的代码看看效果如何,结果碰到这样的错误: 搜索了这些错

Unity3D游戏开发之Lua与游戏的不解之缘终结篇:UniLua热更新完全解读

----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- 喜欢我的博客请记住我的名字:秦元培,我的博客地址是blog.csdn.net/qinyuanpei. 转载请注明出处,本文作者: