Lua初学者笔记2

1.lua编译环境配置
下载lua编译环境“SciTE”,直接安装,在SciTE中即可直接编译lua文件。
地址:https://code.google.com/archive/p/luaforwindows/downloads
或者使用lua studio。
2.lua基本语法
2.1全局变量和局部变量
Lua的全局变量不需要声明,给一个全局变量赋值的同时就创建了一个全局变量。没有加local声明的变量都是全局变量。

局部变量定义是需要加local关键字。

2.2 变量赋值
普通变量赋值与其他语言类似。但lua可以进行多变量同时赋值,多变量赋值的特点:
①先计算右边的值再赋值给左边
②当左右变量数不一致时,左边多,补nil,右边多,舍去。

2.3 逻辑运算符

3.lua的基本类型
Lua有8种基本类型,nil、boolean、number、string、userdata、function、thread、table。
3.1 nil
Lua中特殊的类型,它只有一个值:nil,一个全局变量没有被赋值以前默认值为nil,给全局变量赋值nil可以删除该变量。

3.2 boolean
两个取值false和true。
Ps:在判断条件中false和nil为假,其余所有值都为真。
3.3 number
Lua中不区分浮点数和整数、在保存时同一使用number。
3.4 string
①表示:可以使用“”(不能换行)或[[]](可以换行)。

②当字符串为纯数字时,可直接进行运算

③#可计算字符串的长度

④字符串连接使用..

3.5 table
由于lua中的table是一个“关联数组”,是lua中唯一的数据结构,可以代表队列、链表等各种数据结构。table在使用前必须创建。

由于lua库函数中与table相关的函数的数字索引下标为从1开始,所以建议table的数字下标也从1开始。
3.6 function
在lua中,函数被看作“第一类值”,与number、string同级,可以赋值给变量。

3.7 thread
线程拥有自己独立的栈、局部变量和指令指针。
Lua中特有的协同程序也是一种thread。
3.8 userdata
用户自定义类型,用于表示c/c++中的数据类型。
4.循环语句
4.1 while和repeat
使用和c++类似,不详细介绍。

4.2 数值型for循环

4.3 泛型for循环
泛型for循环通过一个迭代器函数遍历所有值,类似Java的foreach循环。
ipairs遍历从索引1开始的顺序的元素。
pairs遍历所有元素。

5.函数与闭包
5.1 函数定义

optional:可选参数,未设置时为全局函数,为local时为局部函数
name:函数名
a1…an:函数参数,可为可变参数

body:函数体,函数要做的事情
value: 可以返回一个或多个值,返回多值时,变量接受规则和多变量赋值相同。也可以返回函数,返回函数时可能构成闭包。
5.2 闭包
闭包是lua中一个重要的概念。
首先明白几个定义
①词法定界:当函数内部嵌套另一个函数定义时,内部的函数体可以访问外部的函数的局部变量,这种特征称为词法定结。
②upvalue:内部函数可访问的外部函数的局部变量称为upvalue。就像是只针对这个内部函数来说的静态变量。
简单的来说,一个函数和他的upvalue构成了一个闭包。
一个简单的例子:

当有多个内部函数时,它们共享upvalue:

嵌套的函数可以共享upvalue:

5.3 尾调用
如果函数f的在return处返回了一个函数g,f在返回g后不会做任何事情,那么这就是一个尾调用。Lua对于尾调用的函数f,会在调用g时将f的内容出栈,防止循环调用的爆栈。
6.迭代器
6.1 泛型for循环

泛型for的执行过程:
首先,初始化,计算in后面表达式的值,表达式应该返回范性for需要的三个值:迭代函数、状态常量、控制变量;与多值赋值一样,如果表达式返回的结果个数不足三个会自动用nil补足,多出部分会被忽略。
第二,将状态常量和控制变量作为参数调用迭代函数
第三,将迭代函数返回的值赋给变量列表。
第四,如果返回的第一个值为nil循环结束,否则执行循环体。
第五,回到第二步再次调用迭代函数
6.2 无状态的迭代器
无状态的迭代器是指不保留任何状态的迭代器,所以在循环中我们可以利用无状态迭代器避免创建闭包,传递table话费的额外时间。
这个无状态可有有些误导,这里指的是除了状态常量和控制变量,没有其他的变量需要传递,而不是一个变量都不需要传递。
每一次迭代,迭代函数只使用状态常量和控制变量两个参数,不使用额外参数,这种迭代器就是无状态的迭代器。
比如ipairs:

6.3多状态的迭代器
除了状态常量和控制变量外需要其他状态才能获取下一个值的迭代器是多状态迭代器,这时需要闭包或一个table来传递参数。
闭包:

这里除了需要传递三个变量collection、index、count,所以需要用到闭包。
table:

7.模块
模块类似于一个封装库,可以把一些公用代码放在一个文件里,以API接口的形式在其他地方调用,有利于代码的重用和降低代码的耦合度。
模块由变量、函数、已知元素组成的table构成。把需要使用的变量、函数放在一个table中然后返回这个table就可以了。
模块通过require函数在其他程序中调用。
定义一个模块b,在a中调用:

8.元表
Lua中的每个值都有一套预定义的操作,比如数字可以相加、字符串可以相减,但是两个table,则不能直接进行相加的操作。
lua的table没有这些预定义操作,需要通过添加元表来定义这些预操作。可以理解为元表就是table的+、-、*、/、=等操作的集合,元表中的元方法就分别对应这些操作。
通过把b设置为a的元表。
一个元表中包含的元方法:

元表中的元方法都需要自己定义函数,否则nil。
①__add方法

类似于加法的二元元方法的调用过程:
1.对于二元操作符,如果第一个操作数有元表,并且元表中有所需要的字段定义,比如我们这里的__add元方法定义,那么Lua就以这个字段为元方法,而与第二个值无关。
2.对于二元操作符,如果第一个操作数有元表,但是元表中没有所需要的字段定义,比如我们这里的__add元方法定义,那么Lua就去查找第二个操作数的元表。
3.如果两个操作数都没有元表,或者都没有对应的元方法定义,Lua就引发一个错误。
②__index方法

__indexf方法调用过程:
1.访问table的一个索引时,如果table有这个索引,直接返回对应值。
2.若table没有该索引,查看table是否有元表,有则尝试调用元表的__index,否则返回nil。
3.若__index方法存在,调用__index方法,返回__index方法的返回值,若__index为一个table,则查找__index的索引,若__index中有这个索引,返回对应元素,否则返回nil。
③__newindex方法

__newindex用于更新table中的数据,调用过程如下
1.Lua解释器先判断这个table是否有元表;
2.如果有了元表,就查找元表中是否有__newindex元方法;如果没有元表,就直接添加这个索引,然后对应的赋值;
3.如果有这个__newindex元方法,Lua解释器就执行它,而不是执行赋值;
4.如果这个__newindex对应的不是一个函数,而是一个table时,Lua解释器就在这个table中执行赋值,而不是对原来的table。
Ps:如果在__newindex中又对一个table中不存在的键赋值,会发生循环调用__newindex,导致爆栈。
④__metatable方法

设置了一个元表的__metatable后,那么对使用这个元表的table进行setmetatable时会报错,使用getmetatable后会返回__metatable的值。

环境
--lua将所有的全局变量保存在一个_G的Table就是“环境”
_G
setfenv()

a=5
function f1()
    a=10--定义一个f1环境中的变量a=10
    _G.print(a)--输出f1环境中的a ----> 10
    _G.print(_G.a)--输出全局环境中的a---> 5
end
setfenv(f1,{_G=_G})--设置f1的环境
f1()
--setfenv的第一个参数可以是当前幻术调用栈中的层数
a=5

function f1( ... )
    setfenv(1,{_G=_G})--设置当前函数的环境,也就是f1
    a=10--定义一个f1环境中的变量a=10
    _G.print(a)--输出f1环境中的a ---> 10
    _G.print(a)--输出全局环境中的a---> 5
end
f1()



--lua  的面向对象程序设计
Account={balance=0}--定义一个表(表示账户类)

  --第一种:使用 . 来定义
function Account.withDraw(v)--理解为定义一个扣款的方法
    Account.balance=Account.balance-v
end
Acount.withDraw(10)
print(Account.balance)

--第二种:使用 :来定义,治安方法定义中添加一个隐藏参数self
--在参数出入时多传入了一个调用这个函数的变量
function Account:withDraw(v)--理解为定义一个扣款的方法
    self.balance=self.balance-v
end
Account:withDraw(10)
print(Account.balance)


--第二种定义完全等价于
function Account.withDraw(self,v)
        self.balance=self.balance-v
end 
Account.withDraw(Account,10)
print(Account.balance)
--只是简便了一点,这种定义方式在面向对象中的十分常用。
--举个例子
function Account.withDraw( v )
    Account.balance=Account.balance-v
end
a=Account
Account=nil
a.withdraw(10)
--会报错因为Account=nil后,a.withdraw就不能用了。
--但是如果使用了:来定义和调用,用self代替Account就不会有问题。

--类在c++中是个模板,不是真实存在的对象。但在lua中类被当作模板使用,但是实际上是一个实例化的对象。
--类就是真实存在的表,透过原方法__index来实例化这个类。

--实现一个简单的类;
local Account = {balance=10}--定义一个类
function Account:withdraw(v)--定义一个类中的一个方法,实际上是定义一个函数保存子啊Account表中
    self.balance=self.balance-v
end

--创建类的函数,可以理解为构造函数
function Account:new (o)
    o=o or {}--创建出的类的实例,实际上还是用一个表来模拟的,o是用来模拟类的实例的一个表。设置元表后o可以获取Account中所有函数和变量。
    --下面两句相当于setmetatable(o,{__index=self})
    setmetatable(o,self)
    self.__index=self
    return o
end
--一个用Account实例化的类a
a=Account:new ()


--lua 中的继承,
--实例化一个列的过程其实也是一次继承。

--定义一个类和他的方法
local Account ={balance=10}
function Account:withDraw(v)
        self,balance-self.balance-v
end
--a是Account的一个实例,也可以说a继承Account
a={}
setmetotable (a,{__index=Account})
--重写Account的函数
function a:withDraw( v )
        self.balance=self.balance+v
end


--实例化一个a
b={}
setmetotable(b,{__index=a})

b:withDraw(10)
print(b.balance)--输出20
print(a.balance)--输出为10
print(Account.balance)--输出10


--使用new的版本;
--定义一个列和他的方法
local Account={balance=10}
function AccountwithDraw(v)
    self.balance=self.balance-v
end
--创建类的函数

function Account:new (o)
    o=o or {}
    setmetotable(o,self)
self.__index=self
return o
end 
--一个用Account实例化的类a,同时也是继承

a=Account:new ()
function a:withdraw( v )
    self.balance=self.balance+v
end

b=a:new ()
b:withDraw(5)
print(b.balance)
print(a.balance)
print(Account.balance)

--使用new的话,整个程序结构更加清晰。

[Unity]使用Slua框架开发创建Unity引擎中的对象

主要的内容包括
LuaSvr环境对象初始化LuaState状态机对象
Lua中的import关键字脚本中导入UnityEngine设置游戏对象组件属性
Lua中的require关键字导入自定义模块module

创建新的Unity工程并且导入SLua框架

AppDelegate.cs

  using UnityEngine;
  using System.Collections;
  using SLua;

  public class Main : MonoBehaviour
  {

      private LuaSvr lua_svr;
      // Use this for initialization
      void Start()
      {
          //创建一个已经注入UnityEngine的状态机对象..
          lua_svr = new LuaSvr();

          //通过Resources文件夹下的main.txt(lua)文件内的main函数启动程序
          //当然也可以通过修改LuaState.loaderDelegate来修改默认的路径
          lua_svr.start("main");
      }
  }

main.txt

  funcion main()
      print("Hello LuaSvr...")
  end

在main.txt文件内导入UnityEngine包并且创建游戏对象

  import "UnityEngine"

  function main()
      -- 创建Cube对象
      local cube=UnityEngine.GameObject.CreatePrimitive(UnityEngine.PrimitiveType.Cube)
  end
在main.txt文件内导入UnityEngine包并且创建空游戏对象

  import "UnityEngine"

  function main()
      -- 创建空物体对象
      local empty=UnityEngine.GameObject("HHEmptyObject")
  end

操作游戏对象上的组件

获取Transform组件

  import "UnityEngine"

  function main()
      -- 创建Cube对象
      local cube=UnityEngine.GameObject.CreatePrimitive(UnityEngine.PrimitiveType.Cube)

      -- 获取transform组件上的position属性
      local pos = cube.transform.position
      pos.x = 10

      -- 修改transform组件的position属性
      cube.transform.position = pos
  end

添加Rigidbody组件
导入import “UnityEngine”
添加刚体组件cube:AddComponent(Rigidbody)
获取指定类型的组件,例如BoxCollider
导入import "UnityEngine"
自定类型字符串获取local collider = cube:GetComponent("BoxCollider")
由于使用Lua编写Unity项目时,无法与C#相比的是对象函数或者对象属性的智能提示不够健全.
所以在此贴出Slua框架中注入LuaState状态机对象内的一些UnityEngine的函数和属性.

UnityEngine.GameObject对象在LuaState中注入的内容

static public void reg(IntPtr l) {
      getTypeTable(l,"UnityEngine.GameObject");--添加的命名空间
      addMember(l,GetComponent);
      addMember(l,GetComponentInChildren);
      addMember(l,GetComponentInParent);
      addMember(l,GetComponents);
      addMember(l,GetComponentsInChildren);
      addMember(l,GetComponentsInParent);
      addMember(l,SetActive);
      addMember(l,CompareTag);
      addMember(l,SendMessageUpwards);
      addMember(l,SendMessage);
      addMember(l,BroadcastMessage);
      addMember(l,AddComponent);
      addMember(l,CreatePrimitive_s);
      addMember(l,FindGameObjectWithTag_s);
      addMember(l,FindWithTag_s);
      addMember(l,FindGameObjectsWithTag_s);
      addMember(l,Find_s);
      addMember(l,"transform",get_transform,null,true);
      addMember(l,"layer",get_layer,set_layer,true);
      addMember(l,"activeSelf",get_activeSelf,null,true);
      addMember(l,"activeInHierarchy",get_activeInHierarchy,null,true);
      addMember(l,"isStatic",get_isStatic,set_isStatic,true);
      addMember(l,"tag",get_tag,set_tag,true);
      addMember(l,"gameObject",get_gameObject,null,true);
      createTypeMetatable(l,constructor, typeof(UnityEngine.GameObject),typeof(UnityEngine.Object));
  }
只有函数指针位置的部分,在Lua中定义成了Table变量内的函数,例如:`cube:AddCommponent`
 在函数指针名的末尾部分以`_s`结尾的,在Lua中定义成了元表变量内的函数,例如:`GameObject.CreatePrimitive`
 在添加成员时,包含了类似于`"transform"`字符串的,在Lua中定义成了Table变量内的键值对属性,例如:`cube.transform`

UnityEngine.Transform对象在LuaState中注入的内容

 static public void reg(IntPtr l) {
      getTypeTable(l,"UnityEngine.Transform");
      addMember(l,SetParent);
      addMember(l,Translate);
      addMember(l,Rotate);
      addMember(l,RotateAround);
      addMember(l,LookAt);
      addMember(l,TransformDirection);
      addMember(l,InverseTransformDirection);
      addMember(l,TransformVector);
      addMember(l,InverseTransformVector);
      addMember(l,TransformPoint);
      addMember(l,InverseTransformPoint);
      addMember(l,DetachChildren);
      addMember(l,SetAsFirstSibling);
      addMember(l,SetAsLastSibling);
      addMember(l,SetSiblingIndex);
      addMember(l,GetSiblingIndex);
      addMember(l,Find);
      addMember(l,IsChildOf);
      addMember(l,FindChild);
      addMember(l,GetChild);
      addMember(l,"position",get_position,set_position,true);
      addMember(l,"localPosition",get_localPosition,set_localPosition,true);
      addMember(l,"eulerAngles",get_eulerAngles,set_eulerAngles,true);
      addMember(l,"localEulerAngles",get_localEulerAngles,set_localEulerAngles,true);
      addMember(l,"right",get_right,set_right,true);
      addMember(l,"up",get_up,set_up,true);
      addMember(l,"forward",get_forward,set_forward,true);
      addMember(l,"rotation",get_rotation,set_rotation,true);
      addMember(l,"localRotation",get_localRotation,set_localRotation,true);
      addMember(l,"localScale",get_localScale,set_localScale,true);
      addMember(l,"parent",get_parent,set_parent,true);
      addMember(l,"worldToLocalMatrix",get_worldToLocalMatrix,null,true);
      addMember(l,"localToWorldMatrix",get_localToWorldMatrix,null,true);
      addMember(l,"root",get_root,null,true);
      addMember(l,"childCount",get_childCount,null,true);
      addMember(l,"lossyScale",get_lossyScale,null,true);
      addMember(l,"hasChanged",get_hasChanged,set_hasChanged,true);
      createTypeMetatable(l,null, typeof(UnityEngine.Transform),typeof(UnityEngine.Component));
  }

文/肖浩呗(简书作者)
原文链接:http://www.jianshu.com/p/fadb5dd59352
著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值