M.U.G.E.N的主程序官方版本只有dos、win、1.0、1.1beta,其余版本皆为民间改造或编造

基本的trigger指令

首先我们来了解一点基本的trigger指令

 

statetype 或p2statetype= S or C or A or L

statetype是自己的状态,p2statetype是对手的状态。p2就表示是对手的,下面都是这样

S是站立,C是蹲下,A是空中,L是倒地

(也就是stand,crouch,air,lie的首字母)

 

Movetype或p2movetype = I or A or H

这是动作状态,I是通常,A是攻击,H是受击(被打或防御都算受击)

 

stateno 或p2stateno = 数字

state号和对手的state号。State号就可以看作是动作的代号。

 

ctrl = 0 or 1;

判断是不是处于可控制状态,下面的文章里有详细说明

 

canrecover = 0 or 1 ;

判断是不是可以受身、0=受身不可、1=受身可能

 

P2dist x或p2bodydist X 与对手的水平方向距离

P2dist y 或p2bodydist Y 与对手的垂直方向距离,这2个下面有详细说明

Backedgebodydist 与背后版边的距离

Frontedgebodydist 与面前版边的距离

 

random 随机数,下面有说

 

movecontact攻击接触到对手(打到或被防)

movehit 攻击成功击中对手

moveguarded攻击被对手防住

 

life或p2life 自己或对手的血量

power 能量值。

 

enemy, 以敌人为对象,读取敌人的数值。双人战时指定较远的那个敌人

enemynear, 和上面差不多,只不过是双人战时读取离自己较近的敌人。

比如Trigger1 = enemy,power < 1000,就是当对手的能量值小于1000时

 

Stateno的查找

Ai开关写好后,我们就正式开始写ai。

但要写ai,我们先要知道什么招对应的是什么state号。State号就是各种招式和动作的代号。

那如何找出招式的stateno呢?有2种方法

 

注:下面有时会用数字表示方向,至于什么数字表示啥方向,看你的小键盘就知道了,比如624b就是前下后b

 

1 看cmd

Cmd文件本身就是定义“按什么键出什么招”的文件。如果你对人物的出招比较熟,可以看cmd查找相应的stateno

举个例子:

比如要查找下前a这招的stateno,我们先打开cmd。

[Command]里的招式定义部分应该会读了吧,不懂的回去复习episode 1。

 

很明显,下前a就是这个(df是过渡)

[Command]

Name    = "236a"

Command = ~D, DF, F, a

Time = 14

 

然后我们看到这个招被定义了一个名字叫“236a”,然后我们搜索这个236a

可以找到下面那段

[State -1]

Type = ChangeState ;这个表示切换状态,即做动作,前面说过了

Value = 1300    ;这就是state号,不过value也有可能写在下面

TriggerAll = Var(59) = 0

TriggerAll = Command = "236a"

TriggerAll = StateType != A

Trigger1 = Ctrl

 

其他先不用管,我们要知道的就是下前a这招的stateno就是1300

 

2 进入debug模式

打开winmugen\data目录里的mugen.cfg

查找字符串“debug”

找到这段

[Debug]

 ;Set to 0 to disable starting in debug mode by default.

Debug =0

把Debug = 0,改为Debug = 1即可。

然后进入mugen,进入训练模式,用1p选你所要修改ai的角色,会发现左下角出现了不少字。


注意到那个“STATE NO:”了么?这时你只要出招,这招的stateno就会自动出现。

 

common1.cns

有时我们会发现,有些基本动作的state在cmd里没有定义指令。比如防御,跳跃啥的。

因为这是定义在common的cns里的。哪个是定义common的cns呢?要看def

 

def里一般有这行

stcommon = common1.cns

但这个common1.cns,可能在人物目录里找不到

 

如果我们在这个人物的文件夹底下没找到这个common1.cns,那这个文件就是指winmugen\data底下的common1.cns

这是一个系统默认的通用动作定义文件。定义的是一般人物都有的防御,跳跃,前进等动作。具体怎么定义这里不深究,我们只要知道common1.cns定义的一些动作的state号就行。

 

写几个比较重要的

0 站立  

20 步行  

40 跳跃启动(在地面) 仅在人物离地时

100 向前跑/急冲  

105 急退  

120 防御-启动

130 防御-站-摆势  

131 防御-蹲-摆势  

132 防御-空-摆势  (此时人物做出防御动作,但并未受击)

140 防御-站-结束

141 防御-蹲-结束

142 防御-空-结束

150 防御-站-状态

151 防御-蹲-状态

152 防御-空-状态

-------受创--------(5000-5300)

 

5110 躺倒  

5120 起身  

5121 opt 起身 (有些人物有2种起身方式)  

5201 恢复近地 (地面受身) 

5210 恢复近空 (空中受身)

5300 眩晕  

 

简易ai的制作以及ai的基本立回


个人推荐写ai时,不怕麻烦的话,立回,连段,压制,起攻,插动等等最好分开写,且加上注释,这样的话比较清晰。

 

首先介绍一下立回的含义:就是敌我双方都能自由行动的状态,没有一方被压制,被连段,或处于倒地等各种行动不能的状态。

 

在ai上怎么体现呢?我们先来看一个立回ai的代码

 

[State -3,立x]

type = ChangeState

value = 200

triggerall = Var(59)= 1 && roundstate  = 2 ;ai开关开启且对局进行中

triggerall = Statetype != A  && p2statetype !=L;不在空中且对手不是倒地

triggerall = p2movetype != H ;对手不是受击状态,表明不是在连段中

triggerall = ctrl ;可以控制,即可以自由行动

trigger1 = P2dist X = [0,40] ;在与对手中心距离在0到40像素的情况下

trigger1 = random < 300 ;随机数小于300(给出招加一个概率)

 

这就是立x的立回ai

立回的重要特征是ctrl(可控制,即可以自由行动)以及p2movetype !=H(对手不受击)

P2statetype !=L不加也可以,因为一般对手倒地时,系统是判断p2movetype=H的

 

修改一个ai的时候,经常会看到原ai作者把连段和立回的一起写。

像这样:

[State -1, 236B]

type = ChangeState

triggerall = var(59) > 0 && RoundState = 2 && !ishelper && var(41)%10 = 0

triggerall = StateType = S

triggerall = p2bodydist X = [80,150]

trigger1 = Ctrl && random <=300

trigger2 = stateno = 230 && movecontact

value = 1210

 

其中trigger1是立回,trigger2是连段。

但是由于trigger1里面只有ctrl,没有p2movetype !=H,所以这个236B可能会影响某些目押连段。

 

关于ctrl状态

Ctrl即可以自由行动的状态,那什么情况下ctrl = 0呢

1 出招的时候

2 受击的时候(无论防御,还是直接被打)

3 倒地的时候以及其他不能控制的时候

 

写立回时的ai不加ctrl会产生很严重的后果。

如果是攻击动作,就拿上面的立x来说,写ai时不加ctrl的话,那只要符合条件(比如上文里距离在0到40之间),那在这种情况下,会不断的出立x,而且会在立x动作没有完成甚至还没产生判定的情况下,再强行重头做立x的动作。你就会发现ai会在这个距离内鬼畜般的重复做立x的动作,而且很可能打不中人。(因为很可能判定还没出来又重新出立x了),这时游戏可能会卡。

即使被对手在这时候命中,也不会做被打中的动作,还是强行出立x。

如果是防御动作没加ctrl,那在出任何招的过程中,都能无视硬直强行收招防御,甚至被打了,都能强行转入防御,一个bug人物就此诞生。

 

所以写立回ai的时候,一定要加ctrl。但连段“取消”的时候,不能加ctrl(关于连段,下个教程会说明)

 

关于p2dist和p2bodydist

P2dist表示p1与p2两者中心之间的相对距离,p2bodydist表示2者人物框之间的相对距离,一般来说差别不大。除非某些特殊情况下,一般2者都可以随便用。

一般情况下推荐用p2bodydist。某些特殊情况才用p2dist

P2dist有p2dist x和p2dist y之分,p2bodydist也一样。

P2dist x表示x方向,即水平方向的距离,y则表示垂直方向的距离

P2dist x的数值大于0 表示p2在p1前面,小于0就是在后面

P2dist y的数值大于0表示p2在p1下面,小于0表示在上面

 

另外自己和对手相对于地面的绝对高度可以用pos Y和enemy, pos Y表示,enemy也可以换成enemynear,表示离自己最近的敌人,在双人赛里有用。

用enemy和enemynear不只可以读取对手的位置,可以读取对手的各种参数,在ai中可以用来判断对手状态,很有用。

 

以上的各种参数具体请参阅trigger表

 

关于random(随机数)

系统每一F都会产生一个0到999的随机数,这就是random,每F都在变化

random是控制概率用的,random<300,就是每F有30%的概率。Random>700也是30%概率。Random<=1000就是100%的概率,random=500就只有千分之一的概率。(注意不是random<=500)

比如那个立x的立回ai就是当对手在0,40之间,每F都有30%的概率出立x。

剩下70%的可能就是做其他动作,包括发呆

 

用debug模式测量距离

所谓简易ai,就是知道且只知道在什么距离下出什么招的ai。

所以我们要做简易ai,就要知道招式的有效距离为多少。按距离出招也是立回的最主要模式

那怎么才能测出招式的距离呢?这时我们就继续使用万能的debug模式。

 

在state -2或者state -3中加入DisplaytoClipboard的type

当然只是显示距离的话,写在哪里都可以,但要显示其他var或fvar等变量的话

一般ai是写在state -3里就写在state -3的最下面。

没state -3就写在-2里面,不过要写在所要显示的var的定义指令下面

总之现在只是显示距离的话,不用管这么多。没有的话就随便写,有现成的type = DisplaytoClipboard的指令就在这上面直接改就好了

 

只是要显示距离的话,不必那么复杂

直接复制进去下面的指令就够了(其实下面推荐用p2bodydist x和p2bodydist y,但图是显示p2dist的,懒得再截一幅图了)

[State -2, Display] ;或者state -3

type = DisplaytoClipboard

trigger1 = 1

text = "p2dist x : %d | p2dist y: %| eposY : %d| frontedgedist: %d"

params = ceil(p2dist x) , ceil(p2dist y) , ceil(enemy,pos Y), ceil(frontedgedist)

 

text是屏幕上显示的文字,%d则与下面的params一一对应。ceil是取整,因为%d对应的是整型变量(整数)。

而%f可以对应浮点型变量(小数)。这里用不着。

Frontedgedist表示自己离前面版边的距离,在版边的行动中有作用

 

然后以1p进入mugen的训练模式,就可以看到左下方的信息中最后1条就是刚才添加的信息。


主要看p2dist x。在不同距离出招,看能否打中,记下能打中的p2dist x的值的范围。

至于p2dist y,由于人物跳起时高度不断变化,很难测,要求不高的话大概记得个范围就可以了。

地面技的对空范围也可以用enemy,pos Y表示

一人站在左版边,一人站在右版边的话

双方p2dist x 290-300,p2bodydist x大概为260。

而屏幕上下的距离大概150左右(可能有误,自己再测测吧)。

 

然后就按照上面的例子,写每一招的立回ai就可以了

[State -3,立x]

type = ChangeState

value = 200

triggerall = Var(59)= 1 && roundstate  = 2

triggerall = Statetype != A

triggerall = p2movetype != H

triggerall = ctrl

trigger1 = P2dist X = [0,40]

trigger1 = p2dist y = [-30,30]

trigger1 = random < 300

 

[State -3,立y]

type = ChangeState

value = 201

triggerall = Var(59)= 1 && roundstate  = 2

triggerall = Statetype != A

triggerall = p2movetype != H

triggerall = ctrl

trigger1 = P2dist X = [0,60]

trigger1 = p2statetype= S || p2statetype = C ;如果某一招基本只能打到在地面的人物的,就可以这样写,不用管p2dist y了。

trigger1 = random < 350

依次类推。

 

注意

Trigger1 = p2dist x =[0,40] && random < 300

这样系统是不认的,会出错,必须改成这样

Trigger1 = (p2dist x = [0,40]) && random < 300

 

小技巧:在debug模式里测招式的发生速度

Debug状态下,看那个time

一般招式击中对手时,会暂停一下。

趁这个瞬间看time:xx/yy,左边的xx就是这招的发生速度

还有个办法就是用fight factory或mcm看air文件了。这个比较麻烦。

 

投技的距离判断

一般“前+强攻击”这种形式发生的投技,cmd里一般在人用指令已经规定好距离的。(因为不在投技范围内按前+强攻击,就会变成强攻击)

找到这个距离写在ai里即可

有些投技是用“ab”这种方式按的,或者是指令投,只好自己在debug模式里试试距离了

注意写投技的立回ai时通常要加一句

Triggerall = p2movetype != H

因为决大多数投技,投不了在防御硬直中的对手。

 

Ai里立回招式的书写顺序

Ai里立回招式的书写顺序也有讲究。因为系统读ai的时候,每一F都是从上往下读的。这样会造成一些问题。

 

比如

[State -3,立x]

type = ChangeState

value = 200

triggerall = Var(59)= 1 && roundstate  = 2

triggerall = Statetype != A

triggerall = p2movetype != H

triggerall = ctrl

trigger1 = P2dist X = [0,40]

trigger1 = p2dist y = [-30,30]

trigger1 = random < 300

 

[State -3,立a]

type = ChangeState

value = 230

triggerall = Var(59)= 1 && roundstate  = 2

triggerall = Statetype != A

triggerall = p2movetype != H

triggerall = ctrl

trigger1 = P2dist X = [0,30]

trigger1 = p2dist y = [-30,30]

trigger1 = random < 300

 

比如这样写的话。你会发现ai在水平距离0,40的范围里只会出立x,不会出立a。

因为当满足立a的条件时,必定满足立x,而ai是自上而下读取的,所以条件满足时必定先执行立x。

如果我们把立a写在立x前面呢,那水平距离在0,30范围内会出立a,而水平距离在30,40之间时会出立x

但如果我们把立a的random<300的条件,改成random = [300,600],那在0,30的距离内,就既可能出立a也可能出立x了。

 注意:此处原文描述存在错误。random应该是每次调用都会返回不一样的结果,而并非文中提及的每帧为一个结果,所以实际上立a依然可以执行(例如立x的随机数为400,立a的随机数为100)。若希望做到文中描述的效果,需要把random的结果赋值给变量来实现。


如果要做ai等级的话,通常就是用random和控制ai等级的变量来调整出招几率

比如用var(59)作为ai等级,那可以给某招加上

Triggerall = random <= 50*var(59)

这样var(59)越高,出招几率就越高了。

 

所以我们写立回ai的时候

原则上把使用条件苛刻的招写在前面(如超杀等)

凹技写在防御前面

主力技可以适当上调

其他普通技啥的写在后面

 

另外如果控制好距离差距的话,近距离用轻拳,轻脚或投,中距离用中,重拳脚,远距离才用飞行道具。这样对于几个攻击距离不太相近的招而言,顺序也不太重要。

 

也不一定要死套这个原则,有时具体情况要具体分析,可以看一个现成的ai。最主要的是要明白这个招是干什么用的,这样才能合理确定这招应该写在ai的哪个位置。


你,确定要这么做吗?
正在处理中...