闪电战1脚本编写基础教程闪电战论坛.docx
《闪电战1脚本编写基础教程闪电战论坛.docx》由会员分享,可在线阅读,更多相关《闪电战1脚本编写基础教程闪电战论坛.docx(39页珍藏版)》请在冰豆网上搜索。
闪电战1脚本编写基础教程闪电战论坛
序言
闪电战1发行之时,地图编辑器与资源编辑器作为“赠品”与游戏一同打包。
然而编辑器的帮助文件内容有限,而且完全没有介绍如何通过编码(脚本)控制自定义任务内的事件。
全靠累积玩家们在各个论坛上分享的发现,我们才能找出如何发挥编辑器的最大效能。
单从脚本语言LUA方面来看,同样是依靠准确解码、反复试错以及广泛开放的发现共享才得以完善。
数量众多的个人玩家以“破解”Lua为己任来制作自定义地图,为跟随者开创了一条更为平坦的道路。
直到这些先驱人物在“闪”界隐退,每一位玩过或尝试过制作自定义地图的玩家都应该感谢那些慷慨分享心得的编码破译者大量的辛勤劳作。
破译编码过程中产生的重要贡献之一是Calvin的BlitzkriegGuidetoProgrammingLuaFunctions。
它提供了一份闪电战1中使用脚本函数的列表以及如何运用这些函数的指导。
它对于编写闪电战脚本的人是十分必要的。
这个指南像是一本LUA词典,但它没有提供如何将这些术语组合成可用脚本的浅易说明。
(Calvin提供了一个例子,但是这个例子不能被称为“简介”所以脚本编写的新手最好忽略它。
)Calvin的BlitzkriegGuidetoProgrammingLuaFunctions可以在BlitzkriegPortal下载。
Wespex是第一位制作完整编辑地图指南的人,为此他编辑了地图,编写了脚本,并且打包了可以与其他玩家分享的自定义游戏。
这个指南(再版很多次并最终与Calvin的指南合并)是任何希望制作自定义地图玩家的入门教程。
在发布这个指南之后,Wespex在BKP论坛就脚本编写提供了非常广泛的问题答疑。
这些指南和答疑最有价值的特征是在脚本范例中提供了全面的注释,不仅提供了制成的脚本,还解释了脚本运作的原理。
如果说现在绝大多数脚本编写者是靠这个指南启蒙应该是不失公允的。
这个基础教程的编写是为了响应BKP论坛上的一些评论以及解决很多地图编辑者在开始尝试脚本编写时所面临的困难。
本教程阐述Lua的基本原理并展示如何为自定义游戏的某个基本要点编写脚本。
我将尝试揭开某些技术术语的神秘面纱。
考虑要点
像每一个刚开始编写Lua的人那样,我非常熟悉入门的巨大障碍以及脚本无效时的那份挫败感。
无论如何,有些事情应该记在我们的脑子里:
Lua是一种语言,你必须学会单个词句的意思以及如何运用他们来“涂鸦”。
即使你聪明绝顶,这也并不意味你可以不用认真学习词汇和语法就来编写Lua脚本。
和人类语言不同,即使你在Lua语句中犯了一个微不足道的错误,它也将彻底无法被程序理解。
开始编写Lua时的大多数挫败感是来源于那些“一点不起作用”的脚本,“这几乎不可避免的是你的错误!
”自然的反应一般是说:
“程序不在运转”或者“我的电脑出毛病了”。
但有99.9%的可能是你犯下了一个拼写或语法错误。
制作自己的地图并看到自编脚本运转是非常有趣的;但让脚本运转起来并不总是有趣的。
即使BKP论坛上最优秀的脚本编写者也承认在调试自己的作品时仍然遇到大量挫折。
并非每个人都有足够耐心只为了一个错位的字母、一个遗漏的括号或一个多余的分号来逐行检查脚本。
如果想要编写脚本你必须接受这个事实,没有任何人能替你简化它。
一旦你已经了解Lua的基础,最佳学习方法是打开一张你喜欢的地图并且逐行研究它的脚本(在手边准备一份Calvin的指南。
译者注:
或liukun1982翻译的script帮助
文档)以弄清具体的原理。
如果你想测试一些新脚本或从其他编写者那里“借”来的脚本,不要在你乐于为傲的26X26地图上进行测试。
制作一张4X4的测试地图,放上最少的单位,仅测试脚本中对你来说是新鲜内容的代码块。
如果脚本不运行,你可以很快发现问题而不需要检查一大堆其他不相干的东西来判断是否存在冲突。
这是本教程所采用的方针。
如果你确实遇到了困难(在浏览了BKP写的抓虫须知(本书第33页)与这些教程以后),你可以在论坛中进行搜索,大多数问题已被讨论过了。
如果你仍然无法找到答案,你可以在地图编辑问答专栏发布求助贴。
如果你的问题明确具体(最好复制粘贴一些你的脚本),那些脚本编写的大侠们将会给我们这些小菜鸟提供迅速而有益的答复。
基础教程使用
以下每一节教程都配套一份与教程同名的地图(.bzm)与脚本(.lua)文件。
所有.bzm文件和.lua文件应被复制粘贴到闪电战的Data\Maps文件夹。
这也是你用来保存自定义地图与脚本的路径。
为了学习每一节教程,你可以用地图编辑器打开地图,用SciTE编辑器打开脚本。
当你阅读完教程并看完地图和脚本内容以后,你可以按下地图编辑器工具栏右上角的RunBlitzkrieg按钮看看脚本在游戏中是如何执行的。
前五节课程的每张地图都是建立在前一张地图的基础之上,地图和脚本一步步升级。
最后两节课程是独立的,涵盖了BKP论坛中经常提到的问题。
本教程推荐你下载BlitzkriegSciTEEditor。
它非常有效,是免费软件,体积不大,没什么理由不用。
它不能替你编写脚本,当脚本无法运行时它也不能替你找出错误。
但在编写与检查脚本时它对你很有帮助,所以还是下载吧!
BlitzkriegSciTEEditor
(译者注:
由于这个编辑器在处理中文时默认以半角格式处理,所以退格删除中文时只删除半个,从而出现乱码。
建议中文的地图编写者最好加装汉化补丁,从而能完美支持在脚本中输入中文。
)
注:
使用SciTEEditor给文件命名时你必须使用一个.lua后缀否则它会被保存为.xml格式。
基本原理
脚本是一个lua文件。
在地图编辑器上有一个“扳手加字母A”的按键,点击这个按键(译者注:
设置脚本文件名)输入脚本名称,来把地图和相应的lua脚本文件联系起来。
脚本包含一系列的指令,叫做functions(函数),当地图运行时,这些函数使事件发生。
函数可以很简单,可以只是(指令电脑)在特定时间完成一个事件,或是更复杂的,测试某个条件,并根据结果做出不同的事情。
注意:
:
附录A给出了一个闪电战特有函数列表
为了使函数和地图配合,地图上的单位,步兵班,建筑等必须指定一个脚本代码(ScriptID),在地图编辑器中,在左边选择Objects界面,双击单位调出“单位属性窗口”,然后双击“ScriptID”文本框并输入一个代码(ID),然后关闭属性窗口。
提示:
做一个笔记记录哪个代码是哪个单位。
有的地图制作者常常在脚本的开头写下他们的编码方式。
提示:
一个很有用的方法,是给予玩家单位和敌方单位以不同字头的代码(如100s和200s)
很多函数要用到脚本区域(ScriptAreas也译作脚本范围)。
用地图编辑器左边的MapTools按键来定义这些脚本区域:
选择“脚本范围工具”,并且拖动鼠标在在地图上定义一个(方形或圆形)区域,然后输入这个区域的名称。
编写脚本时必须准确的使用这个“脚本区域名称”,包括大小写和空格等都要一样。
提示:
给你的脚本区域一个独特而有明确含义的名字,这样你不会在脚本中混淆。
(Farm和Town,而不要用Area1,Area2)
Lua语言
Lua的语法是简单而固定的,必须严格遵守。
任何拼写,间隔(空格),或标点的错误,都会导致脚本不运行或不能正确执行函数的功能。
多数的脚本问题都是拼写或语法错误。
下面的词语在Lua中有特殊的含义,被认为是保留字(keywords):
andbreakdoelseelseif
ifendfalseforfunction
inlocalnilnotor
repeatreturnthentrueuntilwhile
无论你认为这些词语在现实中是什么意思,在Lua语言中,他们有固定的用法,并且只能那样用。
其中一些词语的用法在下面的教程中会详细解说。
按照规则,这些保留字要用小写。
这里也有一些闪电战的特定保留字,象“RunScript”,“LandReinforcement”,“Win”and“Loose”等。
这些必须正确书写----首字母大写和其它字母小写。
Calvin的指南就像一部字典,可用来指导我们怎样正确书写这些词语。
提示:
如果使用闪电战脚本编辑器(theBlitzkriegSciTEeditor),那么所有的保留字,如果你的拼写正确,会变成蓝色并且以浅蓝背景高亮显示。
脚本编写者所定义的事件(函数)的名称叫“names”。
它们可以是任意数字和字母,但不能以数字开头。
按照规则,我以大写字母开头为(函数)名称。
举例来说,在第一课我写的:
functionMoveCar()
在这里“function”是保留字,而“MoveCar”是名称
提示:
如果使用闪电战脚本编辑器(theBlitzkriegSciTEeditor),那么(保留字是蓝色而)名称是黑色的。
当一个函数的名称确定以后,它就可以在后面被脚本调用。
当它被再次使用时,必须放在双引号内作为字符串被调用。
如我在第一课中写的:
RunScript(“MoveCar”,3000)
这里“RunScript”是闪电战专用保留字,“MoveCar”是字符串,代表我在脚本中定义的一个函数。
一个绝对的要点是这个字符串要被正确的键入,和之前的函数名完全一样,否则不会被脚本认可。
由于这个原因,用复制粘贴函数名和字符串是最好的办法,可以防止键入错误。
在使用双引号时,在引号和字符串的头以及引号和字符串的尾之间不许有任何空格。
提示:
如果使用闪电战脚本编辑器(theBlitzkriegSciTEeditor),那么当引号的位置正确时,字符串显示为紫色文字。
如果你看到紫色背景的高亮显示,说明双引号输入有错(少了一半)。
注意,这个
脚本编辑器不会检查你键入的字符串正确与否函数和保留字是要用参数来限定的。
要明白这个参数的作用,想象函数和保留字就像有人大声对你喊“做”,但这没有任何意义,直到你被告知做什么,什么时候做,做的频率多少,这些就是限定函数去做的参数。
在Lua中参数总是被放在一对括号内,如RunScript(“MoveCar”,3000)保留字“RunScript”需要知道运行哪个函数,什么时候运行。
这两个在括号内的参数,被定义为运行字符串“MoveCar”以及值(value)为3000(这个在后面的教程中解释).
提示:
如果使用闪电战脚本编辑器(theBlitzkriegSciTEeditor),这个数值会显示为淡蓝色。
当有超过一个的以逗号隔开的参数,来赋予这个字符串或值,很重要的一点,如果这个逗号漏写了,脚本将不能运行。
有些保留字或函数不用任何的参数定义,那就是说它们对于脚本是不解自明的,不需要用例如什么,哪里,何时,频率如何等来定义。
没有参数的保留字或函数的括号是空的,但这对括号仍然必须保留。
比如Suicide()以及函数名后的括号。
漏掉一个空括号是脚本不运行的一个常见原因。
缩排是脚本编写者经常用到的(在编辑器中按Tab键),但这不是必须的也不会对脚本有影响。
当然它们非常有助于使语句有条理。
我会在举例中用我自己的方法,而脚本编写者有它们自己的方法。
按照规则,脚本的每一行结尾要有一个分号“;”,(除了某些情形下如一个保留字将直接对下一行执行操作--这在下面的第三课会解说)
备注:
有时候也会包括在脚本中,用于帮助脚本编写者明白或提示某些事情或者告诉其他人写的是什么。
一条备注开始和结束于至少两条短横,如“--NOTE--”(译者注:
只需要开头的两条短横;备注
可以是中文但短横等标点必须是英文标点)。
备注不会被脚本读取,所以在行首和行尾(译者注:
只需要在行首)键入两个短横可以使这一命令行丧失功能。
这一点在测试脚本过程中非常有用。
提示:
如果使用闪电战脚本编辑器(theBlitzkriegSciTEeditor),这个备注会显示为绿色。
第一节––编写一个函数并使它运行
这一课介绍怎样使一个单位从它的初始位置移动到定义的新位置
第一节的地图
打开地图,这里有一辆小轿车(ScriptID为100)(双击单位可查看ID号)和轿车将要到达的一栋建筑
Lesson1.luaScript脚本
functionMoveCar()
Cmd(0,100,916,1516);
Suicide();
end;
functionInit()
RunScript("MoveCar",3000);
end;
注意:
这个脚本以后缀为lua的文件保存,你可以复制粘贴这个脚本的语句到你自己写的脚本中。
一定不要用微软的Word软件来编写脚本,因为Word允许插入不同内码的文字,而这会导致游戏运行过程中止(即忽然退出)。
解说
functionMoveCar()
建立一个名叫“MoveCar”的函数,这个函数能被脚本调用。
Cmd(0,100,916,1516);
命令单位(ID100)“移动(0)”到坐标为x,y(东916,北1516)的新位置。
Suicide();
(自毁)告诉脚本不要继续运行这个函数。
(没有这一句,表示函数将无限次反复运行)
end;
(结束)关闭这个正在运行的函数(这是一个函数语句的结尾,没有这个表示语句不完整,也会导致脚本中止(即游戏退出))
functionInit()
这是一个初始化函数,每个脚本必须有一个functionInit,否则脚本不会运行!
RunScript("MoveCar",3000);
就像这句话说的一样!
运行这个叫“MoveCar”的函数(不要忘记双引号)和何时运行(游戏开始后3000毫秒也就是3秒)
end;
结束语句。
注意:
可以在Calvin的指南或liukun1982翻译的script中找到Cmd命令列表
第二节––添加第二个有多项命令的函数
这一节介绍使第二个单位在与第一个单位不同的时间点,做一系列移动。
第二节地图
这里有一辆小轿车(ScriptID100),一辆卡车(ScriptID101),一棵树和一个纪念碑。
卡车将绕过纪念碑驶向建筑。
Lesson1.luaScript脚本
functionMoveCar()
Cmd(0,100,916,1516);
Suicide();
end;
functionMoveTruck()
Cmd(0,101,1583,223);
QCmd(0,101,1890,1570);
QCmd(0,101,1100,1890);
Suicide();
end;
functionInit()
RunScript("MoveCar",3000);
RunScript("MoveTruck",7000);
end;
解说
functionMoveCar()
Cmd(0,100,916,1516);
Suicide();
end;
functionMoveTruck()
一个新函数,起名MoveTruck
Cmd(0,101,1583,223);
跟第一个函数一样的移动命令
QCmd(0,101,1890,1570);
QCmd和Cmd一样,只是它产生于前一个命令之后。
这个语句命令同一辆卡车在到达第一个点后继续驶向第二个设定的地点。
QCmd(0,101,1100,1890);
Suicide();
end;
functionInit()
RunScript("MoveCar",3000);
RunScript("MoveTruck",7000);
这个指示运行第二个函数,但时间是游戏开始七秒后
end;
注意:
第二个函数也可以象下面的脚本一样由第一个函数来调用。
这是经常被用到的手法,适用于第二个函数肯定在第一个函数被执行之后被调用。
这样做的好处是不用同一时间调用很多函数,而是只在需要时才调用特定的函数,从而可以减少CPU资源的占用。
.
脚本
functionMoveCar()
Cmd(0,100,916,1516);
RunScript("MoveTruck",4000);
Suicide();
end;
functionMoveTruck()
Cmd(0,101,1583,223);
QCmd(0,101,1890,1570);
QCmd(0,101,1100,1890);
Suicide();
end;
functionInit()
RunScript("MoveCar",3000);
end;
解说
functionMoveCar()
Cmd(0,100,916,1516);
RunScript("MoveTruck",4000);
第二个函数现在从这里被调用。
为使卡车在游戏开始后7秒开始移动,我们要把时间改为4秒,因为在游戏开始后第一个函数“MoveCar”已经运行了3秒
Suicide();
end;
functionMoveTruck()
Cmd(0,101,1583,223);
QCmd(0,101,1890,1570);
QCmd(0,101,1100,1890);
Suicide();
end;
functionInit()
RunScript("MoveCar",3000);
现在这个运行“MoveTruck”的命令可以从Initfunction中去掉了。
end;
第三节––测试一个条件(是否具备)
这一节将介绍测试一个条件是否达到特定值从而触发一个新的应激行动。
这是在闪电战地图中非常有用并且常用的程序设计。
第三节地图
这个地图上,在房子周围,有一个名为“Farm”的脚本区域,和两个脚本代码(ScriptID)为“103”,“104”的步兵班.这两个步兵班隶属于援军组“103”(reinforcementgroup>103<).在地图编辑器中点击ReinforcementGroups(援军)键可以看到>103<包含两个脚本代码ScriptID(103和104).这是必要的,如果我们要命令这两个步兵班做不同的事情,就需要两个不同的ID。
这栋建筑的
脚本代码(ScriptID)是“10”。
Lesson3.luaScript脚本
functionMoveCar()
Cmd(0,100,916,1516);
RunScript("MoveTruck",4000);
Suicide();
end;
functionMoveTruck()
Cmd(0,101,1583,223);
QCmd(0,101,1890,1570);
QCmd(0,101,1100,1890);
Suicide();
end;
functionFarmCheck()
ifGetNUnitsInArea(0,"Farm")==2then
RunScript("SquadArrive",1000);
Suicide();
end;
end;
functionSquadArrive()
LandReinforcement(103);
RunScript("SquadMove",2000);
Suicide();
end;
functionSquadMove()
Cmd(0,103,1530,1320);
Cmd(6,104,10);
Suicide();
end;
functionInit()
RunScript("MoveCar",3000);
RunScript("FarmCheck",2000);
end;
解说:
从现在起只列出新的语句。
functionFarmCheck()
ifGetNUnitsInArea(0,"Farm")==2then
“if”是保留字,用于测试某个条件,以返回两个可能的答案:
真或假.在这里测试的条件是在某个地域范围内的单位数量:
“GetNUnitInrea”表示某一方的单位(“0”,表示玩家单位,“1”表示
敌方单位)在某地(”Farm”,不要忘记双引号).条件是“х==2”表示单位数量是否等于2?
(本课结尾列出了比较符号的意义).“then”是另一个保留字,用于定义当这个测试的答案为“真”,将发生什么。
由于“then”将执行下面一行的命令,所以“then”后面没有“;”(分号)
测试的逻辑答案为真,那么运行另一个函数“SquadArrive”。
如果答案为假,那么没事发生但函数继续运行(继续测试这个条件)Suicide();
end;
这个是“if”语句的结束。
作为一个规则,你需要为每个“if”语句加上一个“end;”作为结束
end;
这是整个函数的结束
functionSquadArrive()
LandReinforcement(103);
RunScript("SquadMove",2000);
这一句调用援军组103到地图上
Suicide();
end;
functionSquadMove()
Cmd(0,103,1530,1320);
这一句命令代码为(103)的单位移动(0)到坐标(1530,1320)
Cmd(6,104,10);
这一句命令代码为(104)的单位进入(6)代码为(10)的建筑
Suicide();
end;
functionInit()
RunScript("MoveCar",3000);
RunScript("FarmCheck",2000);
这个语句表示在游戏开始后2秒运行函数“FarmCheck”。
因为这个是条件函数,所以什么也不会发生,直到条件符合
end;
提示:
如果你用BlitzkriegSciTEeditor(闪电战脚本编辑器),每一行左边会有一个减号,对应地需要一个“end”(译者注:
点击减号,这个减号和对应的“end”之间的语句可以折叠起来,减号变为加号。
)。
我把所有的end放在左边,以便检查有相同数量的“end”对应上面的减号。
注意:
在条件语句中下列符号的含义:
a==ba等于b
a>ba大于b
aa>=ba大于或等于b
a<=ba小于或等于b
a~=ba不等于b
注意:
一个有用的模块
下面这个脚本模块能在我写的每个脚本中找到,因为它有助于(地图和脚本)测试:
functionDebugView
Password("Panzerklein");
DisplayTrace("anytextyoulike");
ShowActiveScripts();
--God(0,2);
Suicide();
end;
functionInit()
Runscript(“DebugView”,1000);
end;