函数(二)

莫小仙...大约 9 分钟Lua脚本

函数(二)

通过上一节的讲解,可能小伙伴们对函数有了一定的认识,但可能还是不太明白。这很正常,因为我主要讲的不是脚本的基础知识点,而是讲怎么把想要的效果写出来。我觉得张雪峰老师关于应试技巧的有句话说得很好,就是在一道题我不会做的情况下把题做正确。我这里想讲的就是在只知道脚本大概的意思的情况下,把想要的脚本写出来。

不过有些小伙伴可能有强迫症,就是一定要弄清楚为什么要这么做之后才能继续看下去,不弄明白就看不下去。这里我放上函数的知识点链接,觉得有必要的可以去简单学习一下:

菜鸟教程-函数篇open in new window

下面继续讲函数的相关内容。

参数

参数,可以简单理解为需要的条件。参数也是变量。一个函数,可以有任意多个参数。多个参数以“,”隔开。举几个例子:

function foo1 () end
function foo2 (a)
  print(a)
end
function foo3 (a, b, c)
  print(a + b, c)
end

第1行定义了一个没有参数的函数,该函数函数体为空的,没有做任何事情。

第2~4行定义了一个只有一个参数的函数,该函数打印了参数。

第5~7行定义了一个有三个参数的函数,该函数打印了前两个参数的总和以及第三个参数的值。

回顾一下上一节中用到的获取玩家的名字的API:

Player:getNickname(event.eventobjid)

其中event.eventobjid就是一个参数,代表玩家的id。

函数调用

函数名加上一对括号则完成了函数的调用,格式如下:

函数名(参数)

括号中可以传递参数值。如果函数没有对应参数,而传递了值,则该值不会被用到;如果函数定义了参数,但是却没有对应传值(调用函数时括号内提供的东西少于函数定义中参数的个数),那么在函数调用时,函数体内未传值的参数的值为nil(代表空)。以上面讲参数时定义的函数为例,进行函数的调用:

foo1(100)
foo2() -- 打印nil
foo3(1, 2) -- 打印3 nil

--表明这之后是注释(给读代码的人一段具有提示说明的文字),对代码没有任何影响。

第1行调用了函数foo1。参数值为100,但是函数体是空的,没有用到。

第2行进行了函数foo2的调用,但是没有传入参数,则调用时a的值为nil,打印为nil。

第3行调用了函数foo3,但是没有传递第3个参数的值,则打印出了3 nil。

函数返回值

返回值,可以简单理解为结果。一个函数,可以有返回值,也可以没有返回值(没有返回值时也会有一个默认的返回值nil),具体取决于函数的作用。函数调用后就会产生返回值。举两个简单的例子。

1.函数不需要结果,如打印一下参数值:

function p (a)
  print(a)
end

这里定义了一个打印函数。不需要返回值,直接调用:

p(12) -- 打印12

这样每次打印信息就不用多写4个字母了。

2.函数需要结果,如计算函数:

function plus2 (a, b)
  return a + b
end

这里定义了一个两个数相加的函数。其中的return表示返回结果。需要返回值,则调用如下:

local a = plus2(5, 3) -- a = 8

local表明变量a是一个局部变量,具体用法我在下一节会讲,这里暂时忽略。最后结果是a变量的值会变为8。

与其他大多数语言所不同的是,lua可以有多个返回值,每个返回值以“,”隔开:

function multiResult ()
  return 10, 20, 30
end

调用函数时,可以是:

local a, b, c = multiResult() -- a = 10, b = 20, c = 30

与参数类似,变量与返回值数量可以不一致:

local a, b = multiResult() -- a = 10, b = 20
local a, b, c, d = multiResult() -- a = 10, b = 20, c = 30, d = nil

回顾一下上一节中用到的获取玩家的名字的API:

local result, name = Player:getNickname(event.eventobjid)

这里Player:getNickname就是有两个返回值,一个是函数调用结果result(是否成功),另一个则是玩家的名字name

函数类比

函数讲了这么多,可能有的小伙伴还是不太理解。这里我再来打个比方。

比如我现在要做一道青椒肉丝,有很多个步骤。我怕每次根据记忆来做,年纪大了后记差了,就写了一个青椒肉丝做法(定义函数)。这个做法中,需要用到青椒(参数1)、里脊肉(参数2)、植物油(参数3)以及其他的一些配料等等。最后可以做出青椒肉丝(返回值)。

写成伪码,如下:

1.记录做法。(定义参数)

function 青椒肉丝做法 (辣椒,,, 淀粉, ...)
  辣椒洗净切丝
  肉洗净切丝
  腌制肉
  ...
  return 青椒肉丝
end

2.按照做法炒菜。(调用参数)

可能我没有青椒,就用了红椒;喜欢吃牛肉,就用来牛肉;觉得橄榄油更健康,就放了橄榄油:

青椒肉丝做法(红椒, 牛肉, 橄榄油)

3.盛菜。(使用函数返回值)

菜做好了我们要吃这个菜,需要把它保存起来:

盘子 = 青椒肉丝做法(红椒, 牛肉, 橄榄油)

与之前将大象装进冰箱的例子相比,不同的是:将大象装进冰箱可以不需要返回值。当然,如果逻辑严谨一点,装进冰箱应该返回成功或者失败,比如冰箱装满了。

简单来说,函数大概就这么用。

匿名函数:

先回顾一下,前面讲过了函数定义的基本结构:

function 函数名 (参数)
  函数体
end

而调用的时候就是:

函数名(参数)

但有些时候,一个函数可能只会用一次,就不再使用了。这时候如果也先定义再调用就会有点麻烦,而且也不直观。这时就可以使用匿名函数。

匿名函数,就是没有名字的函数。这里以“点石成金”的例子为例。还是先回顾一下“点石成金”的脚本:

local function Player_ClickBlock (event)
  if event.blockid == 104 then
    Block:placeBlock(410, event.x, event.y, event.z, 0)
  end
end
 
ScriptSupportEvent:registerEvent([=[Player.ClickBlock]=], Player_ClickBlock)

之前讲过了,这里第7行也是一个函数的调用。这里函数的第2个参数Player_ClickBlock就是一个函数类型。因为所有事件注册(第7行的意思就是注册一个玩家点击事件)所需要的函数参数只会用一次,所以我们可以将Player_ClickBlock改写为匿名函数,改写后如下:

ScriptSupportEvent:registerEvent([=[Player.ClickBlock]=], function (event)
  if event.blockid == 104 then
    Block:placeBlock(410, event.x, event.y, event.z, 0)
  end
end)

对比函数定义的基本结构,匿名函数只是去掉了名字,其他地方不变。与之前函数的定义相比较,使用匿名函数后,代码更紧凑,读起代码来也就更容易。

至于何时使用匿名函数,主要就看函数是不是只有一个地方会用到。那如果函数在多个地方被用到时,能不能使用匿名函数呢?当然可以,但那不是麻烦嘛。那样需要复制粘贴多次,而且函数修改起来也容易出错(可能有地方忘记修改了)。如果不理解,那没关系,有空多写写就明白了。

好了,函数的知识就讲这些。其他的如可变参数,可以去上面提到的链接中学习。下面来实操一下。

现在我们来做这样一个效果:如果有玩家哭泣,那么天就会下雨。

简单分析一下,我们需要以下API:

玩家动作表情改变事件 玩家哭泣动作的ID 改变天气为雨天的规则 这里顺带提一下,要分析出需要哪些API,是建立在你对游戏提供的API足够了解的情况下。如果你都没看过API,不知道有哪些API,当然也就不可能指定需要哪些API。换言之,对没有看过API的小伙伴来说,是不可能分析得出来的。所以,写脚本之前,需要先去看看API文档,了解一下能实现什么。

下面开始查API。

在API文档中,点击左侧事件系统,在右侧找到玩家事件:

d7018d0bb33f4a0fa5633940ca48fafd.png

点击跳转后,找到对应事件:

9f27b442632b480a99159b12dc060887.png

照着例子,我们可以把事件的结构搭建出来:

local function Player_PlayAction (event)
  
end
 
ScriptSupportEvent:registerEvent([=[Player.PlayAction]=], Player_PlayAction)

这次稍微调整下,我们改为匿名函数的方式:

ScriptSupportEvent:registerEvent([=[Player.PlayAction]=], function (event)
  
end)

接下来我们需要查询一下哭泣动作的ID。

左侧点击查询:

6bcd9d61af334f5eba5884f37451a3e3.png

右侧点击动作:

58f912c60fa74d2982eab3caea61ee8c.png

找到哭泣:

a2280e6dbce64dfe96e92063687c1e65.png

下面便可以加上条件了:

ScriptSupportEvent:registerEvent([=[Player.PlayAction]=], function (event)
  if event.act == 3 then
    
  end
end)

条件结构就是if … then … end,以后会具体讲解,现在想了解的可以查看地址:

https://www.runoob.com/lua/lua-decision-making.html

在前面找事件API时,我们发现事件里提供了两个参数:eventobjid、act。很显然第一个是玩家id,第二个是动作id。当然,如果小伙伴觉得不显然的话,可以都去试一下。

接下来就是下雨的效果了。

点击左侧游戏规则列表:

2d80d0b5db4746da8ab76de155215975.png

在右侧找到天气:

0adf3a7b71bf48e08e45501742c67649.png

至于怎么写,参考文档中上面的例子:

5e6ff427628e4b018459460f5e096495.png

大胆假设后,写成如下所示:

	ScriptSupportEvent:registerEvent([=[Player.PlayAction]=], function (event)
  if event.act == 3 then
    GameRule.Weather = 1
  end
end)

如此,便完成了。

不过,当我去游戏里试验的时候,发现没有效果。然后根据我的一番测试,发现天气规则是:0晴雨交错;1晴天;2雨天;3雷暴。但是,目前通过脚本只能改为0、1、2,修改为3无效。

于是,第3行代码应该修改为:

GameRule.Weather = 2

最后,如果学有余力的小伙伴,可以把玩家高兴后,天气转为晴天的效果加上。

评论
  • 按正序
  • 按倒序
  • 按热度
Powered by Waline v2.14.7