本帖最后由 yangqs 于 2018-8-14 09:34 编辑
0.概述 LUA系统是一套由事件触发到函数执行的代码体系,是在常规的Behavior、OCL路线之外,实现神秘游戏效果的利器,这篇文章将由浅入深地向大家介绍LUA系统的关键信息,帮助各位理解LUA系统的结构和用法。阅读本文需要用到SDK中LUA文件夹中的文件(主要是ScriptEvents.xml和Scripts.lua) 1.事件列表(EventList) 所谓的LUA系统到底说了一个什么事呢?其实用一句通俗的话概括就是“当_____的时候,就做_____的事情”,比如“当肚子饿的时候,就做吃饭的事情”,“当没弹药的时候,就做发动技能的事情”,“当重伤的时候,就做播放声效的事情”之类的。这个句子的条件状语,“当_____的时候”,在ScriptEvents.xml里的名字叫事件(Event),而这个句子后面的动词,“就做_____的事情”就是Scripts.lua里的函数(Function)。当然,SDK的源码本身也自带很多“当_____的时候,就做_____的事情”这样的句子,比如铁锤坦克有“当造好的时候,就做隐藏副炮模型的事情”这样的描述,都写在ScriptEvents.xml的事件列表(EventList)里。 这是我在测试LUA功能的时候写的一个EventList,它其实就是记录了这样的一句话:“当ReallyDamaged的时候,就做OnJapanAntiVehicleVehicleTech1ReallyDamaged的事情”。 <EventList Name="JapanAntiVehicleVehicleTech1Functions" Inherit="BaseScriptFunctions"> <EventHandler EventName="ReallyDamaged ScriptFunctionName="OnJapanAntiVehicleVehicleTech1ReallyDamaged"DebugSingleStep="false"/>
</EventList>
2.LUA函数(LUAFunction) “当Event的时候,就做Function的事情”,那到底做了什么事情呢?这时候我们就需要看看这个函数(Function)里到底有些什么东西了。用记事本或者UE啥的打开Scripts.lua,发现有一大堆function开头end结尾的东西,和我们通常接触的xml完全不同,这些就是LUA函数。到底做了什么事情呢?就是做了这些事情。这些“function”单词后面的一串就是这个函数的名字,我们在EventList里用到某个函数的时候,就要把函数的名字一字不漏地对应上。函数函数,谁是谁的函数?变量是什么?其实就是函数名字括弧里的东西,例如function kill(self) end,函数名字叫做kill,变量名字叫做self,这和我们中学学的函数是同一个道理,函数f(x),f就是函数名,x就是变量名。当然函数也可以有多个变量,它们之间会用“,”隔开。 我们费那么大力气去搞一个函数,当然想要它实现相应的功能,在这里,我们通常会调用系统自带的一套函数来实现想要的功能。例如,我想要做“海啸开纳米装甲”这个事情,就可以利用系统自带的ObjectDoSpecialPower函数来实现。我就写了这样的一个函数: function OnJapanAntiVehicleVehicleTech1ReallyDamaged(self) ObjectDoSpecialPower(self, "SpecialPower_ToggleEnergizedArmor" ) End 怎么理解这个东西呢?我们知道,函数是一种映射关系,在映射的彼岸,我们需要“海啸开纳米装甲”,那么在映射的此岸,我们就要给函数的变量赋予合适的值。这个函数ObjectDoSpecialPower,它只是描述一种映射,通俗地讲就是一种变化规则,是规则,而并不记录具体值,我们需要给它具体值才能让它动起来。ObjectDoSpecialPower函数有两个变量,第二个变量好说,用什么技能嘛,就是把技能名字写上去就行,不过注意要写在引号里,因为我们是直接写一个值,如果不写引号这就是一个变量名了。而第一变量表示谁用这个技能,这还用问?当然是海啸自己啦。但注意,我们不能直接写“海啸”或者“JapanAntiVehicleVehicleTech1”我们需要写的是【这个海啸】,这个海啸是哪个海啸?这在地图编辑器里很好说,“就是那个海啸!“我们可以这样说,然后在地图上指明一个海啸写进去;然而现在并没有一个具体的海啸,我们做mod的时候需要指涉一个抽象的海啸,怎么办?幸好,在主函数里,我们有一个self变量,这里面恰好就存储着【这个海啸】的信息,我们只需要把它用到ObjectDoSpecialPower函数里就可以了,于是,我们在第一个变量的位置写上self,和主函数的变量self一字不差,这就表示它们是同一个变量,从而给ObjectDoSpecialPower函数说明了抽象的【这个海啸】,到时函数运作起来的时候,该是哪个海啸开技能,哪个海啸就会被赋值到self变量中,进而成为ObjectDoSpecialPower函数的变量,正确地使用技能。需要注意的是,变量的名字和函数的名字一样是可以随意更改的,比如我如果讨厌self这个变量名,我写成 functionOnJapanAntiVehicleVehicleTech1ReallyDamaged(haixiao) ObjectDoSpecialPower(haixiao, "SpecialPower_ToggleEnergizedArmor" ) End 效果是一样的。
3.事件(Event) 我们知道LUA系统是“当Event的时候,就做Function的事情”。做什么事情大概已经知道了,那“当Event的时候做”,到底是什么时候呢?这就要看ScriptEvents.xml中的三类事件(Event)了,分别是: 内部事件(InternalEvent),游戏系统自带的事件,简单好用,变量丰富 可编辑事件(ScriptedEvent),用户自定义的事件,要多少有多少,用的时候注册Name(类似AssetID,在这里叫Name)。 状态事件(ModelConditionEvent/ObjectStatusEvent) ,用户自定义的事件,用的时候也要注册Name,它们会根据ModelCondition或ObjectStatus的变化判断是否触发。 怎么理解事件(Event)的触发呢?用一句话来说就是“一旦_____,就有______事件”,第一个空是事件的触发条件,第二个空是事件名(Name)。例如:一旦被打了,这个单位马上就有OnDamaged事件;一旦打光了弹夹,立刻有OnClipEmpty事件。不过要注意的是,事件描述的是行为,不是状态,是在说“弹夹打空了”而非“弹夹是空的”。一般的内部事件(InternalEvent)所描述的东西都可以从事件的名字里猜出来,比如OnDamaged、OnDestroyed、OnPowerOutage这些都很好理解。 而状态事件也好理解,比如 <ModelConditionEvent Name="ReallyDamaged"> <Conditions>+REALLYDAMAGED</Conditions> </ModelConditionEvent> 就是一个典型的状态事件,它表示一旦这个单位获得了REALLYDAMAGED的状态时,就视为有了ReallyDamaged这个事件。<Conditions>里阔着的东西就是事件的触发条件。 而可编辑事件(ScriptedEvent)则并没有什么自动的触发机制,它需要其他机制来赋予,比如我可以设置一个“午时已到”的事件,那究竟什么时候是午时呢?我还要用别的机制来说明什么时候是午时。 所以说“当Event的时候,就做Function的事情”到底什么时候做呢?就是在发生这个Event的时候。那这个Event怎么发生呢?就看这个事件的发生条件了,或者你用别的机制来强制让某个名字的事件发生,这是可以的。
4.事件(Event)附带的信息 上文(2.)提到,ObjectDoSpecialPower中第一个变量【这个海啸】的信息来自于主函数OnJapanAntiVehicleVehicleTech1ReallyDamaged(self)的self变量,但实际上主函数并没有给这个self变量赋值,那主函数self变量的【这个海啸】的信息又来自于哪里呢?实际上就来自于事件ReallyDamaged。 我们知道,事件的作用是充当启动函数的条件,“当Event的时候,就做Function的事情”,像一个开关一样,但其实函数的变量的初始值也是由事件来的,你不但按了这个开关,按的力、按的时间、按的深度都有意义。每个事件附带的信息是不一样的,这可以在官方的注释里看出来,例如 <InternalEventName="OnDamaged" /> <!-- OnDamaged(self, other) other isattacking object, may be nil --> 这就说明OnDamaged这个事件附带了两个信息:被攻击的单位自己,攻击自己的单位。比如一个武士被PK攻击了,那么【这个武士】和【这个PK】的信息都会被记录下来,可以传递到函数中。内部事件和可编辑事件携带的信息往往比较多,而状态事件则只附带了一个信息:【改变状态的这个单位】。例如上文 <ModelConditionEvent Name="ReallyDamaged"> <Conditions>+REALLYDAMAGED</Conditions> </ModelConditionEvent> 如果当在这个事件发生时,就要做某个函数的事情,那ReallyDamaged事件只能传递给这个函数【获得了REALLYDAMAGED状态的这个单位】的信息。 |