http://blog.csdn.net/zhaoguanghui2012/article/details/46916705
点击打开链接
前言:为什么要用Lua
首先要说,所有编程语言里面,我最喜欢的还是C#,VisualStudio+C#,只能说太舒服了。所以说,为什么非要在Unity里面用Lua呢?可能主要是闲的蛋疼。。。。。另外还有一些次要原因:
坦白来将,我并没有对现在C#与Lua互相调用的所有库进行一个仔细的调研,大概搜了一下,找到这样几个:
- slua:https://github.com/pangweiwei/slua
- Nlua:http://nlua.org/
- UniLua:https://github.com/xebecnan/UniLua
- uLua插件
以上这些方案的具体内容,不是本文的重点,这里就不说了,感兴趣的同学,点开自己去看就行了。
最后我选用了uLua,主要原因是:uLua方案比较成熟,它并没有太多自己的代码,主要是把LuaInterface和Lua解释器整合了一下,都是比较成熟的代码,相对会稳定一些。另外,个人很欣赏LuaInterface这个库。接下来我们就看一下uLua。:)
uLua的基本使用
uLua插件的使用非常简单,基本上看一下他自带的几个例子就明白了。
游戏逻辑粘合层设计
uLua插件解决了语言层面的问题:C#与LUA两种语言代码互相调用,以及参数传递等相关的一系列底层问题。而我们游戏逻辑开发中,到底如何使用LUA是上层的一个问题。下面给出我摸索的一个方案,个人认为:够简单,够清晰,是很薄很薄的一层,不可能更薄了。
使用几个LuaState?
曾经看过一个网友的方案,每次运行脚本就new一个LuaState,个人认为这种方案十分不妥。整个游戏的Lua代码应该运行在一个LuaState之上,原因有二:- 运行在同一LuaState的Lua代码才能互相调用啊。相信一个游戏总会有一定的代码量的,如果不同的lua文件之中的代码,完全独立运行,不能互相调用或者互相调用很麻烦,则游戏逻辑组织平添很多障碍;
- 混合语言编程中原则之一就是:尽量减少代码执行的语言环境切换,因为这个的代价往往比代码字面上看上去要高很多。我的目标是:既然用了Lua,就尽量把UI事件响应等游戏上层逻辑放到Lua代码中编写。
实现LuaComponent
首先说一下我的目标:- 既然C#对于Unity来说是脚本层了,那么Lua应该和C#脚本代码具有相同的逻辑地位;
- Lua整合的代码应该很少,应尽量保持简单;
- using UnityEngine;
- using System.Collections;
- using LuaInterface;
- /// <summary>
- /// Lua组件 - 它调用的Lua脚本可以实现类似MonoBehaviour派生类的功能
- /// </summary>
- [AddComponentMenu("Lua/LuaComponent")]
- public class LuaComponent : MonoBehaviour
- {
- private static LuaState s_luaState; // 全局的Lua虚拟机
- [Tooltip("绑定的LUA脚本路径")]
- public TextAsset m_luaScript;
- public LuaTable LuaModule
- {
- get;
- private set;
- }
- LuaFunction m_luaUpdate; // Lua实现的Update函数,可能为null
- /// <summary>
- /// 找到游戏对象上绑定的LUA组件(Module对象)
- /// </summary>
- public static LuaTable GetLuaComponent(GameObject go)
- {
- LuaComponent luaComp = go.GetComponent<luacomponent>();
- if (luaComp == null)
- return null;
- return luaComp.LuaModule;
- }
- /// <summary>
- /// 向一个GameObject添加一个LUA组件
- /// </summary>
- public static LuaTable AddLuaComponent(GameObject go, TextAsset luaFile)
- {
- LuaComponent luaComp = go.AddComponent<luacomponent>();
- luaComp.Initilize(luaFile); // 手动调用脚本运行,以取得LuaTable返回值
- return luaComp.LuaModule;
- }
- /// <summary>
- /// 提供给外部手动执行LUA脚本的接口
- /// </summary>
- public void Initilize(TextAsset luaFile)
- {
- m_luaScript = luaFile;
- RunLuaFile(luaFile);
- //-- 取得常用的函数回调
- if (this.LuaModule != null)
- {
- m_luaUpdate = this.LuaModule["Update"] as LuaFunction;
- }
- }
- /// <summary>
- /// 调用Lua虚拟机,执行一个脚本文件
- /// </summary>
- void RunLuaFile(TextAsset luaFile)
- {
- if (luaFile == null || string.IsNullOrEmpty(luaFile.text))
- return;
- if (s_luaState == null)
- s_luaState = new LuaState();
- object[] luaRet = s_luaState.DoString(luaFile.text, luaFile.name, null);
- if (luaRet != null && luaRet.Length >= 1)
- {
- // 约定:第一个返回的Table对象作为Lua模块
- this.LuaModule = luaRet[0] as LuaTable;
- }
- else
- {
- Debug.LogError("Lua脚本没有返回Table对象:" + luaFile.name);
- }
- }
- // MonoBehaviour callback
- void Awake()
- {
- RunLuaFile(m_luaScript);
- CallLuaFunction("Awake", this.LuaModule, this.gameObject);
- }
- // MonoBehaviour callback
- void Start()
- {
- CallLuaFunction("Start", this.LuaModule, this.gameObject);
- }
- // MonoBehaviour callback
- void Update()
- {
- if (m_luaUpdate != null)
- m_luaUpdate.Call(this.LuaModule, this.gameObject);
- }
- /// <summary>
- /// 调用一个Lua组件中的函数
- /// </summary>
- void CallLuaFunction(string funcName, params object[] args)
- {
- if (this.LuaModule == null)
- return;
- LuaFunction func = this.LuaModule[funcName] as LuaFunction;
- if (func != null)
- func.Call(args);
- }
- }
- </luacomponent></luacomponent>
这段代码非常简单,实现以下几个功能点:
- 管理一个全局的LuaState;
- 负责将MonoBehavior的调用转发到相应的LUA函数;
- 提供了GetComponent()、AddComponent()对应的LUA脚本版本接口;这点非常重要。
LUA代码约定
为了很好的和LuaComponent协作,Lua脚本需要遵循一些约定:- LUA脚本应该返回一个Table,可以是LUA的Module,也可以是任何的Table对象;
- 返回的Table对象应该含有MonoBehaviour相应的回调函数;
例如:
- require "EngineMain"
- local demoComponent = {}
- function demoComponent:Awake( gameObject )
- Debug.Log(gameObject.name.."Awake")
- end
- return demoComponent
Lua组件之间的互相调用(在Lua代码中)
基于以上结构,就很容易实现Lua组件之间的互相调用。在Demo工程中,有一个“Sphere”对象,绑定了如下脚本:
- require "EngineMain"
- local sphereComponent = {}
- sphereComponent.text = "Hello World"
- function sphereComponent:Awake( gameObject )
- Debug.Log(gameObject.name.."Awake")
- end
- return sphereComponent
- require "EngineMain"
- local demoComponent = {}
- function demoComponent:Awake( gameObject )
- Debug.Log(gameObject.name.."Awake")
- end
- function demoComponent:Start( gameObject )
- Debug.Log(gameObject.name.."Start")
- --演示LuaComponent代码互相调用
- local sphereGO = GameObject.Find("Sphere")
- local sphereLuaComp = LuaComponent.GetLuaComponent(sphereGO)
- Debug.log("Sphere.LuaDemoB:"..sphereLuaComp.text)
- end
- return demoComponent
完整版DEMO下载地址:
百度网盘链接: http://pan.baidu.com/s/1nt1eGPV 密码: 3g7b
最后,顺带总结一下:在设计上次游戏逻辑框架时,比较好的思路是:在透彻的理解Unity自身架构的前提下,在其架构下进行下一层设计,而不是想一种新的框架。因为Unity本身就是一个框架。