8. 公用状态修改:移动、跳跃、受身及防御 | 您所在的位置:网站首页 › mugen人物包制作教程 › 8. 公用状态修改:移动、跳跃、受身及防御 |
终于真正的开始写AI了。从第8章开始到第13章,都是关于AI的编写。 从这章开始我们正式接触人物里面的代码。代码比较长可能比较难理解,建议一段一段的读,不要被长度给吓到,多壮观的建筑都 是一砖一瓦搭成的,无论多长的代码也是如此,把每段代码逐个击破合起来就是你想要的答案。 首先我们先从各个人物几乎都有的状态开始入手,也就是公用状态。 ========================================================================= 8.1公用状态与Stcommon简介 ========================================================================= --------------------------------------------------------------------------- 公用状态是一些各个人物几乎都有的状态,而这些状态的状态号基本上也是固定的。 例如站姿0,蹲姿10~12,行走20,起跳40,跑/前冲100,后撤105,防御120~155,倒地5110,起身5120等等。 Stcommon是一个存放公用状态的特殊st文件。 如果其他st文件没有重复定义Stcommon里面的状态,人物将会使用Stcommon里面定义的状态。 Mugen在data文件夹里面附有默认的common1.cns,如果人物没有自带Stcommon文件,那么人物会调用这个common1.cns。 因此,如果人物自带Stcommon文件,那么我们就直接对那个文件进行修改;如果人物没有自带Stcommon文件的话,就要在人物文件 夹内自己新建/复制一个Stcommon文件,并把Def文件内的Stcommon路径改成你文件的路径。 打开common1.cns后我们可以发现,其实Stcommon和一般的st文件没有太多不同。 里面很多Sctrl其实是不需要管的,如第5章所说,我们只需要看会被command影响的某些Sctrl。 一般来说这些涉及的内容包括:移动(行走、跑、前冲、后撤、跳跃)、受身及防御。 ========================================================================= 8.2行走 ========================================================================= --------------------------------------------------------------------------- 行走是格斗游戏最为常见的移动方式之一。 虽然移动速度比较慢,但是全程都是属于可控状态,可以随时应对各种情况,是最安全的移动方式。 默认的common1.cns中代码如下: ;--------------------------------------------------------------------------- ; Walk [Statedef 20] type = S physics = S sprpriority = 0 [State 20, 1] type = VelSet trigger1 = command = "holdfwd" x = const(velocity.walk.fwd.x) [State 20, 2] type = VelSet trigger1 = command = "holdback" x = const(velocity.walk.back.x) [State 20, 3] type = ChangeAnim triggerall = vel x > 0 trigger1 = Anim != 20 && Anim != 5 trigger2 = Anim = 5 && AnimTime = 0 value = 20 [State 20, 4] type = ChangeAnim triggerall = vel x < 0 trigger1 = Anim != 21 && Anim != 5 trigger2 = Anim = 5 && AnimTime = 0 value = 21 如代码所示,默认的行走分为向前走和向后走,这两个方向的行走用的是同一个状态号20。 这个状态会根据手操按键的不同(也就是前和后)而切换为对应的动画(动画20和21)和速度。 然而这样一个状态包含两个不同属性的动作的情况是相当不利于AI编写的。 因为这样不但要在Statedef -1/-3里面加入对应的trigger使其动起来,还要在stcommon文件里面区分前后两种情况, 分开两个地方去管理一类型动作。这么做相当麻烦还容易出错,所以我们要对其进行一定的调整。 比较常用的方法是新建一个(或者一些)状态来区分不同的动作,以后写状态切换的时候就会简单不少。 实例如下: ;下面两段放到[Statedef 20]附近 ;--------------------------------------------------------------------------- ; Walk B 向后走 [Statedef 19] type = S physics = S sprpriority = 0 [State 20, 2] type = VelSet trigger1 = 1 x = const(velocity.walk.back.x) [State 20, 4] type = ChangeAnim trigger1 = Anim != 21 && Anim != 5 trigger2 = Anim = 5 && AnimTime = 0 value = 21 ;--------------------------------------------------------------------------- ; Walk F 向前走 [Statedef 21] type = S physics = S sprpriority = 0 [State 20, 1] type = VelSet trigger1 = 1 x = const(velocity.walk.fwd.x) [State 20, 3] type = ChangeAnim trigger1 = Anim != 20 && Anim != 5 trigger2 = Anim = 5 && AnimTime = 0 value = 20 之后写上让人物行走的Changestate就可以了: ;下面一段放到[Statedef -1]或[Statedef -3]下面 ;--------------------------------------------------------------------------- [State -1, 向前走] type = ChangeState value = 21 Triggerall = Var(59) > 0;AI开启时 Triggerall = Roundstate = 2;在格斗阶段 Triggerall = Statetype != A;不在空中 Triggerall = Frontedgebodydist >= 10;不在画面边缘 Triggerall = Stateno != 20 && Stateno != 100;不是步行状态和跑步状态 Triggerall = ctrl;可控时 Trigger1 = P2bodydist X > 135 && Random < P2bodydist X * 2 - 250;Trigger1~4根据距离不同分段改变概率 Trigger2 = P2bodydist X > 100 && Random < P2bodydist X Trigger3 = P2bodydist X > 50 && Random < 100 Trigger4 = P2bodydist X > 0 && Random < 25 ;--------------------------------------------------------------------------- [State -1, 向后走] type = ChangeState value = 19 Triggerall = Var(59) > 0 Triggerall = Roundstate = 2 Triggerall = Statetype != A Triggerall = Backedgebodydist >= 10 Triggerall = !inguarddist;非防御距离,因为防御距离内按后会变成防御而不是向后走 Triggerall = ctrl Trigger1 = P2bodydist X < 75 && Random > 950 + P2bodydist X / 2;近距离低几率触发 之后人物就会根据不同的距离以不同的概率随机前后行走了。 ========================================================================= 8.3跑/前冲/后撤 ========================================================================= --------------------------------------------------------------------------- 跑/前冲也是格斗游戏非常常见的移动方式。 虽然移动速度比较快,但是跟行走对比起来相对不灵活,有一定的风险。 然而较快的速度令它在抢硬直、连段目押等需要快速移动的情况大展身手。 默认的common1.cns中代码如下: ;--------------------------------------------------------------------------- ; Run forward [Statedef 100] type = S physics = S anim = 100 sprpriority = 1 [State 100, 1] type = VelSet trigger1 = 1 x = const(velocity.run.fwd.x) [State 100, 2] ;Prevent run from canceling into walk type = AssertSpecial trigger1 = 1 flag = NoWalk [State 100, 3] ;Prevent from turning type = AssertSpecial trigger1 = 1 flag = NoAutoTurn [State 100, 4] type = ChangeState trigger1 = command != "holdfwd" value = 0 如代码所示,默认的是跑,是可控的匀速前进,并且可以随时停下。 然而,很多格斗游戏里面的跑并不是这么简单的,通常是由起步、匀速运动、停止之类的部分组成。 因为起步和停止都是有一定硬直的动作,如果在较短的距离开始跑,对方一旦使用攻击,你将没法停下来进行防御。 和行走对比起来,这类型的跑使用起来需要考虑更多问题。 虽然跑并没有行走那样有两个方向的烦恼,但是跑的启动和停止是分开控制的,并不是像行走那样不需要变为State 0的Sctrl。 所以除了启动我们还要改写Statedef 100下的停止条件: ;--------------------------------------------------------------------------- ; Run forward [Statedef 100] ... [State 100, 4] type = ChangeState trigger1 = Var(59) = 0;AI未开启时 trigger1 = command != "holdfwd";和原来一样接受手操指令 trigger2 = Var(59) > 0 trigger2 = EnemyNear,movetype != H || (EnemyNear,movetype = H && p2dist x < 0);对方不在受击状态或者对方在受击状态但跑过头了 trigger2 = p2bodydist x 0 Triggerall = Roundstate = 2 Triggerall = Statetype != A Triggerall = Frontedgebodydist >= 20;避免在画面边缘开始跑 Triggerall = ctrl Trigger1 = P2bodydist X > 200 && Random < P2bodydist X*5 - 720;Trigger1~4根据距离不同分段改变概率 Trigger2 = P2bodydist X > 135 && Random < P2bodydist X*2 - 120 Trigger3 = P2bodydist X > 90 && Random < P2bodydist X*1.75 - 90 Trigger4 = (P2bodydist X = [45,90]) && Random < P2bodydist X *1.2 - 45 --------------------------------------------------------------------------- 虽然Mugen内默认的状态100是跑,但一些游戏当中双击前方向键(66)并不一定是跑,还有可能是前冲(冲刺/Dash)。 前冲很有可能是长时间硬直的向前移动,若乱使用可以说破绽百出,使用时比起跑要更小心,否则就是给对方送硬直。 后撤默认使用的是状态105,是双击后方向键(44)所发出来的。 在Mugen中,后撤默认为空中状态,也就是启动的瞬间就在空中,所以可以利用这个特性躲避投技。 而在不少格斗游戏作品当中,后撤会有短暂无敌的性能,我们也可以利用这性能对对方攻击进行闪避。 因为默认并没有前冲,所以在此只给出后撤的例子: ;下面一段放到[Statedef -1]或[Statedef -3]下面 ;--------------------------------------------------------------------------- [State -1, 后撤] type = ChangeState value = 105 Triggerall = Var(59) > 0 Triggerall = Roundstate = 2 Triggerall = Statetype != A Triggerall = (Random < Var(59)*200) || (Var(59) > 3) Triggerall = ctrl Trigger1 = EnemyNear,Movetype = A && EnemyNear,Stateno >= 200;对方攻击状态 Trigger1 = facing != EnemyNear,facing;双方面对面 Trigger1 = EnemyNear,hitdefattr = SC,AT;对手攻击带投技判定 Trigger1 = P2bodydist X = [-20,100];一定距离内 ========================================================================= 8.4跳跃 ========================================================================= --------------------------------------------------------------------------- 跳跃同样是格斗游戏常见的移动方式。 因为跳跃能令攻击方式多样化(上下择和落地投等)而且有着相当高的回报,在手操里有着很重要的地位。 同时还可以躲避投技和远处飞行道具,并获得先机。 但是先不说mugen当中空防并不能防御下段攻击和一大堆无敌升龙,通常来说人物在空中只能按照一定的轨迹移动, 而且多数格斗游戏中人物在地面的性能比空中要好,跳跃也需要好好考虑位置和时机才能在AI战里面使用。 默认的common1.cns中代码如下: ;--------------------------------------------------------------------------- ; Jump Start [Statedef 40] type = S physics = S anim = 40 ctrl = 0 sprpriority = 1 facep2 = 1 [State 40, 1] type = VarSet trigger1 = Time = 0 sysvar(1) = 0 [State 40, 2] type = VarSet trigger1 = command = "holdfwd" sysvar(1) = 1 [State 40, 3] type = VarSet trigger1 = command = "holdback" sysvar(1) = -1 [State 40, 4] type = VelSet trigger1 = AnimTime = 0 x = ifelse(sysvar(1)=0, const(velocity.jump.neu.x), ifelse(sysvar(1)=1, const(velocity.jump.fwd.x), const(velocity.jump.back.x))) y = const(velocity.jump.y) [State 40, 5] type = VelSet trigger1 = AnimTime = 0 trigger1 = prevstateno = 100 ;RUN_FWD trigger1 = sysvar(1) = 1 x = const(velocity.runjump.fwd.x) [State 40, 6] type = ChangeState trigger1 = AnimTime = 0 value = 50 ctrl = 1 如代码所示,和行走类似,默认的跳跃分为前跳、后跳、原地跳和跑跳,用的是同一个状态号40。 也是通过按键不同区分开不同的方向和速度。 而在某些有大小跳的作品里面,跳跃的分类会更多。 所以我们会采用和行走一样的办法来处理。 实例如下: ;--------------------------------------------------------------------------- ;Statedef 39后跳 41前跳 42原地跳 [Statedef 39] type = S physics = S anim = 40 ctrl = 0 sprpriority = 1 [State 40, 1] type = VarSet trigger1 = 1 sysvar(1) = -1 [State 40, 4] type = VelSet trigger1 = AnimTime = 0 x = const(velocity.jump.back.x) y = const(velocity.jump.y) [State 40, 6] type = ChangeState trigger1 = AnimTime = 0 value = 50 ctrl = 1 ;--------------------------------------------------------------------------- [Statedef 41] type = S physics = S anim = 40 ctrl = 0 sprpriority = 1 [State 40, 1] type = VarSet trigger1 = 1 sysvar(1) = 1 [State 40, 4] type = VelSet trigger1 = AnimTime = 0 x = const(velocity.jump.fwd.x) y = const(velocity.jump.y) [State 40, 5] type = VelSet trigger1 = AnimTime = 0 trigger1 = prevstateno = 100 ;RUN_FWD trigger1 = sysvar(1) = 1 x = const(velocity.runjump.fwd.x) [State 40, 6] type = ChangeState trigger1 = AnimTime = 0 value = 50 ctrl = 1 ;--------------------------------------------------------------------------- [Statedef 42] type = S physics = S anim = 40 ctrl = 0 sprpriority = 1 [State 40, 1] type = VarSet trigger1 = 1 sysvar(1) = 0 [State 40, 4] type = VelSet trigger1 = AnimTime = 0 x = const(velocity.jump.neu.x) y = const(velocity.jump.y) [State 40, 6] type = ChangeState trigger1 = AnimTime = 0 value = 50 ctrl = 1 =========================================================================== 8.5受身 =========================================================================== --------------------------------------------------------------------------- 受身是指受击身体恢复,通常是空中或者低空受击硬直后,或者倒地时可以更快的恢复成为可控状态。 更快的恢复可控状态可以用于躲避对方的伪连(指中途可被受身回避的连段),或者更容易作出反击。 默认的common1.cns中代码如下(此处因为WinMugen版本写的比较简单容易理解,我用WinMugen版本的代码进行讲解): ;--------------------------------------------------------------------------- ; HITA_FALL (knocked up, falling) [Statedef 5050] type = A movetype= H physics = N ... [State 5050, 4] ;Recover near ground type = ChangeState triggerall = Vel Y > 0 triggerall = Pos Y >= -20 triggerall = alive triggerall = CanRecover trigger1 = Command = "recovery" value = 5200 ;HITFALL_RECOVER [State 5050, 5]; Recover in mid air type = ChangeState triggerall = Vel Y > -1 triggerall = alive triggerall = CanRecover trigger1 = Command = "recovery" value = 5210 ;HITFALL_AIRRECOVER ... --------------------------------------------------------------------------- ; HIT_FALLRECOVER (still falling) [Statedef 5200] type = A movetype= H physics = N [State 5200, 1] ;Change anim if done with transition type = ChangeAnim trigger1 = Anim = 5035 trigger1 = AnimTime = 0 value = 5050 [State 5200, 2] type = VelAdd trigger1 = 1 y = GetHitVar(yaccel) [State 5200, 3] type = SelfState trigger1 = Vel Y > 0 trigger1 = Pos Y >= 10 value = 5201 ;--------------------------------------------------------------------------- ; HIT_FALLRECOVER (on the ground) [Statedef 5201] type = A movetype= H physics = A anim = 5200 [State 5201, 1] ;Turn if not facing opponent type = Turn trigger1 = Time = 0 trigger1 = p2dist X < -5 [State 5201, 2] type = VelSet trigger1 = Time = 0 x = -.15 y = -3.5 [State 5201, 3] type = PosSet trigger1 = Time = 0 y = 0 [State 5201, 4] type = NotHitBy trigger1 = 1 value = SCA time = 1 [State 5201, 5] ;Blink white type = PalFX trigger1 = Time = 0 time = 3 add = 128,128,128 ;256,256,256 [State 5201, 6] type = GameMakeAnim trigger1 = Time = 1 value = 60 pos = 0, 0 under = 1 ;--------------------------------------------------------------------------- ; HIT_AIRFALLRECOVER [Statedef 5210] type = A movetype= I physics = N anim = 5210 ctrl = 0 [State 5210, 1] ;Blink white type = PalFX trigger1 = Time = 0 time = 3 add = 128,128,128 ;256,256,256 [State 5210, 1] type = PosFreeze trigger1 = Time = 0 value = 4 [State 5210, 2] ;Turn if not facing opponent type = Turn trigger1 = Time = 0 trigger1 = p2dist X < -20 [State 5210, 1] type = VelMul trigger1 = Time = 4 x = .8 y = .8 [State 5210, 1] type = VelAdd trigger1 = Time = 4 y = -4.5 [State 5210, 1] type = VelMul trigger1 = Time = 4 trigger1 = Vel Y > 0 y = .5 [State 5210, 1] type = VelAdd trigger1 = Time = 4 trigger1 = Vel Y > -3 y = -2 [State 5210, 1] type = VelAdd trigger1 = Time = 4 trigger1 = Vel Y > -2 y = -1 [State 5210, 2] ;Go up type = VelAdd trigger1 = Time = 4 trigger1 = Command = "holdup" y = -2 [State 5210, 2] ;Go down type = VelAdd trigger1 = Time = 4 trigger1 = Command = "holddown" y = 1.5 [State 5210, 2] ;Go fwd type = VelMul trigger1 = Time = 4 trigger1 = Command = "holdfwd" x = 1 [State 5210, 2] ;Go back type = VelAdd trigger1 = Time = 4 trigger1 = Command = "holdback" x = -1 [State 5210, 3] type = NotHitBy trigger1 = Time = 0 value = SCA time = 15 [State 5210, 4] type = CtrlSet trigger1 = Time = 20 value = 1 [State 5210, 5] type = VelAdd;Gravity trigger1 = Time >= 4 y = .35 [State 5210, 5] ;Land on ground type = ChangeState trigger1 = Vel Y > 0 trigger1 = Pos Y >= 0 value = 52 ;JUMP_LAND ctrl = 1 如代码所示,Mugen当中默认有两种受身,分别是高空受身和低空受身,分别是状态5210和5200。 其发动条件是在掉落状态5050当中处于可恢复状态时,速度和高度符合条件即可。 高空受身5210是受身后20f恢复可控,而低空受身5200是进入预备状态到达低空高度转变为状态5201。 值得一提的是,高空受身有15f的无敌,而低空受身在到达低空高度直到落地前的状态5201有全程无敌。 如果可以在对方伪连时合理利用高空受身这15f的无敌和低空受身状态5201的全程无敌躲开对方的攻击判定, 甚至之后在对方攻击硬直中反击命中对方的话,可以说是相当大的收益。 然而受身其实也是有一定的风险的。高空受身在15f无敌以后有5f的硬直,而低空受身不可控,不可切换成防御状态, 落地也有硬直,乱受身的话很容易被写了受身狩(也就是抓受身)抓住机会。 另外有些AI也会用低硬直的招式骗对方手受身,一旦对方上当形成受身狩就是一套额外的连段。 因为我们暂时还没有学出招记录等技术,没法特别精确地用受身躲避对方伪连, 所以要注意受身的几率不要太高,以免被受身狩循环致死。 实例如下: ;--------------------------------------------------------------------------- ; HITA_FALL (knocked up, falling) [Statedef 5050] type = A movetype= H physics = N ... [State 5050, 4] ;Recover near ground type = ChangeState triggerall = Vel Y > 0 triggerall = Pos Y >= -20 triggerall = alive triggerall = CanRecover trigger1 = Var(59) = 0;当手操时由手操命令控制 trigger1 = Command = "recovery" trigger2 = Var(59) > 0 trigger2 = EnemyNear,Movetype = H || EnemyNear,Statetype = L;对方倒地或者受击 value = 5200 ;HITFALL_RECOVER [State 5050, 5]; Recover in mid air type = ChangeState triggerall = Vel Y > -1 triggerall = alive triggerall = CanRecover trigger1 = Var(59) = 0;当手操时由手操命令控制 trigger1 = Command = "recovery" trigger2 = Var(59) > 0 trigger2 = EnemyNear,Movetype = H || EnemyNear,Statetype = L Trigger3 = Var(59) > 0 Trigger3 = EnemyNear,Movetype = A && EnemyNear,Stateno >= 200;对方攻击状态 Trigger3 = EnemyNear,AnimTime 0 y = 1.5 [State 5210, 2] ;Go fwd type = VelMul triggerall = Time = 4 trigger1 = Var(59) = 0 trigger1 = Command = "holdfwd" trigger2 = Var(59) > 0 x = 1 [State 5210, 2] ;Go back type = VelAdd triggerall = Time = 4 trigger1 = Var(59) = 0 trigger1 = Command = "holdback" x = -1 ========================================================================= 8.6防御 ========================================================================= --------------------------------------------------------------------------- 格斗有攻就有守,防御不必多说是格斗游戏里面很重要的一环。 防御对于攻击来说算相对简单,因为无论你怎么攻击,防御就三种:站防、蹲防、空防(有些游戏甚至没有空防)。 除了投技不可防御、破防技等特殊情况,攻击判定(打击技和飞行道具)有分为上中下三段,可以被对应的防御防住。 (PS:格斗游戏中,上段站、蹲防均可,中段不可蹲防,下段不可站防。但是Mugen当中,上段(guardflag = H)不可蹲防, 中段(guardflag = M = HL)站、蹲防均可,下段(guardflag = L)不可站防,另外guardflag = A决定是否能空防。 在本教程里统一用格斗游戏的说法,即中段不可蹲防,下段不可站防) 默认的common1.cns中代码如下: ;--------------------------------------------------------------------------- ; GUARD (start) [Statedef 120] type = U ;Leave state type unchanged physics = U ;Leave physics unchanged [State 120, 1] type = ChangeAnim trigger1 = Time = 0 value = 120 + (statetype = C) + (statetype = A)*2;这种写法用一段代码完成了三段代码的工作,值得学习 [State 120, 2] type = StateTypeSet trigger1 = Time = 0 && statetype = S physics = S [State 120, 3] type = StateTypeSet trigger1 = Time = 0 && statetype = C physics = C [State 120, 4] type = StateTypeSet trigger1 = Time = 0 && statetype = A physics = A [State 120, Hi to Lo] type = StateTypeSet trigger1 = statetype = S && command = "holddown" statetype = C physics = C [State 120, Lo to Hi] type = StateTypeSet trigger1 = statetype = C && command != "holddown" statetype = S physics = S [State 120, 5] type = ChangeState trigger1 = AnimTime = 0 value = 130 + (statetype = C) + (statetype = A)*2 [State 120, Stop Guarding] type = ChangeState trigger1 = command != "holdback" trigger2 = !inguarddist value = 140 ;--------------------------------------------------------------------------- ; STAND GUARD (guarding) [Statedef 130] type = S physics = S [State 130, 1] type = ChangeAnim trigger1 = Anim != 130 value = 130 [State 130, Hi to Lo] type = ChangeState trigger1 = command = "holddown" value = 131 [State 130, Stop Guarding] type = ChangeState trigger1 = command != "holdback" trigger2 = !inguarddist value = 140 ;--------------------------------------------------------------------------- ; CROUCH GUARD (guarding) [Statedef 131] type = C physics = C [State 131, 1] type = ChangeAnim trigger1 = Anim != 131 value = 131 [State 131, Lo to Hi] type = ChangeState trigger1 = command != "holddown" value = 130 [State 131, Stop Guarding] type = ChangeState trigger1 = command != "holdback" trigger2 = !inguarddist value = 140 ;--------------------------------------------------------------------------- ; AIR GUARD (guarding) [Statedef 132] type = A physics = N [State 132, 1] type = ChangeAnim trigger1 = Anim != 132 value = 132 [State 132, 2] type = VelAdd trigger1 = 1 y = Const(movement.yaccel) [State 132, 3] type = VarSet trigger1 = 1 sysvar(0) = (pos y >= 0) && (vel y > 0) [State 132, 4] type = VelSet trigger1 = sysvar(0) y = 0 [State 132, 5] type = PosSet trigger1 = sysvar(0) y = 0 [State 132, 6] type = ChangeState trigger1 = sysvar(0) trigger1 = command = "holdback" trigger1 = inguarddist value = 130 [State 132, 7] type = ChangeState trigger1 = sysvar(0) value = 52 [State 132, Stop Guarding] type = ChangeState trigger1 = command != "holdback" trigger2 = !inguarddist value = 140 ;--------------------------------------------------------------------------- ; GUARD (end) [Statedef 140] type = U ;Leave state type unchanged physics = U ;Leave physics unchanged ctrl = 1 [State 140, 1] type = ChangeAnim trigger1 = Time = 0 value = 140 + (statetype = C) + (statetype = A)*2 [State 140, 2] type = StateTypeSet trigger1 = Time = 0 && statetype = S physics = S [State 140, 3] type = StateTypeSet trigger1 = Time = 0 && statetype = C physics = C [State 140, 4] type = StateTypeSet trigger1 = Time = 0 && statetype = A physics = A [State 140, Hi to Lo] type = StateTypeSet trigger1 = statetype = S && command = "holddown" statetype = C physics = C [State 140, Lo to Hi] type = StateTypeSet trigger1 = statetype = C && command != "holddown" statetype = S physics = S ;[State 140, 5] ;Implemented within engine ;type = ChangeState ;trigger1 = AnimTime = 0 ;value = (statetype = C)*11 + (statetype = A)*51 ;--------------------------------------------------------------------------- ; SGUARDHIT (shaking) [Statedef 150] type = S movetype= H physics = N velset = 0,0 [State 150, 1] type = ChangeAnim trigger1 = 1 value = 150 [State 150, 2] type = ChangeState trigger1 = HitShakeOver value = 151 + 2*(command = "holddown") [State 150, Hi to Lo] type = StateTypeSet trigger1 = statetype = S && command = "holddown" statetype = C physics = C [State 150, Lo to Hi] type = StateTypeSet trigger1 = statetype = C && command != "holddown" statetype = S physics = S [State 150, 3] type = ForceFeedback trigger1 = time = 0 waveform = square time = 3 ;--------------------------------------------------------------------------- ; SGUARDHIT2 (knocked back) [Statedef 151] type = S movetype= H physics = S anim = 150 [State 151, 1] type = HitVelSet trigger1 = Time = 0 x = 1 [State 151, 2] type = VelSet trigger1 = Time = GetHitVar(slidetime) trigger2 = HitOver x = 0 [State 151, 3] type = CtrlSet trigger1 = Time = GetHitVar(ctrltime) value = 1 [State 151, Hi to Lo] type = StateTypeSet trigger1 = statetype = S && command = "holddown" statetype = C physics = C [State 151, Lo to Hi] type = StateTypeSet trigger1 = statetype = C && command != "holddown" statetype = S physics = S [State 151, 4] type = ChangeState trigger1 = HitOver value = 130 ctrl = 1 ;--------------------------------------------------------------------------- ; CGUARDHIT (shaking) [Statedef 152] type = C movetype= H physics = N velset = 0,0 [State 152, 1] type = ChangeAnim trigger1 = 1 value = 151 [State 152, 3] type = ChangeState trigger1 = HitShakeOver value = 151 + 2*(command = "holddown") [State 152, Hi to Lo] type = StateTypeSet trigger1 = statetype = S && command = "holddown" statetype = C physics = C [State 152, Lo to Hi] type = StateTypeSet trigger1 = statetype = C && command != "holddown" statetype = S physics = S [State 152, 4] type = ForceFeedback trigger1 = time = 0 waveform = square time = 4 ;--------------------------------------------------------------------------- ; CGUARDHIT2 (knocked back) [Statedef 153] type = C movetype= H physics = C anim = 151 [State 153, 1] type = HitVelSet trigger1 = Time = 0 x = 1 [State 153, 2] type = VelSet trigger1 = Time = GetHitVar(slidetime) trigger2 = HitOver x = 0 [State 153, 3] type = CtrlSet trigger1 = Time = GetHitVar(ctrltime) value = 1 [State 153, Hi to Lo] type = StateTypeSet trigger1 = statetype = S && command = "holddown" statetype = C physics = C [State 153, Lo to Hi] type = StateTypeSet trigger1 = statetype = C && command != "holddown" statetype = S physics = S [State 153, 4] type = ChangeState trigger1 = HitOver value = 131 ctrl = 1 ;--------------------------------------------------------------------------- ; AGUARDHIT (shaking) [Statedef 154] type = A movetype= H physics = N velset = 0,0 [State 154, 1] type = ChangeAnim trigger1 = 1 value = 152 [State 154, 2] type = ChangeState trigger1 = HitShakeOver value = 155 ;AGUARDHIT2 [State 154, 3] type = ForceFeedback trigger1 = time = 0 waveform = square time = 4 ;--------------------------------------------------------------------------- ; AGUARDHIT2 (knocked away) [Statedef 155] type = A movetype= H physics = N anim = 152 [State 155, 1] type = HitVelSet trigger1 = Time = 0 x = 1 y = 1 [State 155, 2] type = VelAdd trigger1 = 1 y = Const(movement.yaccel) [State 155, 3] type = CtrlSet trigger1 = Time = GetHitVar(ctrltime) value = 1 [State 155, 4] type = VarSet trigger1 = 1 sysvar(0) = (pos y >= 0) && (vel y > 0) [State 155, 5] type = VelSet trigger1 = sysvar(0) y = 0 [State 155, 6] type = PosSet trigger1 = sysvar(0) y = 0 [State 155, 6] type = ChangeState trigger1 = sysvar(0) trigger1 = command = "holdback" trigger1 = inguarddist value = 130 [State 155, 7] type = ChangeState trigger1 = sysvar(0) value = 52 防御状态默认占据了状态号[120,155],中间根据防御不同阶段又细分了好几个状态。 120是防御准备状态,也就是按下后方向键人物进入的动作,只用于转变为防御未受击状态; [130,132]是防御未受击状态,也就是做好了防御动作但是尚未被攻击的阶段,[130,132]分别对应站、蹲、空; 140是防御结束状态,也就是防御准备状态、未受击状态或者受击状态结束时松开后方向键进入的状态,只用于离开防御状态; [150,155]是防御受击状态,防御准备状态或防御未受击状态受到攻击时进入的状态,[150,151]、[152,153]、[154,155]分别对应 站、蹲、空,偶数为受击震动,奇数为受击滑动。在受击硬直后若还按着后方向键进入防御未受击状态,否则进入防御结束状态。 防御状态的切换是已经写好了的,我们需要管的地方只有手操指令的切换。 而这些切换无非控制的其实是站/蹲防的切换(空防你只有防与不放不用想太多)和停止防御状态两种。 通常来说,站姿攻击多为上段和中段,蹲姿攻击多为上段和下段,空中攻击多为上段和中段。 所以在地面上对站姿攻击和空中攻击我们用站防应对,对蹲姿攻击我们用蹲防应对,对方停止攻击的时候我们停止防御就行了。 实例如下: ;--------------------------------------------------------------------------- ; GUARD (start) [Statedef 120] type = U ;Leave state type unchanged physics = U ;Leave physics unchanged ... [State 120, Hi to Lo] type = StateTypeSet triggerall = statetype = S;当我方是站姿 trigger1 = !Var(59) && command = "holddown" trigger2 = Var(59) && EnemyNear,Statetype = C;而对方是蹲姿的时候 statetype = C;变为蹲姿 physics = C [State 120, Lo to Hi] type = StateTypeSet triggerall = statetype = C;当我方是蹲姿 trigger1 = !Var(59) && command != "holddown" trigger2 = Var(59) && EnemyNear,Statetype = S;而对方是站姿的时候 statetype = S;变为站姿 physics = S [State 120, 5] type = ChangeState trigger1 = AnimTime = 0 value = 130 + (statetype = C) + (statetype = A)*2 [State 120, Stop Guarding] type = ChangeState trigger1 = !Var(59) && command != "holdback" trigger2 = !inguarddist value = 140 ;--------------------------------------------------------------------------- ; STAND GUARD (guarding) [Statedef 130] type = S physics = S [State 130, 1] type = ChangeAnim trigger1 = Anim != 130 value = 130 [State 130, Hi to Lo] type = ChangeState trigger1 = !Var(59) && command = "holddown" trigger2 = Var(59) && EnemyNear,Statetype = C value = 131 [State 130, Stop Guarding] type = ChangeState trigger1 = !Var(59) && command != "holdback" trigger2 = !inguarddist value = 140 ;因为下面的站/蹲姿切换都是类似的代码,以上面的当做参考足矣,下略 ...... 之后还有启动防御的Changestate: ;下面一段放到[Statedef -1]或[Statedef -3]下面 ;--------------------------------------------------------------------------- [State -1, 防御] type = ChangeState value = 120 Triggerall = Var(59) > 0 Triggerall = Roundstate = 2 Triggerall = Statetype != A Triggerall = ctrl Triggerall = !(EnemyNear(Var(54)),hitdefattr = sca,at);对方不是投技判定 Trigger1 = Inguarddist ;在防御范围内 完成这段的修改,你也限制了人物攻击的话,你的人物已经算是铁壁了。 什么?你说铁壁这么简单?没错,只要不主动攻击卖硬直,铁壁就是这么简单。 =========================================================================== 8.7限制隐性手操指令切换状态 =========================================================================== --------------------------------------------------------------------------- 到这里我们已经把带有command的Sctrl修改完了,但是这章内容尚未完结。 还记得笔者在第5章提及的内容吗:除了带有command的Sctrl以外,Mugen里面还存在内置的隐性的手操指令切换状态, 例如下蹲(Stateno 10)、行走(Stateno 20)和跳跃(Stateno 40)等。 我们虽然修改了这几个动作里面的带command的内容,但是我们还没有对它们的触发进行限制。 只要人物处于可控状态,只要内置的隐性的用手操指令刚好被触发,这几个动作依然会不受控制的动起来。 所以除了上面的内容,我们还要限制隐性手操指令切换状态。 --------------------------------------------------------------------------- 因为人物要处于可控状态才会切换状态为下蹲、行走和跳跃等状态,所以首先我们可以把一些可控的状态改成不可控。 在Statedef开始的地方,如果没有标注说是否可控,将会继承上一个状态的可控与否。 例如从可控的站姿切换到行走,行走并没有标注是否可控,行走也将变成可控。 分析之前的代码可知,Mugen默认的行走和跑都可以从可控状态进入,所以这两个状态都可能会被隐性的手操指令切换状态。 于是我们可以进行修改,实例如下: ;因为我们已经采用了新的行走状态State 19/21,所以我们可以不用修改State 20 ;--------------------------------------------------------------------------- ; Walk B 向后走 [Statedef 19] type = S physics = S sprpriority = 0 ctrl = 0;不可控 ... ;--------------------------------------------------------------------------- ; Walk F 向前走 [Statedef 21] type = S physics = S sprpriority = 0 ctrl = 0;不可控 ;而State 100,我们可以像行走那样添加新的状态,设置为不可控,或者是像下面这么修改 ;--------------------------------------------------------------------------- ; Run forward [Statedef 100] ... [State 100, AI Ctrlset];添加不可控设置 type = Ctrlset trigger1 = Var(59) > 0;当AI开关打开的时候 value = 0;不可控 通过这样的修改,在行走和跑步的时候就不会被手操指令影响了,但缺点就是不可控状态可能会混淆对方AI, 我们在写AI的时候也要注意这种写法可能对我们带来的不良影响。 另外,由于原来可控的状态现在变成了不可控,所以其他Sctrl里原来triggerX = ctrl的条件不作相应调整的话人物就变得不会动 了,因此我们还需要调整这些trigger,令他们可以在这些原来是可控的状态里跟原来一样切换状态。例如: ;Stand Light Punch [State -1, Stand Light Punch] type = ChangeState value = 200 trigger1 = Var(59) > 0 trigger1 = statetype = S trigger1 = ctrl 改为 ;Stand Light Punch [State -1, Stand Light Punch] type = ChangeState value = 200 trigger1 = Var(59) > 0 trigger1 = statetype = S trigger1 = ctrl || (Stateno = [19,21]) || Stateno = 100;这样就算不可控,行走和跑时都可以切换状态了 --------------------------------------------------------------------------- 然而修改了行走和跑步依然还是不够的,站姿等可控的情况下依然会乱动。 所以为了对应大多数的可控情况,我们还需要一段泛用的限制代码: ;下面一段放到[Statedef -1]或[Statedef -3]下面 ;--------------------------------------------------------------------------- [State -1, 地面动作限制] type = ChangeState value = 0;强行拉回站姿 Triggerall = Var(59) > 0 Triggerall = Statetype != A Triggerall = Roundstate = 2 Trigger1 = Stateno = 10;下蹲 Trigger2 = Stateno = 20;行走 Trigger3 = Stateno = 40;跳 ctrl = 1 虽然有部分魔改的成分(笑),但是这种是比较常用而且实在的限制乱动的写法了。 --------------------------------------------------------------------------- 至此,公用状态算是基本完成了。 随着以后不同目的的章节的探讨,我们还会继续给这些公用状态添加一些trigger,令它们在适当的时候触发。 不过那是之后的事了,本章只讲这么多。 =========================================================================== 课后作业: 1. 格斗游戏中,什么段攻击可以击破蹲防?什么段攻击可以击破站防? 2. 为什么说章节8.7的这段代码有部分魔改的成分? [State -1, 地面动作限制] type = ChangeState value = 0 Triggerall = Var(59) > 0 Triggerall = Statetype != A Triggerall = Roundstate = 2 Trigger1 = Stateno = 10 Trigger2 = Stateno = 20 Trigger3 = Stateno = 40 ctrl = 1 |
CopyRight 2018-2019 实验室设备网 版权所有 |