【ERM教程】ERM专业版快速入门教程(写给有编程经验的人)
写过很多ERM教程,论坛也有很多教程,其实大部分,都是作为一个普通WOG玩家的角度进行撰写的,内容很多余、繁杂。本教程写给程序猿、业余编程者或其他懂编程语言有编程经验的人,以专业编程的角度帮助以上这些人快速入门ERM(看完基本都明白了)。
本教程以C/C++作为参考语言(用于进行对比),本教程不会细致讲解ERM的语句和算法,但是可以帮助ERM编写者如何去读ERM帮助文档。
没有相关程序编写经验的童鞋可以自行略过。
楼下开始。
第一节:准备工作
在进行ERM编写工作前,我们要了解ERM的作用。ERM是英雄无敌3WOG的嵌入语言,用于改变WOG游戏中的一些参数,将游戏中怪物、英雄、宝物、资源、玩家参数、城镇等因素进行修改和个性化,实现WOG游戏智能化、自由化和多样化。
ERM语言繁杂多样化,语法记忆难度高,所以我们需要使用到ERM的编程手册(ERM帮助)进行查找相关代码。
在进行ERM编写工作前,我们需要准备好编写和测试环境:
1、英雄无敌3WOG或ERA(用于测试ERM脚本):
WOG358F:http://www.wogcn.net/thread-34548-1-1.html
WOG359A(ERA):http://www.wogcn.net/thread-38766-1-1.html
下载安装后,你可以找到WOG地图编辑器,这个编辑器用于将你的ERM代码加入游戏中运行。
2、ERM_S:编写ERM的专业工具:
3、ERM编程手册(列出了所有的ERM命令代码):
http://www.wogcn.net/thread-38278-1-1.html
4、ERM变量申明平台(申明变量,防止变量冲突):
http://www.wogcn.net/ermres/
5、本论坛ERM区可以查阅或参阅相关成品脚本:
推荐:
成长性生物培养:二追
算法:魔法CD
游戏修改:怪物加强III、典当行
——————————————————————————————————————————————————————
在准备好工作环境后,你需要了解ERM是如何工作的,我们编写的ERM脚本存放于地图的事件中(在地图编辑器的选项里,添加新的事件)。
只需要将写好的代码存放于事件中,则这些代码将会在游戏中进行工作。
大家都知道,H3中的事件会在特定的时间弹出,为了防止这种情况,WOG对事件与脚本进行了这些区分:
1、游戏不会对普通事件进行编译,而是会对ERM事件进行编译,为了区分ERM事件和普通事件,我们需要在你的ERM代码的最开头,写上一句:ZVSE
ZVSE用于区分普通事件和ERM事件,游戏将会对所有开头带ZVSE的事件进行编译和运行,其他事件则作为对话框在特定的时间弹出。
注意:便于修改和完善脚本,你不必将所有代码写在同一个事件中,可以将你的脚本分成很多块加入多个事件中。比如说,二追(part1)、(part2)。。。。。在每个ERM事件的开头,必须附上一句ZVSE
2、为了防止ERM脚本弹出给玩家看到,你可以把触发天数改为一个比较大的数值(游戏不可能在那天之前还没结束),比如说:500、600等。
[ 本帖最后由 克招 于 2013-5-8 19:19 编辑 ]
第二节:变量
既然是专业的教程,我就直接写变量,不再举例什么hello world之类的例子。这一节我们会讲解变量和数组。ERM中不存在指针。
在ERM中,我们不需要单独申明或定义变量,ERM系统给与我们17个变量和7个数组,这些变量和数组的类型、容量是固定的,我们不需要也无法重新申明,也无法使用自己自定的变量。
ERM中,宏用于给一个变量设定一个特殊的名称和含义,并无法改变该变量原有的属性。
以下,我将列出所有的变量和数组:
c:系统变量,该变量是只读的,这个变量的值为当前游戏的天数。
d:系统变量,该变量是只读的,该变量的值自动读取当前工作的堆栈的值。
(如当我们需要对1号英雄的攻击力进行修改,那么d将保存1号英雄的攻击力,我们调用这个变量,并在其后加上一个其他值<形如:d5>,那么结果是1号英雄的攻击力增加了5)
f ~ t(从f到t的英文字母): 全局变量,又称快捷变量,类型为int。
v:全局一维数组,类型为int,容量为10000,分别为v~v。
y:当前一维数组,类型为int,容量为200,分别为y[-100]~y[-1]、y~y。仅在当前函数中有效,在每个函数【每次】使用前,将其初始化为0。
z:一维字符串数组,类型为sprint,容量为1010,其中,z[-1]~z[-10]为当前数组,z~z为全局数组。用于储存字符串。z[-1]~z[-10]仅在当前函数中有效,在每个函数【每次】使用前,将其初始化为""。
e:一维浮点数组,类型为float,容量为200,其中,e[-100]~e[-1]为当前数组,e~e为全局数组。用于储存浮点数。e[-100]~e[-1]仅在当前函数中有效,在每个函数【每次】使用前,将其初始化为0.0。
x:当前一维数组,类型为int,容量为16,分别为x~x,用于将一个函数内的数值传递到下一个函数。如:将函数BO中的y1保存为x1,并将x1传递到FU3函数,然后在FU3函数中直接调用x1。
w:全局二维数组,又称英雄变量,类型为int,容量为156x200,分别为w~w。
flag:全局布尔数组,类型为bool,容量为1000,分别为flag~flag。
需要注意的是,在ERM中,数组的下标不需要也不能用符号隔开,如v可以直接写为v1。
注意:我们可以直接将数组中的一个元素视为一个单独的变量,并用变量的形式和方法使用之。
从下节开始,我将直接把这些数组中的一个元素叫做“变量”,将flag数组中的一个元素叫做“标志”。
特别注意:
1、w数组:w数组的一维下标不需要填写,我们可以直接填写二维下标,其一维下标需要用一句代码固定,如当这句代码将w数组的一维下标定义为1时,那么从此刻开始,我们所有使用的w数组,一维下标永远为1,直到一维下标被重新定义。
2、flag数组,不同于常规数组的读写和使用方式,可以查阅ERM帮助中的flag部分。(使用IF:V进行写,使用&进行读)
所有变量的管理、读写和计算,请查阅ERM帮助中的VR、VC、flag&variable章节,使用宏对变量的进行命名和管理请查阅MC章节。
[ 本帖最后由 克招 于 2013-5-11 13:23 编辑 ]
第二节:语法与函数(一)
继续上文所说,从这节开始,数组中的一个元素,我称之为“变量”,因为它其实就是ERM中的变量。这节要讲的是ERM的语法和函数。
其实ERM的语法很简单,所有语句遵从:语句主类型+语句子类型+条件+命令。
形如:
ZVSE
!#MA:A15/15; 将15号生物的攻击力改为15
!?OB12; 12号建筑物的触发函数
!!HE-1&v1=5:W100; 当y5==5时,将当前英雄的可移动点数改为100
从以上的语句不难看出,ERM的语法遵从以下格式:
1、每个脚本的开头,以ZVSE标注。
2、所有语句以感叹号开头,以分号结束。
3、在分号结束的后面,可以作自己的任何注释,长度不限。
4、语句的分行、空格不会对代码造成任何影响。
其中,每句语句遵从以下格式:
一、语句主类型:语句的主类型分为三种,分别为!#\!?\!!
1、!?:函数定义语句。(触发器)
——————————————————————————————————————————————————————————————————————
插入讲解:
ERM中的函数
ERM中的函数分为三种,它们和上文中的变量一样,系统自带提供,类型固定,不需要且无法重新申明,无法使用自己申明或定义的函数。
1、主函数:也就是main函数,这个函数不需要使用!?语句进行函数定义,所有使用!#开头的命令语句,直接视为主函数中的语句。(!#的讲解详见下文介绍)
2、系统函数:系统函数使用!?语句进行定义,其子类型有上百个以上(很难统计到底有多少个),分别为AE0~TM100。所有子函数类型可以在ERM帮助中的Triggers章节中找到。
系统函数又称为触发器,与C语言不同的是:它们不能在主函数或系统函数中引用或跳转(也就是说,不能直接从主函数或其他系统函数中读取或运行某个系统函数)
它们具有一定的触发方式,你也可以理解为:系统函数其实是每个游戏动作所触发的主函数。例如,当一个英雄访问一个建筑物时,就会触发相应的!?OB函数。
从下文开始,我将称呼所有系统函数为:【触发器】。
3、自定义函数:自定义函数相当于C语言中,由编写者申明、定义的自编函数。它们可以从其他函数中跳转或引用,可以使用x变量传递数据。
自定义函数的名称为系统自定,共有30000个,分别为!?FU0~!?FU30000这些函数名称就是它的子类型。所有自定义函数的类型为void。
我们可以使用!#FUx:P;或!!FUx:P;语句调用自定义函数。(x为自定义函数的编号1~30000)
它属于触发器的一种,从下文开始,我将称呼所有自定义函数为:【函数触发器】。
插入完毕。
————————————————————————————————————————————————————————————————————————
2、!!:触发器中的命令语句。
跟在触发器的后面,用于调整游戏参数、变量数值等。
在触发器被触发后,所有跟在该触发器后面的!!语句会按顺序依次执行。与触发器对应,我们称之接收器。
接收器的子类型见ERM帮助中的receivers章节,所有接收器不能独立于触发器单独写出,否则会造成语法错误直接跳出。
3、!#:主函数中的命令语句。
不跟在任何触发器的后面,它属于主函数的一部分,由于主函数不需要单独调出,所以使用#符号与接收器区分,其用法与接收器完全一样。子类型和接收器完全一样。
main函数在游戏开始前(就是loading游戏时在读条中)触发,所有!#语句会按顺序依次执行。
!#语句的用处在于,在游戏开始前对所有游戏数据进行申明、定义或初始化,说明该游戏中的相关信息,所以!#语句称为说明部分。
当然,在实际编写过程中,有些接收器语句不能用于说明部分中(如子类型为CM的接收器),这点大家在今后会有所体会,在此不详细解释。
4、引申:!$:!$其实是!?的另一种写法,也是触发器,用法和!?一模一样,但其有着极其细微的差别,这个差别会在后面提到。
二、子类型
上文中其实已经连同子类型作了少许介绍。
语句的类型定义了它是何种语句(触发器、接收器或说明部分),而子类型则定义了这句语句与游戏中什么元素相关。
举例:子类型MA的语句与游戏中的生物相关,HE的语句与英雄相关,OW的语句与某种颜色的玩家相关,FU的语句则与函数触发器相关。
触发器的子类型可以在ERM帮助中的Triggers章节中找到,接收器和说明部分的子类型可以在Recievers章节中找到,你也可以进入ERM帮助最顶部的AI~VR、Other Objects章节,查看每种接收器子类型下的各种【命令】的语法。
子类型并不指的是两个字母,而是整个子类型。比如说,用于坐标位于0/0/0位置的建筑物的触发器!?OB0/0/0;的子类型和!?OB0/1/0;的子类型是不同的。所以说,可以说,子类型有无数种,但我们为其分为几十类(AI~VR)。
[ 本帖最后由 克招 于 2013-5-11 13:27 编辑 ]
第四节:语法与函数(二)
接上文三、条件
条件的格式以 & 或 | 开头,用于隔开类型部分。(& :和,| :或)
每句语句在运行前会检查条件是否满足,只有当条件满足时,该语句才会运行,否则跳过该语句。
条件部分使用变量或标志进行比较,每句代码的条件部分可以用多个条件进行限定,每个条件之间用 / 或 | 隔开。(/ :和,| :或)
下面,举例一些条件的用法:
1) &v1=1 当v1=1为真
2) &v1=v2 当v1=v2为真
3) &v1=1/v1=v2 当v1=1且v1=v2为真
4) |v1=1/v1=v2 当v1=1或v1=v2为真
5) &v1=1|v1=v2 同(4)
6) &1 当标志1等于1为真
7) &-1 当标志1等于0为真
8) &v1=1/v1=v2|1 当“v1=v2或标志1等于1”为真,且v1=1为真
9) &v1<1/v2>=1/v3=>1/v4<>0/v5><0 当v1小于1,且v2大于等于1,且v3大于等于1,v4不等于0,v5不等于0都为真
10) &v1=<>0 该语句不运行
1、2、3、4我不讲解,下面说说5、6、7、8、9、10
5) 其实是(4)的另一种用法,当条件部分以&开头时,可以在条件之间用 | 相隔,代表两个条件是或的关系
6)7)标志条件
如果忘记了标志是什么东西,可以看看第一节中的flag布尔变量部分。前面我提到过,当标志用于作条件时,可以用正与负表示真与假,用后面接的数字表示flag布尔数组的下标。
8)语句(5)的扩展,当&开头时,既有用 / 又有用 | 相隔的条件,则优先运算“或”部分,再运算“与”部分。
有时候会有很复杂的东西,比如说:&1/-2|3|4/-5|6,这种东西是很让人蛋疼的,所以有时我们会用一些比较简便的方法来更直观化,后面会讲到。
9) 不等号运算,以上列举了所有的不等号使用规则
10)由于条件矛盾不可能得到满足,所以语句不运行(极少使用这种格式,列出此条用于让大家更好理解)
【特别注意:1、只能用最简单的变量比较进行定义条件,不能在条件部分中运算(如&v1+1=2),否则语法错误跳出
2、int变量与bool变量(标志)不能进行比较,int变量和浮点变量可以比较
3、当条件不存在(不需要条件直接运行)时,则条件部分为空,不需要&进行分隔,直接写下一个部分。】
———————————————————————————————————————————————————————————————————————————————————
以上部分为触发器、接收器、说明部分中都可以出现的部分。接下来就是一句语句中最重要的【命令】部分了。
————————————————————————————————————————————————————————————————————————————————————
四、命令
【命令】部分不存在于触发器中,当你写完了触发器的【类型】和【条件】时请打上一个 ; 作为结尾。然后写注释。
【命令】部分存在且必须存在于接收器和说明部分,如果有任何一句接收器或说明部分漏了【命令】部分,则语法错误跳出。
在【命令】部分开始前,使用 : 隔开条件部分。
【命令】部分有着自己的语法,命令的类型有上千余种,每种命令拥有不同的语法,我们可以在AI~VR的各个章节中,找到各种命令的语法。
由于实在太多了,所以我们不需要全部记得,我们只需要记得一些常用的(写过一次ERM脚本就知道哪些是常用的了)。我们可以翻阅一遍ERM帮助,记得大概控制哪些东西的命令在ERM帮助中的哪个章节中介绍,以便我们用于翻阅查找。
五、收尾
不管是触发器还是接受器或者说明部分,请在结尾打上一个 ; 作为收尾,然后自由地写下你的注释。
为了方便查看、修改和调试,尽量每个语句换行写,每个函数之间隔行写,每个大部分之间用连续的一条*作为分割线(ERM作者惯例分割线),每个功能部分前注释一下接下来的部分的用处,脚本更换事件接续时在结尾加上continue to “事件名称”,整个程序结束后在结尾附上end。
当然,这些都不强制性,你有你自己的做法,如果你想你的脚本方便自己查看,或者让其他人能容易去看懂你的脚本,就尽量用这个国际通用的格式。
**********************************************************************************************************************************************************
注意:
注释部分:注释部分不属于ERM的一部分,语法基本完全自由。但注意以下几个方面:
1)注释部分夹在分号(;)的后面以及下一个!#/!?/!!的前面,不需要使用特定的符号标注。在一些脚本中,你会看到某些作者使用//、{}、【】或[]标明注释部分,其实这是一种个人习惯,没有强制要求。
2)因为ERM的注释部分不同于其他成熟语言,使用特定符号来标注(如C语言使用双斜杠//来标注),所以注释部分如果写得跟代码部分的格式一模一样,系统就会误认为它是ERM语句。
因此,注释部分严禁使用!#/!?/!! 、 ; 和 ^ 字符,避免使用 & 、< 、 > 和 =等字符。
3)有些作者不爱写注释,或只写必要或极少的注释,由于ERM语言的编写工具没有语法检测功能,在游戏运行进行BUG调试的时候,调试机制不太成熟,所以造成调试过程略麻烦,所以尽量使用注释以方便deBUG,注释使用技巧还是通过实践。
**********************************************************************************************************************************************************
ERM的基础讲解到此结束,如果你看懂了以上内容,基本上你可以着手编写属于你的ERM脚本了。
在进行接下来的更深入的讲解前,最好找个简单点的脚本参考一下,或者自己写一个hello world之类的小脚本。
你仅仅是想了解ERM的话,你可以到此为止了,如果想自己写脚本,并且让自己的效率得到提升的话,请继续读下去。
[ 本帖最后由 克招 于 2013-5-11 13:16 编辑 ]
第五节:ERM的执行机制
占楼[ 本帖最后由 克招 于 2013-5-9 15:18 编辑 ]
第六节:堆栈与数据
占楼[ 本帖最后由 克招 于 2013-5-9 15:19 编辑 ]
最后一节:引申、ERM的高级写法与相关注意事项
包括ERM写法、注意事项、变量冲突和申明、ERM进阶相关[ 本帖最后由 克招 于 2013-5-9 15:20 编辑 ] 可以插楼了吗?请问克老师学的是什么专业? 克老弟神人也!
复出搞革命了,好耶 一出来就搞大的了;lan; 最让很多ERM初学者感到一头雾水的应该就是变量问题了,坐等老师上课 支持克招开课.
克招出品,必属精品.
话说,既然是以C为对比,标题可以改为"有C编程经验的人"
由于编程软件多样,不一定就懂C的.
比如VB和C++的变量数值范围是不相同的.
克招出品,必属精品
坐等老师继续,完成后必是精品讲解得还是相当直观,有一点基础的应该很容易看懂
回复 13# 的帖子
有编程经验的大部分都学过C/C++语言吧,而且目前主流语言大部分的机制和C大同小异,phyon、JAVA、pascal等等,VB其实也能找到C的影子,我总不能把每种语言都类比一下吧? 这个好,感谢楼主;taoh; 看来Erm区的春天真的到来啦。;orz; 求楼主填坑。 基础讲解部分更新完毕 非常感谢克招的授课.让我们对ERM更深入了解了.其中有一句不是很了解,可能因为不太懂C.
"系统函数又称为触发器,与C语言不同的是:它们不能在主函数或系统函数中引用或跳转(也就是说,不能直接从主函数或其他系统函数中读取或运行某个系统函数)"
这里不是很明白.原则上,但凡函数都可以被调用的吧.
另外有2个地方笔误了.
9) &v1<1/v2>=1/v3=>1/v4<>0/v5=><0 当v1小于1,且v2大于等于1,且v3大于等于1,v4不等于0,v5不等于0都为真
10) &v1=<>0 该语句不运行
第9后面那句条件应为 v5<>0 吧.否则跟下面一句一样会报错的.
第10的这句,不是不运行,是直接报错的.比较符语法错误.
回复 19# 的帖子
在C语言里,比如说printf()是一个系统函数,我们可以在所有函数中调用它用于显示文本。在ERM里,!?OB是一个系统函数,那么你怎么从!?TM里调用到!?OB中的命令呢?显然不行。
其实ERM中是没有函数这个概念的,引入这个概念,方便初学者更好地理解ERM的触发器。
页:
[1]
2