ToLua framework

ToLua is a framework for providing C# language bindings for Lua in Unity. Through ToLua, you can easily expose C# code to Lua scripts, and call C# classes, methods and properties in Lua scripts.

update process

Principle: Use AssetBundle to update resources, and because lua is compiled at runtime, lua files can also be regarded as a resource file (same as fbx, image, etc.) and can be entered into ab package

process:

Download the files.txt manifest file from the server when the game is running, and compare it with the local files.txt manifest file. If the md5 value in the newly downloaded files is different from the md5 value in the local files, or there is no corresponding file in the local list, then download the AB package from the server, and then decompress it for initialization

LuaState

The principle that C# can call lua is to create a lua virtual machine. LuaState encapsulates various stack operations on lua_State pointer, the main data structure of lua.

to reflect

In the old version, lua calls C# functions based on reflection, but at this stage it is based on de-reflection, which means: export all public member variables and member functions of C# class to a corresponding Wrap class. And these member functions are mapped to the lua virtual machine through special tags. When calling the corresponding function in lua, directly call the mapped c# wrap function, and then call the actual c# class to complete the calling process

Lua virtual stack

insert image description here

C# will tell the virtual machine the parameters passed in and the parameters that need to be returned, and then the virtual machine starts to access the stack, fetches the corresponding function from the stack and sends it to the Lua compiler. Then the lua program calls the Lua file global table (Global table) to find the corresponding function, and then pushes the return parameters into the stack, and C# reads the data from the stack.

  1. You can go up from the bottom of the stack in the order of 1 N, or you can go down from the top of the stack from -1 -N
  2. Any data, functions, tables, etc. can be stored in the stack, and the empty position is represented by nil

Preparation

The related classes and methods using Tolua need to call the namespace LuaInterface, and LuaState.Start needs to be called after the tolua code is loaded into memory. If you use assetbunblde to load lua files, the assetbundle must be loaded before calling Start().

To call a lua script, a lua virtual machine must be created first. Generally, for the client, it is recommended to create only one LuaState object. If you want to use multi-State, you need to set the global macro MULTI_STATE in Unity. The creation steps are:

LuaState lua = new LuaState();

load script

The important method lua.AddSearchPath, through this method to add the path of the lua file, only after adding the file path, the lua file on the path can be read. In this way, the DoFile and Require functions can only use the file name without writing the full path.

The easiest way to run a lua script in C# is lua.DoString, which is declared as follows:

public object[] DoString(string chunk, string chunkName = "LuaState.DoString")

Note that dofile needs an extension and can be executed repeatedly, and the latter variables will overwrite the variables loaded by the previous DoFile. Once loaded here, it will be executed sentence by sentence from the beginning.

LuaState.Require

public void Require(string filename)

Because Require reads a file to check whether the file has been loaded, if it has been loaded, it will directly return an index, otherwise it will load and return an index

  • Remember to destroy the lua virtual machine after using it. The specific operation is as follows:

    • First check the emptyness of the lua virtual machine, the specific method is lua.CheckTop
    • Destruct the lua virtual machine, the specific method is: lua.Dispose

C# calls lua function

call lua method

We can regard the function in lua as an object. After the virtual machine is initialized, load the corresponding lua file, and then need to create an object of type LuaFunction to represent the lua function. You can get the corresponding function object by calling lua.GetFunction("方法名");:

After getting it, you need to call it in C#. There are two main ways:

  1. To directly call the func.Call method of an object of type LuaFunction, the complete declaration is:
public object[] Call(params object[] args)

This calling method is relatively simple, but there is a disadvantage that the memory of the Lua object cannot be released automatically, so after using the Lua function object, we need to manually call the func.Dispose(); method of the LuaFunction type object to release Get rid of garbage memory, otherwise it will cause a memory leak!

However, the selection method is entrusted, which needs to be done first.DelegateFactory.Init();

int num = luaFunc.Invoke<int,int>(arg);

or

Func<int, int> Func = luaFunc.ToDelegate<Func<int, int>>();
num = Func(123456);
  1. Like CallFunc() in the case
int CallFunc()
{        
    luaFunc.BeginPCall();                
    luaFunc.Push(123456);
    luaFunc.PCall();        
    int num = (int)luaFunc.CheckNumber();
    luaFunc.EndPCall();
    return num;                
}

First of all func.BeginPCall;, we must start by func.Push(参数)passing parameters to the method through many overloaded functions to solve the gc of parameter conversion, then we need to run through, then obtain the return value func.PCall();through the corresponding method, and finally end through the clear function call. stack changes. The whole process is cumbersome and difficult to encapsulate, but the advantage is that there will be no garbage memory, so there is no need to manually release GC.func.Checkxxx()func.EndPCall();

Get variables in lua

Create global variables

After the lua virtual machine is created and initialized (calling the Start method), you can directly declare a global variable of the lua virtual machine:

LuaState lua = new LuaState();
lua.Start();
lua["Objs2Spawn"] = 5;

The format of obtaining global variables is the same, which is the same as lua, if there is no one, create it or read it. It is worth noting that the obtained function needs to be cast to LuaFunction:LuaFunction func = lua["TestFunc"] as LuaFunction;

Get and create lua table

Get the table in lua by calling the method lua.GetTable of the virtual machine, use the LuaTable type to store the table in lua, and create a table in lua by calling the member method table.AddTable of Luatable, except access through the GetTable method of the virtual machine In addition, the similar method of directly passing the LuaTable type variable according to the dictionary can also call the table

LuaTable table1 = lua.GetTable("varTable");
Debug.Log("Read VarTable from lua name:{0}", table1[map.name]);//会将map.name作为整体识别为键,值为nil,不会有任何输出
table1["map.name"] = "new";// table字符串只能为key,现在为“new”
LuaTable table2 = (LuaTable)table["newmap"];

For the lua metatable, you can also use LuaTable's GetMetaTable() method to get it

There is one thing worth noting about the acquisition of variables:

As mentioned in the comments, the string in [] will be treated as a whole. In lua syntax, if there is a table A, then Ax represents A["x"], which represents the table indexed by the string "x", and a[x] represents the value index corresponding to the variable x surface

a = {}
x = "y"
a[x] = 10
a.x -->nil 字段“x”的值,未定义
a.y -->10, 字段“y”的值

So if you want C# to get it correctly, you need to look like this:

LuaTable table = lua.GetTable("varTable");
LuaTable map = (LuaTable)table["map"];
Debugger.Log("Read varTable from lua,  name: {0}", map["name"]);

For variables of LuaTable type, the memory needs to be released manually after use, otherwise memory leaks will occur because the memory is not automatically released. The specific method is to call the method table.Dispose() of the LuaTable object;

GameObject

private string script =
    @"                                    
        local GameObject = UnityEngine.GameObject          
        local ParticleSystem = UnityEngine.ParticleSystem            
        local go = GameObject('go')
        go:AddComponent(typeof(ParticleSystem))
        local node = go.transform
        node.position = Vector3.one      
        print('gameObject is: '..tostring(go))    
        GameObject.Destroy(go, 2)                        
    ";
    
LuaState lua = null;

void Start()
{        
    lua = new LuaState();
    lua.LogGC = true;
    lua.Start();
    LuaBinder.Bind(lua);
    lua.DoString(script);            
}

void Update(){
    lua.CheckTop();
    lua.Collect();
}

LuaBinder.Bind(); ——This method needs to pass in a LuaStatetype parameter. This method will bind the classes, methods, properties, etc. defined in the C# code to the Lua module. In this way, the Lua script can call the functions and properties defined in the C# code, and the C# code can also call the functions and properties in Lua through the Lua script.

lua.Collect();——Garbage collection, for LuaFunction, LuaTable that is automatically gc, and LuaFunction that is subtracted by delegation, Object that is delayed in deletion, etc. Recycling that requires delayed processing, etc., is automatically executed here

Lua calls C# function

As in the case,

local ParticleSystem = UnityEngine.ParticleSystem
local GameObject = UnityEngine.GameObjectlocal 
go = GameObject('go')
go:AddComponent(typeof(ParticleSystem))
local node = go.transform
node.position = Vector3.one                  
print('gameObject is: '..tostring(go))

For some methods under UnityEngine, lua can directly reference them.

custom script

It is troublesome for the script implemented by yourself, first you need to call LuaBinder.Bind(LuaState lua)

Secondly, you need to write the custom class into the CustomSetting.cs file. You add your custom class to the end of the customTypeList array. If it is a static class, you need to add it to staticClassTypes. Then click Clear wrap files and Gen All in the Lua menu

custom delegate

As long as the delegate field needs to be used, DelegateFactory.Init() must be called once. If there is a custom delegate, it must be added to the customDelegateList in CustomSetting.cs

How to dynamically add listener events to buttons? First, we can define a static class of LuaHelper, implement a static function of AddButtonClick in it, and add an event code example to the button in NGUI: EventDelegate.Add(UIButton.onclick, Call call)

public static void AddButtonClick(GameObject g,LuaFunction callback)
{
    EventDelegate.Add(g.GetComponent<UIButton>().onClick, callback.Call);
}

Just find the corresponding game object in the lua code

local helper = LuaHelper
local btn = UnityEngine.GameObject.Find("Button")

local function Onclick()
    print("OnClick")
end
helper.AddButtonClick(btn,Onclick)

The specification for Lua to call C# is not to initialize in the Start periodic function, but to wrap the initialization-related functions, and get the function in Lua to call

Get C# array elements

There are several ways for lua to access C# arrays,

  • Access by subscript - this is consistent with C#, and you can also get the size of the array through array.Length
  • Access through iterator——array:GetEnumerator() gets the iterator of the array, gets the element value pointed to by the current iterator through iter.Current, and moves the position pointed to by the iterator to the next one through iter:MoveNext()
  • Call array:ToTable() to convert the array object into the form of the corresponding Table table in lua for access. Note that the lua array subscript starts from 0

In C#, the return value of the lua function can be obtained through func.Checkxxx()

for example

double arg1 = func.CheckNumber();//获得func函数double类型的返回值
string arg2 = func.CheckString();//获得func函数string类型的返回值
// 调用通用函数时需要转换类型,避免拆成多个参数
object[] objs = func.Invoke((object)array);
if (objs != null)
{
    Debugger.Log("return is {0} {1} {2}", objs[0], objs[1], objs[2]);
}

ToLua framework UML

insert image description here

Guess you like

Origin blog.csdn.net/jkkk_/article/details/130443941