【代码篇】LUA系统基础教程(搬运,经过苯环授权)

zyb123 11月前 848

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的事情”。

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这些都很好理解。 而状态事件也好理解,比如

就是一个典型的状态事件,它表示一旦这个单位获得了REALLYDAMAGED的状态时,就视为有了ReallyDamaged这个事件。

5.写码套路
       所谓的LUA系统,就是一句话“当Event的时候,就做Function的事情”,当然这句话要转化为xml语言写进mod源码里,一般写在*\Additional\Data\scripts的ScriptEvents.xml文件内,比如:
       <EventList
Name="JapanAntiVehicleVehicleTech1Functions"
Inherit="BaseScriptFunctions">
              <EventHandler
EventName="ReallyDamaged
ScriptFunctionName="OnJapanAntiVehicleVehicleTech1ReallyDamaged"DebugSingleStep="false"/>
       </EventList>
       其中<EventHandler />里的这便是“当ReallyDamaged的时候,就做OnJapanAntiVehicleVehicleTech1ReallyDamaged的事情”这句话的翻版。那么“当ReallyDamaged的时候”到底是什么时候呢?就是:
       <ModelConditionEvent
Name="ReallyDamaged">
              <Conditions>+REALLYDAMAGED</Conditions>
       </ModelConditionEvent>
       这段也要写到ScriptEvents.xml里(实际上官方文件有),这段就是在说“一旦这个单位有REALLYDAMAGED的ModelCondition,就视为发生ReallyDamaged的事件”。
好了,“当……”有了,做什么呢?做OnJapanAntiVehicleVehicleTech1ReallyDamaged,这个函数要写到*\Additional\Data\scripts的Scripts.lua里:
       functionOnJapanAntiVehicleVehicleTech1ReallyDamaged(Self)
              ObjectDoSpecialPower(Self, "SpecialPower_ToggleEnergizedArmor" )
End
好了,我们来检查一下。做OnJapanAntiVehicleVehicleTech1ReallyDamaged这个函数到底是做什么呢?
就是做ObjectDoSpecialPower这个函数呀。
那这个函数又有什么用呢?
就是使用名为SpecialPower_ToggleEnergizedArmor的技能嘛。
那谁使用技能呢?
Self使用。
谁是Self?
主函数OnJapanAntiVehicleVehicleTech1ReallyDamaged的Self咯。
所以是谁呢?
就是ReallyDamaged事件里说的那个啊!
哦,好了,原来是“当这个单位ReallyDamaged的时候,就做该单位开技能的事情”。这个单位那怎么就是海啸了呢?而不是天狗?不是武士呢?
所以需要在海啸这个单位的Behavior写上这一段呀:
              <LUAEventList
                     id="ModuleTag_LUAEventList"
                     EventListName="JapanAntiVehicleVehicleTech1Functions"/>
       记得这一定要和EventList对应上。
6.设计思想和高级用法
       如大家所见,LUA系统十分繁琐,一个微小的改动都需要改变三四个文件。更麻烦的是SDK本身不支持LUA系统的查错,也就是说你任何一个ID/Name只要打错一个标点符号就全盘皆乱,所以在练习的时候应该循序渐进,由小及大,先学会像自动技能、剥夺操作权、播放换弹音效之类的基本操作。
       事件(Event)和函数(Function)是LUA系统最重要的两点,利用事件的自发性我们可以用LUA来做许多自动行为,在Kindof/ObjectFilter难以涉及的一些条件下作出游戏效果;而利用函数的灵活性,我们可以执行地图编辑器的脚本,或者实现一些复杂的多单位互动效果,例如给予/剥夺Upgrade、空放/对目标使用技能、伤害/杀死/抹去单位、显示/隐藏模型、改变单位所属/阵营色、播放音乐/音效/影片、调节音量、杀死/踢出玩家、裁判胜负、计数器等功能。这里有一份SAGE全LUA命令文件,大家可作参考,具体函数的变量如何选择可以参考地图编辑器脚本。
       关于事件的触发,出了自带的触发之外,武器也可以触发事件,例如这种Nugget:
       <LuaEventNugget
              id="2"
              EventName="EASB"
              SendToEnemies="true"
              SendToAllies="true"
              SendToNeutral="true">
       </LuaEventNugget>   
       此外,LUA函数中ObjectBroadcastEvent系列函数也可以触发事件,例如:
       ObjectBroadcastEventToUnits(self,EASB,200)
LUA函数中有许多判断类函数,例如ObjectHasUpgrad,以及所有的EvaluateCondition系列函数。它们有一个返回值,如果判断成立则返回1,判断不成立则返回0,这样的判断能弥补通常用ObjectFilter所带来的不足。例如下面的函数就可以让某个单位在升级后自杀。
function Upgradekill(self)
if            ObjectHasUpgrade(self,“upgrade_killyourself”)==1
              then       ExecuteAction("NAMED_KILL",self)
              elseif     return
              end
end

7.总之
       LUA能绕过Behavior、OCL、Weapon等传统代码,实现复杂而神奇的功能,希望这个教程能够帮助大家读懂LUA函数、拓展设计思路、优化复杂代码、发掘引擎功能。谢谢各位。


星火战队-Ngen
2018年8月14日


最后于 11月前 被zyb123编辑 ,原因:
上传的附件:
最新回复 (4)
  • 洪敬2333 11月前
    0 引用 2
    大哥你的分行键坏掉了吗
  • 014 11月前
    0 引用 3
    硬核搬运
  • Ngen 11月前
    0 引用 4
    我的NB格式都被搬没了
  • zyb123 11月前
    0 引用 5
    Ngen 我的NB格式都被搬没了
    。。。。。
    分行了==
    • RAT红警社区
      6
        登录 注册 QQ登录(不可用)
返回