SLua學(xué)習(xí)前提:
1.熟悉Lua基本語法
2.熟悉Lua與C交互(C#交互類似)
3.熟悉Unity基本組件以及在
簡單使用
1.Slua下載配置
2.導(dǎo)出Lua接口
SLua菜單
All->導(dǎo)出/清空全部接口
Unity->導(dǎo)出UnityEngine或者UnityEngine.UI下類的接口寻馏。
Custom->自定義導(dǎo)出類
3rdDll->導(dǎo)出動(dòng)態(tài)鏈接庫
- test one 創(chuàng)建一個(gè)cube
在C#中如何創(chuàng)建呢?
void Start () {
GameObject.CreatePrimitive(PrimitiveType.Cube);
}
Lua中創(chuàng)建方式
import "UnityEngine" --導(dǎo)包
UnityEngine.GameObject.CreatePrimitive(UnityEngine.PrimitiveType.Cube)
同樣很簡單霞赫。
那么C#該如何調(diào)用Lua文件呢?
首先回憶一下C中調(diào)用方式
//初始化全局L
LuaState L = luaL_newstate();
//打開庫
luaL_openlibs(L);
if (luaL_loadfile(L,fileName))
{
printf("error\n");
}
在C#中也類似調(diào)用
LuaSvr lua_svr = new LuaSvr ();
lua.init(null, () =>
{
lua.start("test");
});
LuaSvr將調(diào)用方式稍微做了封裝,原理類似。
- test two 修改text文字
C#寫法:
void Start () {
Text textComponent= GameObject.Find("Canvas/Text").GetComponent<Text>();
textComponent.text = "slua";
}
Lua寫法:
import "UnityEngine"
local textComponent= GameObject.Find("Canvas/Text"):GetComponent("Text")
textComponent.text="123"
原理淺析
- step one:跟蹤C(jī)#接口導(dǎo)出
//LuaCodeGen.cs
[MenuItem("SLua/Unity/Make UnityEngine")]
//導(dǎo)出需要的lua文件接口(UnityEngine下的)
static public void Generate()
{
if (IsCompiling) {
return;
}
//反射UnityEngine類
Assembly assembly = Assembly.Load("UnityEngine");
//獲取此程序集中定義的公共類型丰捷,這些公共類型在程序集外可見。
Type[] types = assembly.GetExportedTypes();
List<string> uselist;
List<string> noUseList;
CustomExport.OnGetNoUseList(out noUseList);
CustomExport.OnGetUseList(out uselist);
// Get use and nouse list from custom export.
object[] aCustomExport = new object[1];
InvokeEditorMethod<ICustomExportPost>("OnGetUseList", ref aCustomExport);
if (null != aCustomExport[0])
{
if (null != uselist)
{
uselist.AddRange((List<string>)aCustomExport[0]);
}
else
{
uselist = (List<string>)aCustomExport[0];
}
}
aCustomExport[0] = null;
InvokeEditorMethod<ICustomExportPost>("OnGetNoUseList", ref aCustomExport);
if (null != aCustomExport[0])
{
if ((null != noUseList))
{
noUseList.AddRange((List<string>)aCustomExport[0]);
}
else
{
noUseList = (List<string>)aCustomExport[0];
}
}
List<Type> exports = new List<Type>();
string path = GenPath + "Unity/";
foreach (Type t in types)
{
//Generate(t, path)遍歷導(dǎo)出每個(gè)類 后面跟蹤一下
if (filterType(t, noUseList, uselist) && Generate(t, path))
exports.Add(t);
}
//以上代碼導(dǎo)出所有系統(tǒng)類
//調(diào)用static void GenerateBind(List<Type> list, string name, int order,string path)跟蹤一下
GenerateBind(exports, "BindUnity", 0, path);
if(autoRefresh)
AssetDatabase.Refresh();
Debug.Log("Generate engine interface finished");
}
//跟蹤一
static bool Generate(Type t, string path)
{
return Generate(t, null, path);
}
static bool Generate(Type t, string ns, string path)
{
if (t.IsInterface)
return false;
CodeGenerator cg = new CodeGenerator();
cg.givenNamespace = ns;
cg.path = path;
return cg.Generate(t);
}
//最終調(diào)用
public bool Generate(Type t)
{
if (!Directory.Exists(path))
{
Directory.CreateDirectory(path);
}
if (!t.IsGenericTypeDefinition && (!IsObsolete(t) && t != typeof(YieldInstruction) && t != typeof(Coroutine))
|| (t.BaseType != null && t.BaseType == typeof(System.MulticastDelegate)))
{
if (t.IsEnum)
{
StreamWriter file = Begin(t);
WriteHead(t, file);
RegEnumFunction(t, file);
End(file);
}
else if (t.BaseType == typeof(System.MulticastDelegate))
{
if (t.ContainsGenericParameters)
return false;
string f = DelegateExportFilename(path, t);
StreamWriter file = new StreamWriter(f, false, Encoding.UTF8);
file.NewLine = NewLine;
WriteDelegate(t, file);//跟蹤一下
file.Close();
return false;
}
else
{
funcname.Clear();
propname.Clear();
directfunc.Clear();
StreamWriter file = Begin(t);
WriteHead(t, file);
WriteConstructor(t, file);
WriteFunction(t, file,false);
WriteFunction(t, file, true);
WriteField(t, file);
RegFunction(t, file);
End(file);
if (t.BaseType != null && t.BaseType.Name.Contains("UnityEvent`"))
{
string basename = "LuaUnityEvent_" + _Name(GenericName(t.BaseType)) + ".cs";
string f = path + basename;
string checkf = LuaCodeGen.GenPath + "Unity/" + basename;
if (!File.Exists(checkf)) // if had exported
{
file = new StreamWriter(f, false, Encoding.UTF8);
file.NewLine = NewLine;
WriteEvent(t, file);
file.Close();
}
}
}
return true;
}
return false;
}
//有點(diǎn)長寂汇,可以大概過一下
void WriteDelegate(Type t, StreamWriter file)
{
string temp = @"
using System;
using System.Collections.Generic;
using LuaInterface;
namespace SLua
{
public partial class LuaDelegation : LuaObject
{
static internal int checkDelegate(IntPtr l,int p,out $FN ua) {
int op = extractFunction(l,p);
if(LuaDLL.lua_isnil(l,p)) {
ua=null;
return op;
}
else if (LuaDLL.lua_isuserdata(l, p)==1)
{
ua = ($FN)checkObj(l, p);
return op;
}
LuaDelegate ld;
checkType(l, -1, out ld);
if(ld.d!=null)
{
ua = ($FN)ld.d;
return op;
}
LuaDLL.lua_pop(l,1);
l = LuaState.get(l).L;
ua = ($ARGS) =>
{
int error = pushTry(l);
";
temp = temp.Replace("$TN", t.Name);
temp = temp.Replace("$FN", SimpleType(t));
MethodInfo mi = t.GetMethod("Invoke");
List<int> outindex = new List<int>();
List<int> refindex = new List<int>();
temp = temp.Replace("$ARGS", ArgsList(mi, ref outindex, ref refindex));
Write(file, temp);
this.indent = 4;
for (int n = 0; n < mi.GetParameters().Length; n++)
{
if (!outindex.Contains(n))
Write(file, "pushValue(l,a{0});", n + 1);
}
Write(file, "ld.pcall({0}, error);", mi.GetParameters().Length - outindex.Count);
int offset = 0;
if (mi.ReturnType != typeof(void))
{
offset = 1;
WriteValueCheck(file, mi.ReturnType, offset, "ret", "error+");
}
foreach (int i in outindex)
{
string a = string.Format("a{0}", i + 1);
WriteCheckType(file, mi.GetParameters()[i].ParameterType, i + offset, a, "error+");
}
foreach (int i in refindex)
{
string a = string.Format("a{0}", i + 1);
WriteCheckType(file, mi.GetParameters()[i].ParameterType, i + offset, a, "error+");
}
Write(file, "LuaDLL.lua_settop(l, error-1);");
if (mi.ReturnType != typeof(void))
Write(file, "return ret;");
Write(file, "};");
Write(file, "ld.d=ua;");
Write(file, "return op;");
Write(file, "}");
Write(file, "}");
Write(file, "}");
}
最后生成了每一個(gè)類的導(dǎo)出文件
//跟蹤二
static void GenerateBind(List<Type> list, string name, int order,string path)
{
CodeGenerator cg = new CodeGenerator();
cg.path = path;
cg.GenerateBind(list, name, order);
}
// class CodeGenerator
public void GenerateBind(List<Type> list, string name, int order)
{
HashSet<Type> exported = new HashSet<Type>();
string f = System.IO.Path.Combine(path , name + ".cs");
StreamWriter file = new StreamWriter(f, false, Encoding.UTF8);
file.NewLine = NewLine;
Write(file, "using System;");
Write(file, "using System.Collections.Generic;");
Write(file, "namespace SLua {");
Write(file, "[LuaBinder({0})]", order);
Write(file, "public class {0} {{", name);
Write(file, "public static Action<IntPtr>[] GetBindList() {");
Write(file, "Action<IntPtr>[] list= {");
foreach (Type t in list)
{
WriteBindType(file, t, list, exported);
}
Write(file, "};");
Write(file, "return list;");
Write(file, "}");
Write(file, "}");
Write(file, "}");
file.Close();
}
上面步驟導(dǎo)出了BindUnity.cs 生成了GetBindList() 獲取所有系統(tǒng)類導(dǎo)出清單
using System;
using System.Collections.Generic;
namespace SLua {
[LuaBinder(0)]
public class BindUnity {
public static Action<IntPtr>[] GetBindList() {
Action<IntPtr>[] list= {
Lua_UnityEngine_AsyncOperation.reg,
Lua_UnityEngine_AssetBundleCreateRequest.reg,
Lua_UnityEngine_AssetBundleRequest.reg,
Lua_UnityEngine_Object.reg,
Lua_UnityEngine_AssetBundle.reg,
Lua_UnityEngine_AssetBundleManifest.reg,
Lua_UnityEngine_SendMessageOptions.reg,
Lua_UnityEngine_PrimitiveType.reg,
Lua_UnityEngine_Space.reg,
Lua_UnityEngine_RuntimePlatform.reg,
//省略
}
那么生成的List是怎么使用的呢病往?
private void doBind(object state)
{
IntPtr L = (IntPtr)state;
List<Action<IntPtr>> list = new List<Action<IntPtr>>();
//省略
var assemblyName = "Assembly-CSharp";
Assembly assembly = Assembly.Load(assemblyName);
list.AddRange(getBindList(assembly,"SLua.BindUnity"));
list.AddRange(getBindList(assembly,"SLua.BindUnityUI"));
list.AddRange(getBindList(assembly,"SLua.BindDll"));
list.AddRange(getBindList(assembly,"SLua.BindCustom"));
//...省略
當(dāng)LuaSvr將會(huì)把所有類加載到lua環(huán)境中,所以在lua代碼中import后可以調(diào)用該類骄瓣。
Slua導(dǎo)出類生成規(guī)則停巷。以O(shè)bject類為例:
static public void reg(IntPtr l) {
getTypeTable(l,"UnityEngine.Object");
//方法導(dǎo)出
addMember(l,GetInstanceID);
addMember(l,Destroy_s);
addMember(l,DestroyImmediate_s);
addMember(l,FindObjectsOfType_s);
addMember(l,DontDestroyOnLoad_s);
addMember(l,DestroyObject_s);
addMember(l,Instantiate_s);
addMember(l,FindObjectOfType_s);
addMember(l,op_Equality);
addMember(l,op_Inequality);
//變量導(dǎo)出(可參照api)
addMember(l,"name",get_name,set_name,true);
addMember(l,"hideFlags",get_hideFlags,set_hideFlags,true);
createTypeMetatable(l,constructor, typeof(UnityEngine.Object));
}
方法導(dǎo)出
[MonoPInvokeCallbackAttribute(typeof(LuaCSFunction))]
static public int GetInstanceID(IntPtr l) {
try {
UnityEngine.Object self=(UnityEngine.Object)checkSelf(l);
//調(diào)用C#中方法
var ret=self.GetInstanceID();
//返回值壓棧
pushValue(l,true);
pushValue(l,ret);
return 2;
}
catch(Exception e) {
return error(l,e);
}
}
變量導(dǎo)出
由于Lua調(diào)用C以方法的形式調(diào)用,所以稍微包裝了一下
[MonoPInvokeCallbackAttribute(typeof(LuaCSFunction))]
static public int get_name(IntPtr l) {
try {
UnityEngine.Object self=(UnityEngine.Object)checkSelf(l);
pushValue(l,true);
pushValue(l,self.name);
return 2;
}
catch(Exception e) {
return error(l,e);
}
}
[MonoPInvokeCallbackAttribute(typeof(LuaCSFunction))]
static public int set_name(IntPtr l) {
try {
UnityEngine.Object self=(UnityEngine.Object)checkSelf(l);
string v;
checkType(l,2,out v);
self.name=v;
pushValue(l,true);
return 1;
}
catch(Exception e) {
return error(l,e);
}
}
最后addMember方法
// LuaObject.cs
protected static void addMember(IntPtr l, LuaCSFunction func)
{
checkMethodValid(func);
pushValue(l, func);
string name = func.Method.Name;
if (name.EndsWith("_s"))
{
name = name.Substring(0, name.Length - 2);
LuaDLL.lua_setfield(l, -3, name);
}
else
LuaDLL.lua_setfield(l, -2, name);
}
protected static void addMember(IntPtr l, LuaCSFunction func, bool instance)
{
checkMethodValid(func);
pushValue(l, func);
string name = func.Method.Name;
LuaDLL.lua_setfield(l, instance ? -2 : -3, name);
}
protected static void addMember(IntPtr l, string name, LuaCSFunction get, LuaCSFunction set, bool instance)
{
checkMethodValid(get);
checkMethodValid(set);
int t = instance ? -2 : -3;
LuaDLL.lua_createtable(l, 2, 0);
if (get == null)
LuaDLL.lua_pushnil(l);
else
pushValue(l, get);
LuaDLL.lua_rawseti(l, -2, 1);
if (set == null)
LuaDLL.lua_pushnil(l);
else
pushValue(l, set);
LuaDLL.lua_rawseti(l, -2, 2);
LuaDLL.lua_setfield(l, t, name);
}
protected static void addMember(IntPtr l, int v, string name)
{
LuaDLL.lua_pushinteger(l, v);
LuaDLL.lua_setfield(l, -2, name);
}
所以在lua中調(diào)用C#方法形式為 :
local text_ui= GameObject.Find("Canvas/Text")--調(diào)用靜態(tài)方法
local textComponent= text_ui:GetComponent("Text")--調(diào)用成員方法
textComponent.text="123"--設(shè)置變量等價(jià)于textComponent["text"] = "123"
Slua的分析告一段落榕栏,是不是覺得用Lua控制組件比較麻煩畔勤,不能像C#腳本一樣直接可以把想要控制的組件掛到對(duì)應(yīng)的腳本下,可以思考一下怎么實(shí)現(xiàn)扒磁。參考鏈接