2.如何初始化一个lua脚本
通过 Cheat 函数来调用lua脚本
如图所示,执行地图中导入的 test.lua 脚本
已改为: execlua:test
-
3.如何写lua脚本
有2种办法:用其他工具写好了导入到地图中/在编辑器里写
这里只介绍第二种
如图所示,在脚本的任意位置写下这样的格式遍可以自动往地图中导入名为"test.lua"的文件,文件内容即为 [[]] 括起来的部分
应该是三种,1.27+的ydwe支持从本地硬盘上导入lua脚本文件。并且import本身就是一个lua函数,对lua熟悉的童靴可以编写自己的导入函数,比如导入一个文件夹内的所有lua脚本。
-
建议使用 <?import("文件名")[=====[脚本内容]=====]?> 的形式,等号数量前后一致就行,这样就可以和脚本中的 [[ ]] 进行区分以免冲突
4.建立第一个lua文件
在第一个文件中,我建议你只写以下代码
jass_ext.EnableConsole()
setmetatable(_G, {__index = getmetatable(jass).__index})
第一句的作用是打开一个cmd窗口,这样你的其他脚本有错误话会提示你
第二句代码如果不写的话你得用 jass.SetUnitState(jass.GetTriggerUnit(), jass.UNIT_STATE_LIFE, 0) 来写代码,写了以后"jass."就可以都省略掉了
_G是5.1的旧写法,有可能在未来lua版本中被移除,请使用_ENV代替
之后你可以在jass中用Cheat函数来运行其他lua脚本,你也可以在lua中通过
require "another.lua"
来运行其他lua脚本
注意这样运行的话每个脚本只能运行一次,如果你之前运行过"another.lua",那么之后的require "another.lua"就不会执行了
5.lua的基本介绍
lua是一个简单,高效,强大的脚本语言,其语法和jass有很多相似之处,因此有jass基础的话可以很快掌握lua
lua和jass最大的不同在于lua不需要写变量类型,例:
local a = 1
if a == 1 then
a = "等于1"
end
这个例子中a分别是数字(number)和字符串(string)
lua有以下几个类型
nil --空值,任何变量在复制前都是nil,类似于jass的null
boolean --布尔值,和jass一样包括true和false
number --数字,jass中的real和integer在lua中统一为number,因此在lua中 5/2 == 2.5
string --字符串,和jass中的string基本相同
function --函数,和jass不同的是lua中函数是一个变量,你随时可以把一个函数赋值给另一个变量,或传递给另一个函数做参数
userdata --自定义数据,你可以理解为jass中的handle,lua无法直接对其进行操作
table类型呢……thread应该不能用。:table漏了...thread会和GetTriggerUnit什么不兼容所以不介绍了,acb正在写新的协同线程
lua有以下保留字
and break do else elseif
end false for function if
in local nil not or
repeat return then true until
while
6.变量
lua对变量进行操作时不需要set 关键字
lua使用全局变量无须事先声明,任何时候
a = 10
都是有效的.在a被复制前,如果去获取a的类型将返回"nil"
局部变量的声明方式类似于jass:
local a = 10
记住,lua不需要写变量类型哦
补充一下
对于 if a then 这样的判定,当a为nil或false时不成立,其他包括0与""在内都是成立的
lua中字符串连接符是 .. 而不是 + ,原因很简单:
1 + 2 = 3
1 .. 2 = "12"
7.表达式
jass中的 != 在lua中写为 ~= ,其他加减乘除 括号 比较 and or 都一样
此外lua还自带了取余(%)和幂(^)
5%2 = 1
2^3 = 8
8.关于数字和字符串的一些特殊写法
lua和jass一样都支持 0x前缀的16进制数字,但是jass中 '' 表示的256进制数字符号在lua中要改为 || (注:这是acb加入的村规,lua并不自带256进制)
lua的字符串符号有3个,分别是 "" '' 和 [[ ]]
不过最后一个和YDWE有点冲突请不要使用(如果你是在外面写脚本然后导入地图可以无视)
9.数据结构
抛弃掉jass的哈希表吧
lua只有一种数据结构:table(表)
t = {} --将一张新建的表赋值给变量t
t[1] = 10
t[2] = 20 --当数组用
t.x = 50
t.y = 100 --当哈希表用
任何非nil的值都可以当做索引,例如
t["呜喵"] = "wumiao"
t[GetTriggerUnit()] = "厉害" --这里做索引的是一个单位,你可以抛弃GetHandleId了
你可以在新建的时候直接定义数组内容,例:
t = {
10, --10赋值给t[1]
x = 50, --50赋值给t.x
y = 100, --100赋值给t.y
20 --20赋值给t[2]
}
注意这样写的话,数据之间要加上一个逗号
table可以进行多层嵌套,例如
t = {}
t[1] = {}
t[1][2] = {}
t[1][2][3] = "呜喵"
10.函数
函数的内容比较多,我得分成好几段...
函数声明:
lua的函数声明有2种:
function func(a, b, c)
end
func = function(a, b, c)
end
这2种方法是等价的,都是声明了一个名为"func",参数为a b c 的函数
显然,参数不需要写类型,也不需要写返回类型.endfunction改为end即可
在lua中函数是一个变量,这在第二种声明方式中得以显现.你可以随时将他赋值给另一个值,例如
R2I = math.floor --后者是lua自带的取整函数
这样你以后写 R2I(11.2) 就完全等价于 math.floor(11.2)
既然是变量,那么他就可以局部化:
local function func(a, b, c)
end
local func = function(a, b, c)
end
这样外部就调用不到这个func了
函数定义还有一种形式 function point:get_x() return self.x end,这也是一个语法糖。和function point.get_x(self) return self.x end的写法是完全等价的
函数的调用
与jass不同,lua调用函数不需要写 call
以上面注册的函数为例,我只需要
func(1, 2, 3)
即可调用这个函数
lua调用函数没有顺序限制,只要func已经被赋值了函数就能调用,即使他赋值的函数是在之后定义的
lua也不限定你的参数,如果你写成
func(1, 2)
也依然可以正常调用func函数,只是该函数的参数c将为nil
lua也不限定你的返回值,你可以这样声明函数:
func = function(a)
if a == 1 then
return 1
elseif a == 2 then
return "呜喵"
elseif a == 3 then
return true
elseif a == 4 then
return {50, 100}
end
end
此时如果你
local r = func(0)
r的值就是nil
lua还支持多返回值:
GetXY = function(u)
return GetUnitX(u), GetUnitY(u)
end
local x, y, z = GetXY(u) → x = 0, y = 0, z = nil
函数的特殊调用
当函数只有一个字符串或表参数,且不是变量时,可以省掉括号
例如之前的例子:
require "another.lua"
其实是
require("another.lua")
的缩写
还有个很常用的写法是
func = function(data)
SetUnitState(data.unit, data.state, GetUnitState(data.unit, data.state) + data.plus)
end
func{
unit = GetTriggerUnit(),
state = UNIT_STATE_LIFE,
plus = 100
}
这个例子中,我将参数全部保存在一个表中,然后将整个表作为一个参数传递给函数.
闭包
当一个函数内部嵌套另一个函数定义时,内部的函数体可以访问外部的函数的局部变量,这种特征我们称作词法定界。虽然这看起来很清楚,事实并非如此,词法定界加上第一类函数在编程语言里是一个功能强大的概念,很少语言提供这种支持。
↑↑我反正是看着眼花,直接上演示:
很显然,这个函数的实现方式是开了个1秒的循环计时器
需要注意的是,我直接在本该传入code参数的位置写了个性的函数在上面(这个函数无需命名,称为匿名函数)
这个匿名函数可以正确调用到父函数"heal"的参数以及其声明的局部变量
嗯,这个就叫做闭包
看下面一个例子:
这里我将一个函数作为一个参数传递给了test,然后在1秒后运行了这个函数.思考一下,运行后,cmd窗口上将显示几呢?(print函数即为在cmd窗口上显示内容)
11.条件与循环
嗯..应该在函数之前讲的说...
条件语句与jass基本相同,只需要将endif改成end即可,可以看16楼函数返回值的例子
lua中有3种循环方式
1.for 循环
最常用的循环方式
for i = 1, 10, 2 do
print(i)
end
cmd窗口中将显示"1 3 5 7 9"
例子中的 1 10 2 分别代表 开始 结束 步长
这里的 i 是自动声明的局部变量
如果步长是1,那么可以省略不写,例如
for i = 1, 5 do
print(i)
end
显示"1 2 3 4 5"
要注意的是,你不能通过修改i的值来临时更改循环,你可以理解为
forLoop(1, 5,
function(i)
print(i)
end
)
2.while 循环
当条件成立时进行循环
local i = 1
while i <= 5 do
print(i)
i = i + 1
end
如果你要手动修改i来控制循环,可以使用while
3.repeat 循环
至少我没用过
local i = 1
repeat
print(i)
i = i + 1
until i >= 5
你可以使用break来提前退出一个循环,常见的写法有
local i = 1
while true do
print(i)
i = i + 1
if i >= 5 then
break
end
end
需要注意的是,break与return必须写在一段代码的最后面,身后紧靠着end,elseif之类
如果你必须要在代码中间使用,请写成 do break end(do return end)
12.对表的操作
通过前面的学习我们可以直到,与jass的"某个变量是数组"不同,lua中"某个变量的值是表".因此这个表可以重新赋值给别的变量或当做参数
lua自带了许多table相关的函数,你可以完全用table来取代group
例:
t = {10, 9, 8, 7, 6 , a = "呜喵", b = "喵呜", c = true}
这个表分为2个部分,数组与哈希表
我们可以通过 #t 来获取数组的长度(5)
for i = 1, #t do
print(i .. " = " .. t[i])
end
这是遍历数组的一个简单的方式
lua还提供了一种遍历数组的方式:
for i, v in ipairs(t) do
print(i .. " = " .. v)
end
这里的i 与 v都是局部变量
此外lua还提供一个遍历表中所有值的方式:
for i, v in pairs(t) do
print(i .. " = " .. v)
end
除了数组部分,可以将哈希表部分也都遍历出来,并将对应的索引放在i中,值放在v中
不过要注意的时,虽然这样遍历时依然会先按照顺序遍历出数组部分,但之后的哈希表部分的遍历顺序是随机的,与你储存时的顺序是没有关系的哦
lua还提供了2个常用的插入/拔出...好吧删除功能
table.insert(t, 3, 20)
将20插入到t[3]上,之前从[3]开始的数据都会被挤得往后挪一个位置
table.remove(t, 3)
将t[3]删除,同时之后的数据都会往前挪一个位置填补空缺
如果你不填位置,那么将默认在数组的尾端上操作:
table.insert(t, 20) == t[#t + 1] = 20
table.remove(t) == t[#t] = nil
13.面向对象
面向对象是区别于面向过程的编程方式
jass使用者应该对面向过程很清楚-----你平时写的就是面向过程
一个简单的例子:
我使用了一张表保存了一个单位,以及其一些数据,此外还在里面保存了一个函数,这个函数的作用就是删除这个单位
之后,我只需要使用 hero:remove 即可调用到这个函数,从而删除hero里记录的单位
注意这个函数的调用的方式,hero与remove之间有一个冒号,代表调用hero的成员函数.如果你不能理解,你只要知道
hero:remove() 完全等价于 hero.remove(hero)
我们还可以在表的外面添加一个成员函数
这2种声明方式是等价的,当然你也可以使用
function hero:resethpmp(hp, mp)
来添加函数,效果是一样的
相对的,调用方式则为
hero.resethpmp(hero, 0, 0)
或者
hero:resethpmp(0, 0)
面向对象的好处在于,你可以把一系列的数据当做一个整体来处理,当你调用其中的一个函数时,这个函数可以获得这个整体的信息
看不懂的话看这句
如果说RemoveUnit(hero.unit)是找一把武器来杀死这个单位,那么hero:remove()就是按下这个单位身上的自爆按钮
14.重载函数
重载函数这个名字很好听,但说白了就是给函数重新赋值罢了...
例子:
首先我将KillUnit保存在局部变量中,然后重新赋值了KillUnit:
如果单位不是"最萌小汐"才能执行杀死动作
由于lua的函数都是动态的,因此我们可以只在需要的时候进行重载.当你不再需要这个判断的时候,只需一句
KillUnit = oldKillUnit
即可取消重载
在lua中,不再使用的lua数据会被自动回收,这就包括的table
我们可以利用table来重载location相关的函数,例如:
这样你便可以放心大胆的使用location,完全无需担忧泄露问题
15.重载操作符
我们定义2个三维向量
a = {10, 20, 0}
b = {20, 30, 90}
我们希望通过相加的方式来获得一个新的三维向量,但直接写 a + b 是错误的,因为lua并没有定义过表的相加方式
不过我们可以定义
我们可以使用setmetatable这个函数来为一个表添加一个元表,当table执行一些操作时,lua会去元表中寻找这个操作的定义方式
这里的mt就是一个a和b的元表
我们现在需要定义的是加法操作,其名称为: __add
之后我们可以通过
c = a + b
来获取a和b相加后的三维向量c
具体的流程如下:
当lua执行a + b时,检查出a的类型的table,于是打开它的元表mt,寻找加法操作__add.在这里lua很顺利的找到了mt.__add,于是执行了mt.__add(a, b),并将返回值赋值给了c.如果元表不存在,或原表中没有__add函数,lua将抛出错误
__add 加法 +
__sub 减法 -
__mul 乘法 *
__div 除法 /
__unm 负值 -(符号)
__pow 幂 ^
__mod 取模 %
__concat 链接 ..
16.表的继承
除了操作符外,读取与新建表不存在的数据也可以进行重载,其关键字分别为__index与__newindex,同样也是通过元表(setmetatable)来实现的
例子:
这里我定义了一个单位,他的数据包括hp和mp,然后我定义了一个英雄,既然是英雄那么他就要有三维,但同时他也要有单位的数据
在这里,我通过元表mt将__index赋值为了unit,这样当你获取hero.hp时将会返回100.流程如下:
试图获取hero.hp,但是hero里没有hp的定义,打开元表mt,成功找到了__index,他的值是另一张表unit,于是便到unit中寻找hp,成功找到了100,将其返回
在这个例子中,我将__index赋值为了一张表.不过他也可以赋值为函数,进行逻辑操作
例子:
此时print(t[10]) 的结果是89
__newindex的例子:
如果你试图 unit.str = 15,那么就会赋值失败并显示错误
同样__newindex 也接受table做参数,例如前面的例子中我可以 hero.hp = 200,通过__newindex我可以实现让hero继承的unit来进行unit.hp = 200
lua引擎的使用教程
|
请发表评论