Lua基础
1、如果想获取某个lua文件的表。记得在该lua文件创建一个方法New,return 这个表。或者直接在lua文件末尾return
2、tolua在C#里经过Dofile的文件,只要全局变量,都能直接获取并进行使用。比如Dofile了Music.lua和Control.lua。Control.lua文件里能直接调用Music表里的函数
3、require也是加载lua文件的方法,但比dofile更好。
有两个优点:
(1) require会搜索目录加载文件
(2) require会判断是否文件已经加载避免重复加载同一文件。
4、静态方法、静态变量、成员变量、成员属性均使用 “.” 来访问,成员方法使用 “:” 来访问。
表用“:”来添加方法,会将自身“self”传入方法内,“.”则需要手动传入。
5、lua带参数方法,可以不传入参数执行,但默认参数为nil
6、setmetatable具有返回值,返回值为setmetatable(o,self)里的o
7、lua中的多线程
8、pairs 和 ipairs区别
(1)pairs可以遍历表中所有的key,并且除了迭代器本身以及遍历表本身还可以返回nil;
(2)ipairs则不能返回nil,只能返回数字0,如果遇到nil则退出。它只能遍历到表中出现的第一个不是整数的key
分为两种情况:
第一种是有整数1、3没有2那么,遍历完1退出遍历
第二种是遇见非整数key,则直接退出遍历
具体情况如下
local tabFiles = {
[1] = "?",
two = "2",
[3] = "test2",
[6] = "test3",
[4] = "test1"
}
for k, v in ipairs(tabFiles) do
print(k, v)
end--打印结果仅为“1 ?”
-----------------如果用pairs
for k, v in pairs(tabFiles) do
print(k, v)
end--打印结果为“1 ?|two 2|3 test2|6 test3|4 test1
8、深拷贝(DeepCopy),目前理解主要应用是针对表
(1)什么是拷贝?就是创建出一个和本体一样的副本,并且对副本的任何操作都不影响本体!
(2)浅拷贝,又叫Shallow Copy,只复制对象的基本类型,对象类型仍属于原来的引用。深拷贝,又叫Deep Copy,不仅复制对象的基本类型,同时也复制原对象中的对象,完全产生新对象。
(3)
赋值拷贝:nil、boolean、number、string、function经过以下拷贝,创建出跟本体一样的副本,但对副本的任意操作都不影响本体。
非赋值拷贝:然而在 lua 中并不是所有类型的变量都可以通过直接赋值来实现这样的拷贝——比如 table 、userdata、thread类型(其中table相当于把表索引给赋值过去了)就不行。lua深拷贝学习参考
q=true
w=q
w=false
print(q)--打印出来值为true
------------
q={i=1}
w=q
w["i"]=2
print(q["i"])--打印结果为2
(4)lua中Table它不能简单通过赋值进行拷贝,而是需要创建一个新的 table 并将原 table 的 key-value 递归拷贝,实现方法如下
function DeepCopy(object)
local searchTable={}
local function Copy(object)--假设这个object就是个表
if(type(object)~="table") then--如果不是一个表返回值
return object
--elseif searchTable[object] then--感觉可以不要这一段
--return searchTable
end
local newTable={}--创建一个新表
searchTable[object]=newTable--
for key,value in pairs(object) do--将object的元素copy进新表里
newTable[Copy(key)] = Copy(value)--递归调用,参数不是表直接返回值,是表则再次递归
end
return setmetatable(newTable,getmetatable(object))
end
return Copy(object)
end
lua的面向对象
1、实现类的实例化
Person={name="?"}
p1=Person--这样就实例化了一个Person
p1.name="p1";
p2=Psrson--但批量实例化Person。其实算浅拷贝,既变量索引都指向同一个
p2.name="p2"
print(p1.name..p2.name)--打印结果都是p2.正确实例化以及继承都需要用到元表,具体实现看以下实现代码
2、lua创建如何一个类,以及如何继承
表可以看错C#一个类,以下主要是实现如何继承
(1)官方做法,使用元表实现
其原理是:根据以下Object的new函数,当继承他的子类,成员调用不存在的函数时候都会去self查找,如果有同名方法,则覆盖。
Object = {class_id = 0}
function Object:new(o)
o = o or {}
setmetatable(o,self) -- 对象o调用不存在的成员时都会去self中查找,而这里的self指的就是Object
self.__index = self--
return o
end
function Object.Test(str)
print(str)
end
---以下我们创建对象来测试以下
local o1 = Object:new()
o1.class_id = 11;
local o2 = Object:new()
o2.class_id = 22;
print(o1.class_id..":"..o2.class_id)
print(o1.Test("3333"))
print(o2.Test("4444"))
--打印结果为11:22,3333,4444。可发现调用均为Object函数的成员变量和函数
-----------------------分割线----------------------------
DisplayObject = Object:new()
-- 现在为止,DisplayObject只是Object的一个实例,注意以下代码
function DisplayObject:new(width,height)
print(width..":"..height)
--o=Object:new();--还可以这么写调用父类初始化方法,暂时不知道有没有更好的办法
--return o;
end
DisplayObject:new(3,4)
--调用该方法得出结果为“3:4”,此时就没调用Object里的new方法,而是覆盖后的方法
(2)复制表的方式
首先创建一个克隆表(CloneTab)的方法
再创建基类,以及new方法
创建一个拷贝(Copy)方法
将父类方法都Copy进子类方法里
function cloneTab(tab)
local ins ={}
for key,var in pairs(tab) do
ins[key] = var ;
end
return ins;
end
-----------------
Object = {class_id=1}
function Object.new()
local o = cloneTab(Object);
return o;
end
local p=Object.new()
print(p.class_id)--打印出参数是1
------------------
function copy(dist,tab)
for key,var in pairs(tab) do
dist[key]=var
end
end
------------------
DisplayObject={value=3}
function DisplayObject.new()
local ss = Object.new()
copy(ss,DisplayObject)
return ss
end
local p1 = DisplayObject.new()
print(p1.value.."--"..p1.class_id)--打印出参数是3--1
(3)使用函数闭包的形式实现面向对象(感觉这是最不理想的方案…)
function People(name)
local self={}
local function init()
self.name=name
end
self.SayHi=function()
print("????"..self.name)
end
init()
return self
end
local p =People("HTY")
p:SayHi()
function Man(name)
local self = People(name);
self.SayHello=function()
print("hi"..self.name)
end
return self
end
local m=Man("List")
m.SayHi()
ToLua
案例学习
1、C#调用Lua方法
//(1)有回调方法
LuaState lua = new LuaState();
LuaFunction luaFunc;
int CallFunc()
{
luaFunc = lua.GetFunction("方法名称");
luaFunc.BeginPCall();
luaFunc.Push(123456);//有多个参数需要传递就再写Push
luaFunc.PCall();
int num = (int)luaFunc.CheckNumber();
luaFunc.EndPCall();
return num;
//luaFunc.Invoke();//这是luaFUnc自带回调方法,只有传参方法不一样
}
//(2)没回调方法
//基本跟上面一样,除了没回调方法,自带方法调用LuaFcuc.Call()即可
2、C#对lua变量访问(平时很少用)
LuaState lua = new LuaState();
void GetValue()
{
//使用LuaState访问器
lua["全局属性名"];
lua["表名.属性名"];
//通过LuaTable
LuaTable table = lua.GetTable("varTable");//获取表
table["map.name"] = "new"; //table 字符串只能是key
table["map.name"]);//获取表中标的属性
table.AddTable("newmap");//添加表
LuaTable table1 = (LuaTable)table["newmap"];
table1["name"] = "table1";
}
3、协程
//lua文件
function Delay()
local c = 1
local url = www("网络请求地址");//协程执行www
coroutine.www(url);
url : GetAudioClip();//获取声音片段,这个看unityApi
while true do
coroutine.wait(1) //延迟一秒执行
print("Count: "..c)
c = c + 1
end
end
function StartDelay()//
coDelay = coroutine.start(Delay)//开启协程
end
function StopDelay()
coroutine.stop(coDelay)//关闭协程
end
//C#文件
void MyFunc()
{
lua = new LuaState();
lua.Start();
LuaBinder.Bind(lua);
DelegateFactory.Init();
looper = gameObject.AddComponent<LuaLooper>();
looper.luaState = lua;
LuaFunction func = lua.GetFunction("StartDelay");
func.Call();//执行协程方法
}
4、lua线程,不是真的线程,只是开启了一个迭代器,等需要使用的时候再去研究
5、lua访问C#数组,且如何获取lua方法多个返回值
//lua脚本
function TestArray(array)
local len = array.Length
for i = 0, len - 1 do
print('Array: '..tostring(array[i]))
end
return 1, '123', true
end
//c#
void MyFunc()
{
int[] array = { 1, 2, 3, 4, 5 };
func = lua.GetFunction("TestArray");
func.BeginPCall();
func.Push(array);
func.PCall();
double arg1 = func.CheckNumber();
string arg2 = func.CheckString();
bool arg3 = func.CheckBoolean();
Debugger.Log("return is {0} {1} {2}", arg1, arg2, arg3);//获取lua方法3个返回值
func.EndPCall();
}
6、Tolua如何使用C#的字典
//lua代码
function TestDict(map)
//字典遍历
local iter = map:GetEnumerator()
while iter:MoveNext() do
local v = iter.Current.Value
print('id: '..v.id ..' name: '..v.name..' sex: '..v.sex)
end
//根据key获取Value
local flag, account = map:TryGetValue(1, nil)
if flag then
print('TryGetValue result ok: '..account.name)
end
//获取keys
local keys = map.Keys
iter = keys:GetEnumerator()
print('------------print dictionary keys---------------')
while iter:MoveNext() do
print(iter.Current)
end
print('----------------------over----------------------')
//获取Values
local values = map.Values
iter = values:GetEnumerator()
print('------------print dictionary values---------------')
while iter:MoveNext() do
print(iter.Current.name)
end
print('----------------------over----------------------')
//移除
print('kick '..map[2].name)
map:Remove(2)
iter = map:GetEnumerator()
while iter:MoveNext() do
local v = iter.Current.Value
print('id: '..v.id ..' name: '..v.name..' sex: '..v.sex)
end
end
7、toLua访问C#枚举
//lua文件,首先要将枚举注册才能使用
function ChangeLightType(light, type)
print('change light type to '..tostring(type))
light.type = type
end
//C#
void MyFunc()
{
GameObject go = GameObject.Find("/Light");
Light light = go.GetComponent<Light>();
LuaFunction func = state.GetFunction("ChangeLightType");
func.BeginPCall();
func.Push(light);
LightType type = (LightType)(count++ % 4);
func.Push(type);
func.PCall();
func.EndPCall();
func.Dispose();
{
8、委托和事件
(1)想要在toLua中使用委托,首先需要在CustomSettings中注册对应委托(后面版本的tolua不用手动注册了),然后在代码开始开启的时候激活委托
//lua代码
function SetClick1(listener)
if listener.onClick then//自定义的一个委托,如果委托不为空
listener.onClick:Destroy()//删除委托
end
listener.onClick = DoClick1//添加委托
end
function AddClick1(listener)
if listener.onClick then
listener.onClick = listener.onClick + DoClick1//添加委托
else
listener.onClick = DoClick1
end
end
function RemoveClick1(listener)
if listener.onClick then
listener.onClick = listener.onClick - DoClick1//移除委托
else
print('empty delegate')
end
end
function DoClick1(go)
print('click1 gameObject is '..go.name)
end
function AddEvent(listener)//添加事件
listener.onClickEvent = listener.onClickEvent + TestEvent
end
function RemoveEvent(listener)//删除事件
listener.onClickEvent = listener.onClickEvent - TestEvent
end
function TestEvent()
print('this is a event')
end
//C#代码
//正常注册在lua里的普通方法,不同的委托类型也需要单独注册(现在框架作者已经帮忙做了这个事了)
public class TestEvent : MonoBehaviour
{
public event Action<GameObject> onClickEvent = delegate { };
public Action<GameObject> onClick=delegate { print("初始空方法"); };
[NoToLuaAttribute]
public void OnClickEvent(GameObject go)
{
onClickEvent(go);
}
}
//C#调用文件,放一个举例就ok
void AddDelegate()
{
if(AddClick1==null)
AddClick1 = state.GetFunction("AddClick1");
CallLuaFunction(AddClick1);
}
void CallLuaFunction(LuaFunction func)
{
func.BeginPCall();
func.Push(listener);
func.PCall();
func.EndPCall();
}
9、在toLua中创建GameObject
//lua代码
//将C#注册进lua的方法重命名
local Color = UnityEngine.Color
local GameObject = UnityEngine.GameObject
local ParticleSystem = UnityEngine.ParticleSystem
//DoTween完成后的回调方法
function OnComplete()
print('OnComplete CallBack')
end
local go = GameObject('go')
go:AddComponent(typeof(ParticleSystem))//添加粒子组件
local node = go.transform
node.position = Vector3.one
--go.transform:DOPath({Vector3.zero, Vector3.one * 10}, 1, DG.Tweening.PathType.Linear, DG.Tweening.PathMode.Full3D, 10, nil)
--go.transform:DORotate(Vector3(0,0,360), 2, DG.Tweening.RotateMode.FastBeyond360):OnComplete(OnComplete)
GameObject.Destroy(go, 2)
go.name = '123'
//C#
void Update()
{
lua.CheckTop();//不用也能正常执行
lua.Collect();//没有这个代码就不会执行GameObject.Destroy(go, 2)
//或者有LuaLooper loop=gameObject.AddComponent<LuaLooper>().luaState = lua;也可以,这个不放循环里
}
10、toLua调用C#带有out/ref的方法如何取值
//lua脚本
local box = UnityEngine.BoxCollider
function TestPick(ray)
local num= 998//待传递ref值
local num1,num2=TestEvent.RefFunc(num);
print(num1..'----'..num2) //打印结果100,20
local _layer = 2 ^ LayerMask.NameToLayer('Default')
local time = os.clock()
local flag, hit = UnityEngine.Physics.Raycast(ray, nil, 5000, _layer)//第一个为方法返回值,第二个为out返回值
--local flag, hit = UnityEngine.Physics.Raycast(ray, RaycastHit.out, 5000, _layer)//这样也可以,但我没明白RayCastHit.out这参数是怎么编译的
if flag then
print('pick from lua, point: '..tostring(hit.point))
end
end
//C#
public class TestEvent
{
public static int RefFunc(ref int num)
{
num = 20;
return 100;
}
}
11、(15_ProtoBuffer暂时没看懂,也不知道有啥用)
12、toLua解析Json文件(感觉还不如C#里写工具类调用LitJson)
//lua文件
local json = require 'cjson'
function Test(str)
local data = json.decode(str)
print(data.glossary.title)
s = json.encode(data)
print(s)
end
13、toLua中调用C#String,Int32
string = System.String
int = System.Int32
function Test(num)
str1='男儿当自强';//lua的String,并不能调用C#String方法
int1= int.New();//创建一个int32值为0,放入参数还是0原因暂不知
int1=3;
print(int1)
print(string.IsNullOrEmpty(str1))//可以用C#的静态方法判断lua的String
local str = System.String.New('男儿当自强')//C#的String创建
--print(str:Remove(3))//成员方法调用
local index = str:IndexOfAny('儿自')
print('and index is: '..index)
local buffer = str:ToCharArray()
print('str type is: '..type(str)..' buffer[0] is ' .. buffer[0])
local luastr = tolua.tolstring(buffer)
print('lua string is: '..luastr..' type is: '..type(luastr))
luastr = tolua.tolstring(str)
print('lua string is: '..luastr)
end
14、toLua里List的使用(感觉跟在C#用着没什么区别…感谢toLua作者大大)
//需要导出委托类型如下:
//System.Predicate
//System.Action
//System.Comparison
#lua代码
function Exist2(v)
return v == 2
end
function IsEven(v)
return v % 2 == 0
end
function Test(list,list1)
list:Add(123);
print(list[0]);
list:AddRange(list1);
index = list:IndexOf(123);
local pos = list:BinartSearch(123);
print(list:Contains(123));
local f = list:Find(Exist2);
local fa = list:FindAll(IsEven);
list:Remove(123);
list:Insert(0,123);
list:RemoveAt(0);
list:Insert(0, 123);
--注意推导后的委托声明必须注册, 这里是System.Predicate<int>
local index = list:FindIndex(System.Predicate_int(Exist2))
list:Clear();
end
x.零散知识
(1)重要方法lua.AddSearchPath ,通过此方法添加lua文件的路径,只有添加了文件路径之后,在该路径上的lua文件才可以被读取
(2)使用完lua虚拟机之后记得要销毁,具体操作如下:先进行lua虚拟机栈的判空(不知道是不是指还有没运行完代码意思),具体对应的就是lua.CheckTop , 然后就是析构掉lua虚拟机,具体方法为lua.Dispose
(3)lua 中强制类型转换,暂时没测试能不能对注册的C#类有用
tolua.cast(object,”CCSprite”)