2016.03.3123:08

AI 辅助器 记录对手滞空时间

Episode 7 记录对手滞空时间

今天的内容可能会略复杂,嗯,所以没看懂的也不太要紧,会用就行了

于是,不谈其他的闲话了,直接进入正题,需要用到的有yaccel计算器,TAG计算器,这两个前面都有讲过这里就不谈了

那么给出辅助器,(不知道的看前面,fvar(20)是记录yaccel的变量,var(58)为TAG变量)

;--------------------------------------------------------------------
; 相手の滞空時間、超絶やっつけ 对手滞空时间
;--------------------------------------------------------------------
[State -3, VarSet]
Type = VarSet
Trigger1 = 1
fvar(1) = 0
IgnoreHitPause = 1 ;还原

[State -3, VarSet]
Type = VarSet
Trigger1 = EnemyNear(var(58)),StateType = A && fvar(20) != 0
Trigger1 = ((floor(EnemyNear(var(58)),Vel Y))**2-2*(fvar(20))*(floor(EnemyNear(var(58)),Pos Y))) >= 0
fvar(1) = floor(((-EnemyNear(var(58)),Vel Y)+(((floor(EnemyNear(var(58)),Vel Y))**2-2*(fvar(20))*(floor(EnemyNear(var(58)),Pos Y)))**0.5))/(fvar(20)))+1
IgnoreHitPause = 1 ;记录

条件略复杂,我们假设一下元,计算的时候把floor忽略

令EnemyNear(var(58)),Vel Y = v, fvar(20) = a, EnemyNear(var(58)),Pos Y = h

trigger1等价代换一下,v**2(y方向v的平方) - 2ah >= 0,也就是v**2 >= 2ah

我们知道,h = 1/2*a*t**2 ,v = at 两式子合并,消去t,得到 v**2 = 2ah,而v**2 = 2ah只是临界条件,v**2 - 2ah >= 0就是说在临界条件之上,这是第一个公式的来源

我们要得到的数据是时间,令fvar(1) = t,那么把公式代换一下,同样的,忽略掉floor,((xxx**0.5)代表对xxx开平方)

((-v)+(v**2-2ah)**0.5)/a + 1 = t, (t-1)*a = -v+(v**2-2ah)**0.5, 令(t-1) = T

aT+v = (v**2-2ah)**0.5, v**2 + 2avT + (a**2)*(T**2) = v**2 - 2ah

舍去两边v**2,舍掉一个a, 2vT + 2ah + aT**2 = 0,这是最终结果

证明结论,令h = 0,也就是说对手在地面被击飞,刚刚pausetime结束的时候,此时pos y = 0

2vT = -aT**2, 2v = -a*T , v = -(1/2)*a*T = -(1/2)*a*(t+1),+1为起始值

那么来图证明一下结论吧,这招的打击速度为 -2,-10,yacce = 0.5
LR~(S)8FQ5P4FAD[)4_$IUX

可以看到下面的打击时间,因为是1起始,所以是40+1 = 41,所以刚才的公式成立
GPDVR802S`JG2VTYET2{2T3

对手在此过程中,如果不受外力,那么对手做匀加速曲线运动,a一定,y方向v随着T变化,x方向的v不变,那么对手做的是抛物线运动,可以看到下图,对手的滞空时间在减少A3EET3X9`{1_NC~HI9SW{P

好的,对手现在着地了,我们也能看到fvar(1)的值变为0了,说明对手不再浮空
_7(H$V~(9Q[68ICA}2NR927

此变量因为有floor的限制,所以会有一定的小误差,但不会超过4,那么,记录对手这个数据有什么用呢?这个变量,在对手Pausetime未消失的时候,虽然有ignorehitpause,但对手没有vel和yaccel,那么我们只能记录下对手pausetime以外的滞空时间

这个变量,给弹幕起手的角色比较好用,能计算对手滞空还有多少时间,来确认进行接下来的连招,如果是比较朴素的角色,还是建议不要用这个了,还省了你一个变量,但是给弹幕起手角色的再合适不过了,当然,如果有援护招式的话,也是很合适的,来判断对手还剩下多少滞空时间进行出招判断

好了,今天的课就先到这,如果您有什么问题的话可以私信我(只要是我会的,不管什么问题),我会第一时间给您答复,或者把问题发到我的邮箱

mizore@kotori.moe, 谢谢大家!
2016.03.1712:32

AI HELPER-3 AI HELPER记忆器的制作

Episode 3 AI HELPER记忆器的制作

记忆器的话,写法是可以通用的,因为都是记招,因为HELPER能记下来的也不是万能的,所以也要配合AI里面的条件使用

举个例子,先说基本部分

;----------------------------------------------------------------------
[Statedef 36000]
anim = 9999
ctrl = 0

[State 36000, AssertSpecial]
Type = AssertSpecial
Trigger1 = 1
flag = invisible

[State 36000, BindToRoot]
type = BindToRoot
trigger1 = 1
facing = 1
pos = 0,0

[State 36000, NotHitBy]
Type = NotHitBy
Trigger1 = 1
Value = SCA

[State 36000, PlayerPush]
type = PlayerPush
trigger1 = 1
value = 0

[State 36000, SelfState]
Type = SelfState
Trigger1 = !IsHelper
Value = 0

[State 36000, AssertSpecial]
type = AssertSpecial
Trigger1 = 1
flag = NoShadow
flag2 = invisible

以上是基本部分,anim = 9999是一个空动画,前面提到过,不再讲

比如我现在记录下对手出招状态和判定触发时间,我可以这么写

;----------------------------------------------------------------------
[State 36000, VarSet]
Type = VarSet
TriggerAll = EnemyNear(Root,var(58)),Ctrl = 0 ;对手不可控
TriggerAll = EnemyNear(Root,var(58)),MoveType = A ;对手攻击状态
TriggerAll = EnemyNear(Root,var(58)),HitDefAttr = SCA,NA,SA ;一般记下来普攻和必杀技攻击就可以,虽然说下面的状态有限制
TriggerAll = EnemyNear(Root,var(58)),StateNo = [200,499] ;200-499一般是地面拳脚,概率比较大
TriggerAll = Root,StateNo = 150 || Root,StateNo = 152 ;防御硬直中触发
TriggerAll = Root,Time = 1 ;硬直中第一帧记下来,防出错
Trigger1 = var(1) = 0 ;未记录情况
V = 1
Value = (EnemyNear(Root,var(58)),StateNo) ;记下对面状态号
IgnoreHitPause = 1

[State 36000, VarSet]
Type = VarSet
Trigger1 = var(1)!= 0 ;对面状态号有
Trigger1 = var(2) = 0 ;时间还未记下来
V = 2
Value = (EnemyNear(Root,var(58)),Time) ;记录下敌方经过时间
IgnoreHitPause = 1

一个招记下来了,接下来记第二个

[State 36000, VarSet]
Type = VarSet
TriggerAll = var(1) > 0 ;有第一招的记录
TriggerAll = EnemyNear(Root,var(58)),Ctrl = 0
TriggerAll = EnemyNear(Root,var(58)),MoveType = A
TriggerAll = EnemyNear(Root,var(58)),HitDefAttr = SCA,NA,SA
TriggerAll = EnemyNear(Root,var(58)),StateNo = [200,499]
TriggerAll = EnemyNear(Root,var(58)),StateNo != var(1) ;不能和第一个冲突
TriggerAll = Root,StateNo = 150 || Root,StateNo = 152
TriggerAll = Root,Time = 1
Trigger1 = var(3) = 0
V = 3
Value = (EnemyNear(Root,var(58)),StateNo)
IgnoreHitPause = 1

[State 36000, VarSet]
Type = VarSet
TriggerAll = var(2) ;第一个时间记下来了
Trigger1 = var(3)!= 0 ;有第二招的记录
Trigger1 = var(4) = 0
V = 4
Value = (EnemyNear(Root,var(58)),Time) ;记录下第二招时间
IgnoreHitPause = 1

然后一直记到10个招
............

[State 36000, VarSet]
Type = VarSet
TriggerAll = var(17) > 0
TriggerAll = EnemyNear(Root,var(58)),Ctrl = 0
TriggerAll = EnemyNear(Root,var(58)),MoveType = A
TriggerAll = EnemyNear(Root,var(58)),HitDefAttr = SCA,NA,SA
TriggerAll = EnemyNear(Root,var(58)),StateNo = [200,499]
TriggerAll = EnemyNear(Root,var(58)),StateNo != var(1)
TriggerAll = EnemyNear(Root,var(58)),StateNo != var(3)
TriggerAll = EnemyNear(Root,var(58)),StateNo != var(5)
TriggerAll = EnemyNear(Root,var(58)),StateNo != var(7)
TriggerAll = EnemyNear(Root,var(58)),StateNo != var(9)
TriggerAll = EnemyNear(Root,var(58)),StateNo != var(11)
TriggerAll = EnemyNear(Root,var(58)),StateNo != var(13)
TriggerAll = EnemyNear(Root,var(58)),StateNo != var(15)
TriggerAll = EnemyNear(Root,var(58)),StateNo != var(17)
TriggerAll = Root,StateNo = 150 || Root,StateNo = 152
TriggerAll = Root,Time = 1
Trigger1 = var(19) = 0
V = 19
Value = (EnemyNear(Root,var(58)),StateNo)
IgnoreHitPause = 1

[State 36000, VarSet]
Type = VarSet
TriggerAll = var(18)
Trigger1 = var(19)!= 0
Trigger1 = var(20) = 0
V = 20
Value = (EnemyNear(Root,var(58)),Time)
IgnoreHitPause = 1

这个就是记录下敌方出招和时间的方法

除此之外,还可以记录下别的东西,用敌方飞行道具为例子来举个例子

;----------------------------------------------------------------------
[State 36000, 1] ;第一个状态
Type = VarSet
TriggerAll = EnemyNear(Root,var(58)),NumProj = 1 ;一般是地波和中段波之类的,出大跳的话可以大跳躲过去抓硬直
TriggerAll = EnemyNear(Root,var(58)),Ctrl = 0
TriggerAll = EnemyNear(Root,var(58)),MoveType = A
Trigger1 = var(1) = 0
var(1) = EnemyNear(Root,var(58)),StateNo
IgnoreHitPause = 1

[State 36000, 2] ;第二个
Type = VarSet
TriggerAll = EnemyNear(Root,var(58)),NumProj = 1
TriggerAll = EnemyNear(Root,var(58)),Ctrl = 0
TriggerAll = EnemyNear(Root,var(58)),MoveType = A
TriggerAll = EnemyNear(Root,var(58)),StateNo != var(1)
Trigger1 = var(1) != 0
Trigger1 = var(2) = 0
var(2) = EnemyNear(Root,var(58)),StateNo
IgnoreHitPause = 1

.............

[State 36000, 10] ;第十个
Type = VarSet
TriggerAll = EnemyNear(Root,var(58)),NumProj = 1
TriggerAll = EnemyNear(Root,var(58)),Ctrl = 0
TriggerAll = EnemyNear(Root,var(58)),MoveType = A
TriggerAll = EnemyNear(Root,var(58)),StateNo != var(1)
TriggerAll = EnemyNear(Root,var(58)),StateNo != var(2)
TriggerAll = EnemyNear(Root,var(58)),StateNo != var(3)
TriggerAll = EnemyNear(Root,var(58)),StateNo != var(4)
TriggerAll = EnemyNear(Root,var(58)),StateNo != var(5)
TriggerAll = EnemyNear(Root,var(58)),StateNo != var(6)
TriggerAll = EnemyNear(Root,var(58)),StateNo != var(7)
TriggerAll = EnemyNear(Root,var(58)),StateNo != var(8)
TriggerAll = EnemyNear(Root,var(58)),StateNo != var(9)
Trigger1 = var(1) != 0
Trigger1 = var(2) != 0
Trigger1 = var(3) != 0
Trigger1 = var(4) != 0
Trigger1 = var(5) != 0
Trigger1 = var(6) != 0
Trigger1 = var(7) != 0
Trigger1 = var(8) != 0
Trigger1 = var(9) != 0
Trigger1 = var(10) = 0
var(10) = EnemyNear(Root,var(58)),StateNo
IgnoreHitPause = 1

这样十个招都记下来了

接下来我们来用DISPLAYTOCLIPBOARD代码来观察下面的DEBUG信息

;---------------------------------------------------------------------------
[State 36000, DisplayToClipBoard]
Type = DisplayToClipBoard
Trigger1 = IsHelper(36000) ;确认是helper并且helper的ID是36000,载入的时候记得,stateno = 36000, ID = 36000
Text = "var(1)=%d,var(2)=%d,var(3)=%d,var(4)=%d,var(5)=%d"
Params = var(1),var(2),var(3),var(4),var(5)
IgnoreHitPause = 1

[State 36000, AppendToClipboard]
Type = AppendToClipboard
Trigger1 = IsHelper(36000) ;补充段,当然,不管是记录飞行道具状态号还是记录出招状态号和时间,想观察哪个就可以改成哪个
Text = "\nvar(6)=%d,var(7)=%d,var(8)=%d,var(9=%d,var(10)=%d"
Params = var(6),var(7),var(8),var(9),var(10)
IgnoreHitPause = 1

记忆器使用其实还是挺简单的吧,实战中最好用能用到的记忆,缺哪个就补哪个

好了,今天的课就先到这,如果您有什么问题的话可以私信我,我会第一时间给您答复,或者把问题发到我的邮箱

mizore@kotori.moe, 谢谢大家!
2016.03.1321:26

AI HELPER-2.5 防御段位被破的记忆(进阶)

Episode 2.5 防御段位被破的记忆(进阶)

在读此文章之前先把上一篇认真读完并看懂大概意思

接下来先放出改进后的HELPER

;------------------------------------------------------------------------------
;ガードチェックヘルパー
;------------------------------------------------------------------------------
[Statedef 31000]
type = A
movetype = I
physics = N
anim = 0
velset = 0,0
sprpriority = 1
ctrl = 0

[State 31000, AssertSpecial]
type = AssertSpecial
Trigger1 = 1
flag = NoShadow
flag2 = invisible

[State 31000, NotHitBy]
type = NotHitBy
trigger1 = 1
value = SCA

[State 31000, PlayerPush]
type = PlayerPush
trigger1 = 1
value = 0

[State 31000, 間違いで本体が来たら立ちへ移行]
type = SelfState
trigger1 =!ishelper
value = 0

[State -3, 目の前の敵が一番近い相手である]
type = VarSet
triggerall = roundstate = 2
trigger1 = numenemy <= 1
trigger2 = numenemy >= 2
trigger2 = enemy(0),life <= 0 || enemy(1),life <= 0
trigger2 = enemynear(0),life > 0
trigger3 = numenemy >= 2
trigger3 = enemy(0),life > 0 && enemy(1),life > 0
trigger3 = abs(root,pos x - enemynear(0),pos x) <= abs(root,pos x - enemynear(1),pos x)
var(1) = 0
IgnoreHitPause = 1

[State -3, 目の前の敵が一番近い相手ではない]
type = VarSet
triggerall = roundstate = 2
triggerall = numenemy >= 2
trigger1 = enemynear(0),life <= 0
trigger2 = numenemy >= 2
trigger2 = enemy(0),life <= 0 || enemy(1),life <= 0
trigger2 = enemynear(0),life <= 0
trigger3 = enemy(0),life > 0 && enemy(1),life > 0
trigger3 = abs(root,pos x - enemynear(0),pos x) > abs(root,pos x - enemynear(1),pos x)
var(1) = 1
IgnoreHitPause = 1

;自分の後ろに設置 ――――――――――――――――――――――――――――――――――――――――
[State 31000, Turn]
type = Turn
trigger1 = facing*ifelse((enemynear(var(1)),pos x-pos x)>=0,1,-1) < 0
IgnoreHitPause = 1
supermovetime = 9999
pausemovetime = 9999

[State 31000, VarSet]
type = VarSet
trigger1 = enemynear(var(1)),facing*ifelse((enemynear(var(1)),pos x-root,pos x)>=0,1,-1)<=0
trigger2 = !enemynear(var(1)),hitdefattr = A,NA,SA,HA
var(2) = 0
IgnoreHitPause = 1
supermovetime = 9999
pausemovetime = 9999

[State 31000, VarSet]
type = VarSet
trigger1 = enemynear(var(1)),facing*ifelse((EnemyNear(var(1)),pos x-root,pos x)>=0,1,-1)>0
trigger1 = enemynear(var(1)),hitdefattr = A,NA,SA,HA
var(2) = 1
IgnoreHitPause = 1
supermovetime = 9999
pausemovetime = 9999

[State 31000, PosSet]
type = PosSet
trigger1 = 1
x = root,pos x+ifelse((enemynear(var(1)),pos y<0&&var(2)=1),(enemynear(var(1)),const(size.attack.dist)),(enemynear(var(1)),const(size.proj.attack.dist)))*(enemynear(var(1)),facing)
IgnoreHitPause = 1
supermovetime = 9999
pausemovetime = 9999

;---------------------------------------------------------------------------
[State 31000, リセット]
Type = VarSet
TriggerAll = Root,var(59) = 1
TriggerAll = Root,StateNo != [120,155]
Trigger1 = Root,MoveType!= H
Trigger1 = Root,Ctrl = 1 && var(50) > 0
Trigger2 = Root,MoveType = H
Trigger2 = Root,Time > 1 && var(50) > 0
var(50) = 0
IgnoreHitPause = 1

;分别记下来防御中站立,蹲下,空中(可有可无)情况

[State 31000, 立ちガード時学習用]
Type = VarSet
TriggerAll = Root,var(59) = 1
TriggerAll = Root,StateNo = [120,155]
Trigger1 = Root,StateType = S
var(50) = 1
IgnoreHitPause = 1

[State 31000, 下段ガード時学習用]
Type = VarSet
TriggerAll = Root,var(59) = 1
TriggerAll = Root,StateNo = [120,155]
Trigger1 = Root,StateType = C
var(50) = 2
IgnoreHitPause = 1

[State 31000, 空中ガード時学習用]
Type = VarSet
TriggerAll = Root,var(59) = 1
TriggerAll = Root,StateNo = [120,155]
Trigger1 = Root,StateType = A
var(50) = 3
IgnoreHitPause = 1

;---------------------------------------------------------------------------
[State 31000, リセット]
Type = VarSet
Trigger1 = 1
var(51) = 0
IgnoreHitPause = 1

[State 31000, 特殊状態記憶] ;重点在这里,记录下本体被打并且不是站立也不是蹲下的情况
Type = VarSet
TriggerAll = Root,var(59) = 1
Trigger1 = Root,MoveType = H && Root,StateType != S && Root,StateType != C && (Root,StateNo != [120,155])
var(51) = 1
IgnoreHitPause = 1

;---------------------------------------------------------------------------
;ガード学習
;---------------------------------------------------------------------------
[State 31000, 中段記憶]
Type = VarSet
TriggerAll = Root,var(59) = 1
TriggerAll = Root,MoveType = H && Root,Time <= 1
TriggerAll =(Root,StateNo != [120,155]) && (Root,PrevStateNo = [120,155]) || Root,StateNo >= 200 && var(51) = 1 ;这里记录下特殊情况,下面也是一样
TriggerAll = EnemyNear(Root,var(58)),StateType != A && (Root,StateType = C || var(50) = 2)
Trigger1 = var(10) = 0
V = 10
Value = (EnemyNear(Root,var(58)),StateNo)
IgnoreHitPause = 1

[State 31000, 中段記憶]
Type = VarSet
TriggerAll = Root,var(59) = 1
TriggerAll = Root,MoveType = H && Root,Time <= 1
TriggerAll =(Root,StateNo != [120,155]) && (Root,PrevStateNo = [120,155]) || Root,StateNo >= 200 && var(51) = 1
TriggerAll = EnemyNear(Root,var(58)),StateType != A && (Root,StateType = C || var(50) = 2)
TriggerAll = EnemyNear(Root,var(58)),StateNo != var(10)
Trigger1 = var(10) > 0
Trigger1 = var(11) = 0
V = 11
Value = (EnemyNear(Root,var(58)),StateNo)
IgnoreHitPause = 1

[State 31000, 中段記憶]
Type = VarSet
TriggerAll = Root,var(59) = 1
TriggerAll = Root,MoveType = H && Root,Time <= 1
TriggerAll =(Root,StateNo != [120,155]) && (Root,PrevStateNo = [120,155]) || Root,StateNo >= 200 && var(51) = 1
TriggerAll = EnemyNear(Root,var(58)),StateType != A && (Root,StateType = C || var(50) = 2)
TriggerAll = EnemyNear(Root,var(58)),StateNo != var(10)
TriggerAll = EnemyNear(Root,var(58)),StateNo != var(11)
Trigger1 = var(11) > 0
Trigger1 = var(12) = 0
V = 12
Value = (EnemyNear(Root,var(58)),StateNo)
IgnoreHitPause = 1

[State 31000, 中段記憶]
Type = VarSet
TriggerAll = Root,var(59) = 1
TriggerAll = Root,MoveType = H && Root,Time <= 1
TriggerAll =(Root,StateNo != [120,155]) && (Root,PrevStateNo = [120,155]) || Root,StateNo >= 200 && var(51) = 1
TriggerAll = EnemyNear(Root,var(58)),StateType != A && (Root,StateType = C || var(50) = 2)
TriggerAll = EnemyNear(Root,var(58)),StateNo != var(10)
TriggerAll = EnemyNear(Root,var(58)),StateNo != var(11)
TriggerAll = EnemyNear(Root,var(58)),StateNo != var(12)
Trigger1 = var(12) > 0
Trigger1 = var(13) = 0
V = 13
Value = (EnemyNear(Root,var(58)),StateNo)
IgnoreHitPause = 1

[State 31000, 中段記憶]
Type = VarSet
TriggerAll = Root,var(59) = 1
TriggerAll = Root,MoveType = H && Root,Time <= 1
TriggerAll =(Root,StateNo != [120,155]) && (Root,PrevStateNo = [120,155]) || Root,StateNo >= 200 && var(51) = 1
TriggerAll = EnemyNear(Root,var(58)),StateType != A && (Root,StateType = C || var(50) = 2)
TriggerAll = EnemyNear(Root,var(58)),StateNo != var(10)
TriggerAll = EnemyNear(Root,var(58)),StateNo != var(11)
TriggerAll = EnemyNear(Root,var(58)),StateNo != var(12)
TriggerAll = EnemyNear(Root,var(58)),StateNo != var(13)
Trigger1 = var(13) > 0
Trigger1 = var(14) = 0
V = 14
Value = (EnemyNear(Root,var(58)),StateNo)
IgnoreHitPause = 1

;---------------------------------------------------------------------------
[State 31000, 下段記憶]
Type = VarSet
TriggerAll = Root,var(59) = 1
TriggerAll = Root,MoveType = H && Root,Time <= 1
TriggerAll =(Root,StateNo != [120,155]) && (Root,PrevStateNo = [120,155]) || Root,StateNo >= 200 && var(51) = 1
TriggerAll = EnemyNear(Root,var(58)),StateType != C && (Root,StateType = S || var(50) = 1)
Trigger1 = var(20) = 0
V = 20
Value = (EnemyNear(Root,var(58)),StateNo)
IgnoreHitPause = 1

[State 31000, 下段記憶]
Type = VarSet
TriggerAll = Root,var(59) = 1
TriggerAll = Root,MoveType = H && Root,Time <= 1
TriggerAll =(Root,StateNo != [120,155]) && (Root,PrevStateNo = [120,155]) || Root,StateNo >= 200 && var(51) = 1
TriggerAll = EnemyNear(Root,var(58)),StateType != C && (Root,StateType = S || var(50) = 1)
TriggerAll = EnemyNear(Root,var(58)),StateNo != var(20)
Trigger1 = var(20) > 0
Trigger1 = var(21) = 0
V = 21
Value = (EnemyNear(Root,var(58)),StateNo)
IgnoreHitPause = 1

[State 31000, 下段記憶]
Type = VarSet
TriggerAll = Root,var(59) = 1
TriggerAll = Root,MoveType = H && Root,Time <= 1
TriggerAll =(Root,StateNo != [120,155]) && (Root,PrevStateNo = [120,155]) || Root,StateNo >= 200 && var(51) = 1
TriggerAll = EnemyNear(Root,var(58)),StateType != C && (Root,StateType = S || var(50) = 1)
TriggerAll = EnemyNear(Root,var(58)),StateNo != var(20)
TriggerAll = EnemyNear(Root,var(58)),StateNo != var(21)
Trigger1 = var(21) > 0
Trigger1 = var(22) = 0
V = 22
Value = (EnemyNear(Root,var(58)),StateNo)
IgnoreHitPause = 1

[State 31000, 下段記憶]
Type = VarSet
TriggerAll = Root,var(59) = 1
TriggerAll = Root,MoveType = H && Root,Time <= 1
TriggerAll =(Root,StateNo != [120,155]) && (Root,PrevStateNo = [120,155]) || Root,StateNo >= 200 && var(51) = 1
TriggerAll = EnemyNear(Root,var(58)),StateType != C && (Root,StateType = S || var(50) = 1)
TriggerAll = EnemyNear(Root,var(58)),StateNo != var(20)
TriggerAll = EnemyNear(Root,var(58)),StateNo != var(21)
TriggerAll = EnemyNear(Root,var(58)),StateNo != var(22)
Trigger1 = var(22) > 0
Trigger1 = var(23) = 0
V = 23
Value = (EnemyNear(Root,var(58)),StateNo)
IgnoreHitPause = 1

[State 31000, 下段記憶]
Type = VarSet
TriggerAll = Root,var(59) = 1
TriggerAll = Root,MoveType = H && Root,Time <= 1
TriggerAll =(Root,StateNo != [120,155]) && (Root,PrevStateNo = [120,155]) || Root,StateNo >= 200 && var(51) = 1
TriggerAll = EnemyNear(Root,var(58)),StateType != C && (Root,StateType = S || var(50) = 1)
TriggerAll = EnemyNear(Root,var(58)),StateNo != var(20)
TriggerAll = EnemyNear(Root,var(58)),StateNo != var(21)
TriggerAll = EnemyNear(Root,var(58)),StateNo != var(22)
TriggerAll = EnemyNear(Root,var(58)),StateNo != var(23)
Trigger1 = var(23) > 0
Trigger1 = var(24) = 0
V = 24
Value = (EnemyNear(Root,var(58)),StateNo)
IgnoreHitPause = 1

;---------------------------------------------------------------------------
[State 31000, 中段記憶削除]
Type = VarSet
TriggerAll = Root,var(59) = 1
TriggerAll = Root,MoveType = H && Root,Time <= 1
TriggerAll =(Root,StateNo != [120,155]) && (Root,PrevStateNo = [120,155]) || Root,StateNo >= 200 && var(51) = 1
TriggerAll = EnemyNear(Root,var(58)),StateType != A && (Root,StateType = S || var(50) = 1)
TriggerAll = EnemyNear(Root,var(58)),StateNo = var(10)
Trigger1 = var(10) > 0
V = 10
Value = 0
IgnoreHitPause = 1

[State 31000, 中段記憶削除]
Type = VarSet
TriggerAll = Root,var(59) = 1
TriggerAll = Root,MoveType = H && Root,Time <= 1
TriggerAll =(Root,StateNo != [120,155]) && (Root,PrevStateNo = [120,155]) || Root,StateNo >= 200 && var(51) = 1
TriggerAll = EnemyNear(Root,var(58)),StateType != A && (Root,StateType = S || var(50) = 1)
TriggerAll = EnemyNear(Root,var(58)),StateNo = var(11)
Trigger1 = var(11) > 0
V = 11
Value = 0
IgnoreHitPause = 1

[State 31000, 中段記憶削除]
Type = VarSet
TriggerAll = Root,var(59) = 1
TriggerAll = Root,MoveType = H && Root,Time <= 1
TriggerAll =(Root,StateNo != [120,155]) && (Root,PrevStateNo = [120,155]) || Root,StateNo >= 200 && var(51) = 1
TriggerAll = EnemyNear(Root,var(58)),StateType != A && (Root,StateType = S || var(50) = 1)
TriggerAll = EnemyNear(Root,var(58)),StateNo = var(12)
Trigger1 = var(12) > 0
V = 12
Value = 0
IgnoreHitPause = 1

[State 31000, 中段記憶削除]
Type = VarSet
TriggerAll = Root,var(59) = 1
TriggerAll = Root,MoveType = H && Root,Time <= 1
TriggerAll =(Root,StateNo != [120,155]) && (Root,PrevStateNo = [120,155]) || Root,StateNo >= 200 && var(51) = 1
TriggerAll = EnemyNear(Root,var(58)),StateType != A && (Root,StateType = S || var(50) = 1)
TriggerAll = EnemyNear(Root,var(58)),StateNo = var(13)
Trigger1 = var(13) > 0
V = 13
Value = 0
IgnoreHitPause = 1

[State 31000, 中段記憶削除]
Type = VarSet
TriggerAll = Root,var(59) = 1
TriggerAll = Root,MoveType = H && Root,Time <= 1
TriggerAll =(Root,StateNo != [120,155]) && (Root,PrevStateNo = [120,155]) || Root,StateNo >= 200 && var(51) = 1
TriggerAll = EnemyNear(Root,var(58)),StateType != A && (Root,StateType = S || var(50) = 1)
TriggerAll = EnemyNear(Root,var(58)),StateNo = var(14)
Trigger1 = var(14) > 0
V = 14
Value = 0
IgnoreHitPause = 1

;---------------------------------------------------------------------------
[State 31000, 下段記憶削除]
Type = VarSet
TriggerAll = Root,var(59) = 1
TriggerAll = Root,MoveType = H && Root,Time <= 1
TriggerAll =(Root,StateNo != [120,155]) && (Root,PrevStateNo = [120,155]) || Root,StateNo >= 200 && var(51) = 1
TriggerAll = EnemyNear(Root,var(58)),StateType != C && (Root,StateType = C || var(50) = 2)
TriggerAll = EnemyNear(Root,var(58)),StateNo = var(20)
Trigger1 = var(20) > 0
V = 20
Value = 0
IgnoreHitPause = 1

[State 31000, 下段記憶削除]
Type = VarSet
TriggerAll = Root,var(59) = 1
TriggerAll = Root,MoveType = H && Root,Time <= 1
TriggerAll =(Root,StateNo != [120,155]) && (Root,PrevStateNo = [120,155]) || Root,StateNo >= 200 && var(51) = 1
TriggerAll = EnemyNear(Root,var(58)),StateType != C && (Root,StateType = C || var(50) = 2)
TriggerAll = EnemyNear(Root,var(58)),StateNo = var(21)
Trigger1 = var(21) > 0
V = 21
Value = 0
IgnoreHitPause = 1

[State 31000, 下段記憶削除]
Type = VarSet
TriggerAll = Root,var(59) = 1
TriggerAll = Root,MoveType = H && Root,Time <= 1
TriggerAll =(Root,StateNo != [120,155]) && (Root,PrevStateNo = [120,155]) || Root,StateNo >= 200 && var(51) = 1
TriggerAll = EnemyNear(Root,var(58)),StateType != C && (Root,StateType = C || var(50) = 2)
TriggerAll = EnemyNear(Root,var(58)),StateNo = var(22)
Trigger1 = var(22) > 0
V = 22
Value = 0
IgnoreHitPause = 1

[State 31000, 下段記憶削除]
Type = VarSet
TriggerAll = Root,var(59) = 1
TriggerAll = Root,MoveType = H && Root,Time <= 1
TriggerAll =(Root,StateNo != [120,155]) && (Root,PrevStateNo = [120,155]) || Root,StateNo >= 200 && var(51) = 1
TriggerAll = EnemyNear(Root,var(58)),StateType != C && (Root,StateType = C || var(50) = 2)
TriggerAll = EnemyNear(Root,var(58)),StateNo = var(23)
Trigger1 = var(23) > 0
V = 23
Value = 0
IgnoreHitPause = 1

[State 31000, 下段記憶削除]
Type = VarSet
TriggerAll = Root,var(59) = 1
TriggerAll = Root,MoveType = H && Root,Time <= 1
TriggerAll =(Root,StateNo != [120,155]) && (Root,PrevStateNo = [120,155]) || Root,StateNo >= 200 && var(51) = 1
TriggerAll = EnemyNear(Root,var(58)),StateType != C && (Root,StateType = C || var(50) = 2)
TriggerAll = EnemyNear(Root,var(58)),StateNo = var(24)
Trigger1 = var(24) > 0
V = 24
Value = 0
IgnoreHitPause = 1

;---------------------------------------------------------------------------
[State 31000, DisplayToClipBoard]
Type = DisplayToClipBoard
trigger1 = IsHelper
Text = "var(10)=%d,var(11)=%d,var(12)=%d,var(13)=%d,var(14)=%d"
Params = var(10),var(11),var(12),var(13),var(14)
IgnoreHitPause = 1

[State 31000, AppendToClipboard]
type = AppendToClipboard
trigger1 = IsHelper
text = "\nvar(20)=%d,var(21)=%d,var(22)=%d,var(23)=%d,var(24)=%d"
params = var(20),var(21),var(22),var(23),var(24)
IgnoreHitPause = 1

;---------------------------------------------------------------------------
[State 31000, DestroySelf]
type = DestroySelf
triggerall = ishelper
triggerall = roundstate >= 3
trigger1 = root,stateno != [120,159]
trigger2 = roundstate >= 4

和前面一样,就是多加了两个var,记下来root,statetype情况和本体被击飞或者被强制倒地的情况

因为本来上节课那么写的是不能记下来被击飞或者强制倒地的情况的,只能记录root,statetype = S或者root,statetype = C的情况

下面来给出示例图,可以放大图片看下面的DEBUG数据

这是未记录任何被破情况
这个是未记录任何被破情况

此时下段被破了,记录下下段
此时下段被破了,记录下下段StateNo

下次就不会再吃了
那么下次就不会再吃了

中段
中段技也一样,先是被打了

下次也不会再吃了
同理,下次也不会再吃了


所以这次呢,不妨大家试用一下我改进以后的防御记忆HELPER

好了,今天的课就先到这,如果您有什么问题的话可以私信我,我会第一时间给您答复,或者把问题发到我的邮箱

mizore@kotori.moe, 谢谢大家!
2016.03.1212:53

AI HELPER-2,防御段位被破的记忆

Episode 2 防御段位被破的记忆

这次算是给前面的防御段位切换的AI进行补充了

我们之前说过,防御分站防,蹲防,空防,空防因为不会有段位变化姑且不谈,AI的难题之一就是地面上的防御段位切换

下面给出一个防御记忆HELPER

;------------------------------------------------------------------------------
;ガードチェックヘルパー
;------------------------------------------------------------------------------
[Statedef 31000]
type = A
movetype = I
physics = N
anim = 0
velset = 0,0
sprpriority = 1
ctrl = 0

[State 31000, AssertSpecial]
type = AssertSpecial
Trigger1 = 1
flag = NoShadow
flag2 = invisible

[State 31000, NotHitBy]
type = NotHitBy
trigger1 = 1
value = SCA

[State 31000, PlayerPush]
type = PlayerPush
trigger1 = 1
value = 0

[State 31000, 間違いで本体が来たら立ちへ移行]
type = SelfState
trigger1 =!ishelper
value = 0

;------------------------从这边以上都是基础部分就不讲了

[State -3, 目の前の敵が一番近い相手である] ;记录下有近敌,作用相当于TAG
type = VarSet
triggerall = roundstate = 2 ;比赛中
trigger1 = numenemy <= 1 ;敌人只有1个或者没有
trigger2 = numenemy >= 2 ;敌人两个或者更多
trigger2 = enemy(0),life <= 0 || enemy(1),life <= 0 ;近敌或者远敌死亡
trigger2 = enemynear(0),life > 0 ;近敌存活,此时可以记住这个是近敌
trigger3 = numenemy >= 2 ;两个或者以上的敌人
trigger3 = enemy(0),life > 0 && enemy(1),life > 0 ;都存活
trigger3 = abs(root,pos x - enemynear(0),pos x) <= abs(root,pos x - enemynear(1),pos x) ;与最近敌人的距离小于较远的那个,就可以判断是近敌了
var(1) = 0
IgnoreHitPause = 1

[State -3, 目の前の敵が一番近い相手ではない] ;和上面反着来,这次就是较远的那位
type = VarSet
triggerall = roundstate = 2
triggerall = numenemy >= 2
trigger1 = enemynear(0),life <= 0
trigger2 = numenemy >= 2
trigger2 = enemy(0),life <= 0 || enemy(1),life <= 0
trigger2 = enemynear(0),life <= 0
trigger3 = enemy(0),life > 0 && enemy(1),life > 0
trigger3 = abs(root,pos x - enemynear(0),pos x) > abs(root,pos x - enemynear(1),pos x)
var(1) = 1
IgnoreHitPause = 1

;自分の後ろに設置 ――――――――――――――――――――――――――――――――――――――――
[State 31000, Turn] ;面向设置,保持(HELPER自己)面向对手(近的那位),var(1) = 0为优先,所以与近敌之间面对的时候不转方向,背对的时候转个方向
type = Turn
trigger1 = facing*ifelse((enemynear(var(1)),pos x-pos x)>=0,1,-1) < 0
IgnoreHitPause = 1
supermovetime = 9999
pausemovetime = 9999

[State 31000, VarSet]
type = VarSet
trigger1 = enemynear(var(1)),facing*ifelse((enemynear(var(1)),pos x-root,pos x)>=0,1,-1)<=0 ;近敌与本体间的距离判断为背对
trigger2 = !enemynear(var(1)),hitdefattr = A,NA,SA,HA ;对手不是空中技能(只能说明绝大多数)
var(2) = 0
IgnoreHitPause = 1
supermovetime = 9999
pausemovetime = 9999

[State 31000, VarSet]
type = VarSet
trigger1 = enemynear(var(1)),facing*ifelse((EnemyNear(var(1)),pos x-root,pos x)>=0,1,-1)>0 ;与上面相反,说明本体是面对近敌
trigger1 = enemynear(var(1)),hitdefattr = A,NA,SA,HA ;对手是空中技能(绝大多数)
var(2) = 1
IgnoreHitPause = 1
supermovetime = 9999
pausemovetime = 9999

[State 31000, PosSet] ;设置HELPER自己的位置,当对手是空中并且面对对手,满足空中拳脚之类的技能的时候,本体距离+attack.dist(默认160),除此之外就是proj.attack.dist(默认90),并确定朝向近敌
type = PosSet
trigger1 = 1
x = root,pos x+ifelse((enemynear(var(1)),pos y<0&&var(2)=1),(enemynear(var(1)),const(size.attack.dist)),(enemynear(var(1)),const(size.proj.attack.dist)))*(enemynear(var(1)),facing)
IgnoreHitPause = 1
supermovetime = 9999
pausemovetime = 9999

;---------------------------------------------------------------------------
;ガード学習 ;破防记忆,只可惜被击飞或者某些带p2stateno的特殊状态没法被记下来:(
;---------------------------------------------------------------------------
[State 31000, 中段記憶] ;当对手在地面且本体蹲下的时候,招式被破了,记下来,一般是特殊技6A,6B之类的
Type = VarSet
TriggerAll = Root,var(59) = 1
TriggerAll = Root,MoveType = H && Root,Time <= 1 ;被打的第一时间记下来,防止多记下不该有的
TriggerAll =(Root,StateNo != [120,155]) && (Root,PrevStateNo = [120,155]) ;被破防之前是防御状态
TriggerAll = EnemyNear(Root,var(58)),StateType != A && Root,StateType = C
Trigger1 = var(10) = 0
V = 10
Value = (EnemyNear(Root,var(58)),StateNo)
IgnoreHitPause = 1

[State 31000, 中段記憶]
Type = VarSet
TriggerAll = Root,var(59) = 1
TriggerAll = Root,MoveType = H && Root,Time <= 1
TriggerAll =(Root,StateNo != [120,155]) && (Root,PrevStateNo = [120,155])
TriggerAll = EnemyNear(Root,var(58)),StateType != A && Root,StateType = C
TriggerAll = EnemyNear(Root,var(58)),StateNo != var(10) ;不能与上面冲突,记下新的
Trigger1 = var(10) > 0 ;确定这个状态不是默认状态,记下新的,下面也是一样的道理
Trigger1 = var(11) = 0
V = 11
Value = (EnemyNear(Root,var(58)),StateNo)
IgnoreHitPause = 1

[State 31000, 中段記憶]
Type = VarSet
TriggerAll = Root,var(59) = 1
TriggerAll = Root,MoveType = H && Root,Time <= 1
TriggerAll =(Root,StateNo != [120,155]) && (Root,PrevStateNo = [120,155])
TriggerAll = EnemyNear(Root,var(58)),StateType != A && Root,StateType = C
TriggerAll = EnemyNear(Root,var(58)),StateNo != var(10)
TriggerAll = EnemyNear(Root,var(58)),StateNo != var(11)
Trigger1 = var(11) > 0
Trigger1 = var(12) = 0
V = 12
Value = (EnemyNear(Root,var(58)),StateNo)
IgnoreHitPause = 1

[State 31000, 中段記憶]
Type = VarSet
TriggerAll = Root,var(59) = 1
TriggerAll = Root,MoveType = H && Root,Time <= 1
TriggerAll =(Root,StateNo != [120,155]) && (Root,PrevStateNo = [120,155])
TriggerAll = EnemyNear(Root,var(58)),StateType != A && Root,StateType = C
TriggerAll = EnemyNear(Root,var(58)),StateNo != var(10)
TriggerAll = EnemyNear(Root,var(58)),StateNo != var(11)
TriggerAll = EnemyNear(Root,var(58)),StateNo != var(12)
Trigger1 = var(12) > 0
Trigger1 = var(13) = 0
V = 13
Value = (EnemyNear(Root,var(58)),StateNo)
IgnoreHitPause = 1

[State 31000, 中段記憶]
Type = VarSet
TriggerAll = Root,var(59) = 1
TriggerAll = Root,MoveType = H && Root,Time <= 1
TriggerAll =(Root,StateNo != [120,155]) && (Root,PrevStateNo = [120,155])
TriggerAll = EnemyNear(Root,var(58)),StateType != A && Root,StateType = C
TriggerAll = EnemyNear(Root,var(58)),StateNo != var(10)
TriggerAll = EnemyNear(Root,var(58)),StateNo != var(11)
TriggerAll = EnemyNear(Root,var(58)),StateNo != var(12)
TriggerAll = EnemyNear(Root,var(58)),StateNo != var(13)
Trigger1 = var(13) > 0
Trigger1 = var(14) = 0
V = 14
Value = (EnemyNear(Root,var(58)),StateNo)
IgnoreHitPause = 1

;---------------------------------------------------------------------------
[State 31000, 下段記憶] ;本体站立时候且对手不是下蹲状态,一般记下5B之类的
Type = VarSet
TriggerAll = Root,var(59) = 1
TriggerAll = Root,MoveType = H && Root,Time <= 1
TriggerAll =(Root,StateNo != [120,155]) && (Root,PrevStateNo = [120,155])
TriggerAll = EnemyNear(Root,var(58)),StateType != C && Root,StateType = S
Trigger1 = var(20) = 0
V = 20
Value = (EnemyNear(Root,var(58)),StateNo)
IgnoreHitPause = 1

[State 31000, 下段記憶]
Type = VarSet
TriggerAll = Root,var(59) = 1
TriggerAll = Root,MoveType = H && Root,Time <= 1
TriggerAll =(Root,StateNo != [120,155]) && (Root,PrevStateNo = [120,155])
TriggerAll = EnemyNear(Root,var(58)),StateType != C && Root,StateType = S
TriggerAll = EnemyNear(Root,var(58)),StateNo != var(20)
Trigger1 = var(20) > 0
Trigger1 = var(21) = 0
V = 21
Value = (EnemyNear(Root,var(58)),StateNo)
IgnoreHitPause = 1

[State 31000, 下段記憶]
Type = VarSet
TriggerAll = Root,var(59) = 1
TriggerAll = Root,MoveType = H && Root,Time <= 1
TriggerAll =(Root,StateNo != [120,155]) && (Root,PrevStateNo = [120,155])
TriggerAll = EnemyNear(Root,var(58)),StateType != C && Root,StateType = S
TriggerAll = EnemyNear(Root,var(58)),StateNo != var(20)
TriggerAll = EnemyNear(Root,var(58)),StateNo != var(21)
Trigger1 = var(21) > 0
Trigger1 = var(22) = 0
V = 22
Value = (EnemyNear(Root,var(58)),StateNo)
IgnoreHitPause = 1

[State 31000, 下段記憶]
Type = VarSet
TriggerAll = Root,var(59) = 1
TriggerAll = Root,MoveType = H && Root,Time <= 1
TriggerAll =(Root,StateNo != [120,155]) && (Root,PrevStateNo = [120,155])
TriggerAll = EnemyNear(Root,var(58)),StateType != C && Root,StateType = S
TriggerAll = EnemyNear(Root,var(58)),StateNo != var(20)
TriggerAll = EnemyNear(Root,var(58)),StateNo != var(21)
TriggerAll = EnemyNear(Root,var(58)),StateNo != var(22)
Trigger1 = var(22) > 0
Trigger1 = var(23) = 0
V = 23
Value = (EnemyNear(Root,var(58)),StateNo)
IgnoreHitPause = 1

[State 31000, 下段記憶]
Type = VarSet
TriggerAll = Root,var(59) = 1
TriggerAll = Root,MoveType = H && Root,Time <= 1
TriggerAll =(Root,StateNo != [120,155]) && (Root,PrevStateNo = [120,155])
TriggerAll = EnemyNear(Root,var(58)),StateType != C && Root,StateType = S
TriggerAll = EnemyNear(Root,var(58)),StateNo != var(20)
TriggerAll = EnemyNear(Root,var(58)),StateNo != var(21)
TriggerAll = EnemyNear(Root,var(58)),StateNo != var(22)
TriggerAll = EnemyNear(Root,var(58)),StateNo != var(23)
Trigger1 = var(23) > 0
Trigger1 = var(24) = 0
V = 24
Value = (EnemyNear(Root,var(58)),StateNo)
IgnoreHitPause = 1

;---------------------------------------------------------------------------
[State 31000, 中段記憶削除] ;★★★为了防止某些特殊的状态(比如崩防,statetype = S,movetype = H)这种的而准备的
Type = VarSet
TriggerAll = Root,var(59) = 1
TriggerAll = Root,MoveType = H && Root,Time <= 1
TriggerAll =(Root,StateNo != [120,155]) && (Root,PrevStateNo = [120,155])
TriggerAll = EnemyNear(Root,var(58)),StateType != A && Root,StateType = S
TriggerAll = EnemyNear(Root,var(58)),StateNo = var(10) ;记错了,把它消去,重新记录
Trigger1 = var(10) > 0
V = 10
Value = 0
IgnoreHitPause = 1

[State 31000, 中段記憶削除]
Type = VarSet
TriggerAll = Root,var(59) = 1
TriggerAll = Root,MoveType = H && Root,Time <= 1
TriggerAll =(Root,StateNo != [120,155]) && (Root,PrevStateNo = [120,155])
TriggerAll = EnemyNear(Root,var(58)),StateType != A && Root,StateType = S
TriggerAll = EnemyNear(Root,var(58)),StateNo = var(11)
Trigger1 = var(11) > 0
V = 11
Value = 0
IgnoreHitPause = 1

[State 31000, 中段記憶削除]
Type = VarSet
TriggerAll = Root,var(59) = 1
TriggerAll = Root,MoveType = H && Root,Time <= 1
TriggerAll =(Root,StateNo != [120,155]) && (Root,PrevStateNo = [120,155])
TriggerAll = EnemyNear(Root,var(58)),StateType != A && Root,StateType = S
TriggerAll = EnemyNear(Root,var(58)),StateNo = var(12)
Trigger1 = var(12) > 0
V = 12
Value = 0
IgnoreHitPause = 1

[State 31000, 中段記憶削除]
Type = VarSet
TriggerAll = Root,var(59) = 1
TriggerAll = Root,MoveType = H && Root,Time <= 1
TriggerAll =(Root,StateNo != [120,155]) && (Root,PrevStateNo = [120,155])
TriggerAll = EnemyNear(Root,var(58)),StateType != A && Root,StateType = S
TriggerAll = EnemyNear(Root,var(58)),StateNo = var(13)
Trigger1 = var(13) > 0
V = 13
Value = 0
IgnoreHitPause = 1

[State 31000, 中段記憶削除]
Type = VarSet
TriggerAll = Root,var(59) = 1
TriggerAll = Root,MoveType = H && Root,Time <= 1
TriggerAll =(Root,StateNo != [120,155]) && (Root,PrevStateNo = [120,155])
TriggerAll = EnemyNear(Root,var(58)),StateType != A && Root,StateType = S
TriggerAll = EnemyNear(Root,var(58)),StateNo = var(14)
Trigger1 = var(14) > 0
V = 14
Value = 0
IgnoreHitPause = 1

;---------------------------------------------------------------------------
[State 31000, 下段記憶削除] ;这边也是一样
Type = VarSet
TriggerAll = Root,var(59) = 1
TriggerAll = Root,MoveType = H && Root,Time <= 1
TriggerAll =(Root,StateNo != [120,155]) && (Root,PrevStateNo = [120,155])
TriggerAll = EnemyNear(Root,var(58)),StateType != C && Root,StateType = C
TriggerAll = EnemyNear(Root,var(58)),StateNo = var(20)
Trigger1 = var(20) > 0
V = 20
Value = 0
IgnoreHitPause = 1

[State 31000, 下段記憶削除]
Type = VarSet
TriggerAll = Root,var(59) = 1
TriggerAll = Root,MoveType = H && Root,Time <= 1
TriggerAll =(Root,StateNo != [120,155]) && (Root,PrevStateNo = [120,155])
TriggerAll = EnemyNear(Root,var(58)),StateType != C && Root,StateType = C
TriggerAll = EnemyNear(Root,var(58)),StateNo = var(21)
Trigger1 = var(21) > 0
V = 21
Value = 0
IgnoreHitPause = 1

[State 31000, 下段記憶削除]
Type = VarSet
TriggerAll = Root,var(59) = 1
TriggerAll = Root,MoveType = H && Root,Time <= 1
TriggerAll =(Root,StateNo != [120,155]) && (Root,PrevStateNo = [120,155])
TriggerAll = EnemyNear(Root,var(58)),StateType != C && Root,StateType = C
TriggerAll = EnemyNear(Root,var(58)),StateNo = var(22)
Trigger1 = var(22) > 0
V = 22
Value = 0
IgnoreHitPause = 1

[State 31000, 下段記憶削除]
Type = VarSet
TriggerAll = Root,var(59) = 1
TriggerAll = Root,MoveType = H && Root,Time <= 1
TriggerAll =(Root,StateNo != [120,155]) && (Root,PrevStateNo = [120,155])
TriggerAll = EnemyNear(Root,var(58)),StateType != C && Root,StateType = C
TriggerAll = EnemyNear(Root,var(58)),StateNo = var(23)
Trigger1 = var(23) > 0
V = 23
Value = 0
IgnoreHitPause = 1

[State 31000, 下段記憶削除]
Type = VarSet
TriggerAll = Root,var(59) = 1
TriggerAll = Root,MoveType = H && Root,Time <= 1
TriggerAll =(Root,StateNo != [120,155]) && (Root,PrevStateNo = [120,155])
TriggerAll = EnemyNear(Root,var(58)),StateType != C && Root,StateType = C
TriggerAll = EnemyNear(Root,var(58)),StateNo = var(24)
Trigger1 = var(24) > 0
V = 24
Value = 0
IgnoreHitPause = 1

;---------------------------------------------------------------------------
[State 31000, DisplayToClipBoard] ;在ctrl+d下可以看到变化
Type = DisplayToClipBoard
trigger1 = IsHelper
Text = "var(10)=%d,var(11)=%d,var(12)=%d,var(13)=%d,var(14)=%d"
Params = var(10),var(11),var(12),var(13),var(14)
IgnoreHitPause = 1

[State 31000, AppendToClipboard] ;增加下段观察
type = AppendToClipboard
trigger1 = IsHelper
text = "\nvar(20)=%d,var(21)=%d,var(22)=%d,var(23)=%d,var(24)=%d"
params = var(20),var(21),var(22),var(23),var(24)
IgnoreHitPause = 1

;---------------------------------------------------------------------------
[State 31000, DestroySelf] ;一局结束之后把它消去比较好,下一局重新记录
type = DestroySelf
triggerall = ishelper
triggerall = roundstate >= 3
trigger1 = root,stateno != [120,159]
trigger2 = roundstate >= 4

;------------------------------------------------------------------------------------
然后同样的方法,召唤出HELPER

[State -3, ガードチェックヘルパー]
Type = Helper
TriggerAll = var(59)
TriggerAll = RoundState = 2
Trigger1 =!NumHelper(31000+ID)
Trigger1 =!IsHelper
HelperType = Normal
Name = "Guard"
PosType = left
StateNo = 31000
ID = 31000+ID
Pos = 0,9999
KeyCtrl = 0
Ownpal = 1
Persistent = 0
IgnoreHitPause = 1
PauseMoveTime = 2147483647
SuperMoveTime = 2147483647

[State -3, 関係無いステートに行かないように]
Type = ChangeState
Trigger1 = IsHelper
Trigger1 = IsHelper(31000+Root,ID)
Trigger1 = StateNo != 31000
Value = 31000

;很多都是前面提过的,这里就不多讲了

然后在段位里面这样用,消耗一个var,记录在-3下,如果var冲突了就换别的var

;==============================================================================
;AIガード切り替え
;==============================================================================
[State -3, 防御する必要がない状況] ;不需要防守的时候,敌人死了,或者是不在guarddist内
Type = VarSet
TriggerAll = !IsHelper
Trigger1 = NumEnemy = 0
Trigger2 = !InGuardDist
var(48) = 0
IgnoreHitPause = 1

[State -3, 立ち] ;站防,用var(48) = 2来代替
Type = VarSet
TriggerAll = var(59) = 1
TriggerAll =!IsHelper
TriggerAll = NumEnemy
TriggerAll = NumHelper(31000+ID)
TriggerAll = StateType != A
TriggerAll = EnemyNear(var(58)),StateNo != 0
Trigger1 = EnemyNear(var(58)),StateNo = Helper(31000+ID),var(10)
Trigger2 = EnemyNear(var(58)),StateNo = Helper(31000+ID),var(11)
Trigger3 = EnemyNear(var(58)),StateNo = Helper(31000+ID),var(12)
Trigger4 = EnemyNear(var(58)),StateNo = Helper(31000+ID),var(13)
Trigger5 = EnemyNear(var(58)),StateNo = Helper(31000+ID),var(14)
var(48) = 2
IgnoreHitPause = 1

[State -3, しゃがみ] ;蹲防,用var(48) = 1来代替
Type = VarSet
TriggerAll = var(59) = 1
TriggerAll =!IsHelper
TriggerAll = NumEnemy
TriggerAll = NumHelper(31000+ID)
TriggerAll = StateType != A
TriggerAll = EnemyNear(var(58)),StateNo != 0
Trigger1 = EnemyNear(var(58)),StateNo = Helper(31000+ID),var(20)
Trigger2 = EnemyNear(var(58)),StateNo = Helper(31000+ID),var(21)
Trigger3 = EnemyNear(var(58)),StateNo = Helper(31000+ID),var(22)
Trigger4 = EnemyNear(var(58)),StateNo = Helper(31000+ID),var(23)
Trigger5 = EnemyNear(var(58)),StateNo = Helper(31000+ID),var(24)
var(48) = 1
IgnoreHitPause = 1

[State -3, 立ち]
Type = VarSet
TriggerAll = var(59) = 1
TriggerAll =!IsHelper
TriggerAll = NumEnemy
TriggerAll = NumHelper(31000+ID)
TriggerAll = StateType != A
TriggerAll = EnemyNear(var(58)),StateNo != Helper(31000+ID),var(20) ;不能是被破防的招式
TriggerAll = EnemyNear(var(58)),StateNo != Helper(31000+ID),var(21)
TriggerAll = EnemyNear(var(58)),StateNo != Helper(31000+ID),var(22)
TriggerAll = EnemyNear(var(58)),StateNo != Helper(31000+ID),var(23)
TriggerAll = EnemyNear(var(58)),StateNo != Helper(31000+ID),var(24)
Trigger1 = var(57) = [1,4]
Trigger1 = EnemyNear(var(58)),StateType = A
Trigger1 = Random <= ((var(57) * 12.5) * var(56))
Trigger1 = Random <= 250
Trigger2 = var(57) > 4
Trigger2 = EnemyNear(var(58)),StateType = A
Trigger3 = var(57) > 4
Trigger3 = EnemyNear(var(58)),StateType = S
Trigger3 = EnemyNear(var(58)),Time >= 20 || EnemyNear(var(58)),StateNo >= 1000
Trigger3 =!EnemyNear(var(58)),NumProj
var(48) = 2
IgnoreHitPause = 1

[State -3, しゃがみ]
Type = VarSet
TriggerAll = var(59) = 1
TriggerAll =!IsHelper
TriggerAll = NumEnemy
TriggerAll = NumHelper(31000+ID)
TriggerAll = StateType != A
TriggerAll = EnemyNear(var(58)),StateNo != Helper(31000+ID),var(10)
TriggerAll = EnemyNear(var(58)),StateNo != Helper(31000+ID),var(11)
TriggerAll = EnemyNear(var(58)),StateNo != Helper(31000+ID),var(12)
TriggerAll = EnemyNear(var(58)),StateNo != Helper(31000+ID),var(13)
TriggerAll = EnemyNear(var(58)),StateNo != Helper(31000+ID),var(14)
Trigger1 = var(57) = [1,4]
Trigger1 = EnemyNear(var(58)),StateType != A
Trigger1 = Random <= ((var(57) * 12.5) * var(56))
Trigger1 = Random <= 250
Trigger2 = var(57) > 4
Trigger2 = EnemyNear(var(58)),StateType = C
Trigger3 = var(57) > 4
Trigger3 = EnemyNear(var(58)),StateType = S
Trigger3 = EnemyNear(var(58)),Time < 20 && EnemyNear(var(58)),StateNo < 1000
Trigger3 =!EnemyNear(var(58)),NumProj
Trigger4 = var(57) > 4
Trigger4 = EnemyNear(var(58)),StateType = S
Trigger4 = EnemyNear(var(58)),NumProj
var(48) = 1
IgnoreHitPause = 1

[State -3, 立ち]
Type = VarSet
TriggerAll = var(59) = 1
TriggerAll =!IsHelper
TriggerAll = NumEnemy
TriggerAll = NumHelper(31000+ID)
TriggerAll = StateType != A
TriggerAll = MoveType != H
Trigger1 = EnemyNear(var(58)),StateNo = Helper(31000+ID),var(10) ;破防以后换一个段位记忆招式
Trigger2 = EnemyNear(var(58)),StateNo = Helper(31000+ID),var(11)
Trigger3 = EnemyNear(var(58)),StateNo = Helper(31000+ID),var(12)
Trigger4 = EnemyNear(var(58)),StateNo = Helper(31000+ID),var(13)
Trigger5 = EnemyNear(var(58)),StateNo = Helper(31000+ID),var(14)
var(48) = 2
IgnoreHitPause = 1

[State -3, しゃがみ]
Type = VarSet
TriggerAll = var(59) = 1
TriggerAll =!IsHelper
TriggerAll = NumEnemy
TriggerAll = NumHelper(31000+ID)
TriggerAll = StateType != A
TriggerAll = MoveType != H
Trigger1 = EnemyNear(var(58)),StateNo = Helper(31000+ID),var(20)
Trigger2 = EnemyNear(var(58)),StateNo = Helper(31000+ID),var(21)
Trigger3 = EnemyNear(var(58)),StateNo = Helper(31000+ID),var(22)
Trigger4 = EnemyNear(var(58)),StateNo = Helper(31000+ID),var(23)
Trigger5 = EnemyNear(var(58)),StateNo = Helper(31000+ID),var(24)
var(48) = 1
IgnoreHitPause = 1

120-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) != 1
trigger1 = command = "holddown"
trigger2 = var(59) = 1
trigger2 = var(48) = 1 ;var(48) = 1为蹲防,2为站防,下面有一样的就不多讲了
statetype = C
physics = C

[State 120, Lo to Hi]
type = StateTypeSet
triggerall = statetype = C
trigger1 = var(59) != 1
trigger1 = command != "holddown"
trigger2 = var(59) = 1
trigger2 = var(48) = 2
statetype = S
physics = S

;---------------------------------------------------------------------------
; STAND GUARD (guarding)
[Statedef 130]
type = S
physics = S

[State 130, Hi to Lo]
type = ChangeState
trigger1 = var(59) != 1
trigger1 = command = "holddown"
trigger2 = var(59) = 1
trigger2 = var(48) = 1
value = 131

[State 130, Stop Guarding]
type = ChangeState
triggerall = var(59) != 1
trigger1 = command != "holdback"
trigger2 = !inguarddist
value = 140

[State 130, Stop Guarding]
type = ChangeState
triggerall = var(59) = 1
triggerall = !inguarddist
trigger1 = enemynear(var(58)),movetype != A ;不处于攻击状态并且背对自己
trigger1 = p2bodydist x < 0
trigger2 = p2bodydist x >= 0
trigger2 = numhelper(31000+ID)
trigger2 = !helper(31000+ID),inguarddist ;确认helper不在guarddist内撤销防御
value = 140
ignorehitpause = 1

;---------------------------------------------------------------------------
; CROUCH GUARD (guarding)
[Statedef 131]
type = C
physics = C

[State 131, Lo to Hi]
type = ChangeState
trigger1 = var(59) != 1
trigger1 = command != "holddown"
trigger2 = var(59) = 1
trigger2 = var(48) = 2
value = 130

[State 131, Stop Guarding]
type = ChangeState
triggerall = var(59) != 1
trigger1 = command != "holdback"
trigger2 = !inguarddist
value = 140

[State 131, Stop Guarding]
type = ChangeState
triggerall = var(59) = 1
triggerall = !inguarddist
trigger1 = enemynear(var(58)),movetype != A
trigger1 = p2bodydist x < 0
trigger2 = p2bodydist x >= 0
trigger2 = numhelper(31000+ID)
trigger2 = !helper(31000+ID),inguarddist
value = 140
ignorehitpause = 1

;---------------------------------------------------------------------------
; AIR GUARD (guarding)
[Statedef 132]
type = A
physics = N

[State 132, 6]
type = ChangeState
triggerall = sysvar(0)
triggerall = inguarddist
trigger1 = var(59) != 1
trigger1 = command = "holdback"
trigger2 = var(59) = 1
value = 130

[State 132, Stop Guarding]
type = ChangeState
triggerall = var(59) != 1
trigger1 = command != "holdback"
trigger2 = !inguarddist
value = 140

[State 132, Stop Guarding]
type = ChangeState
triggerall = var(59) = 1
triggerall = !inguarddist
trigger1 = enemynear(var(58)),movetype != A
trigger1 = p2bodydist x < 0
trigger2 = p2bodydist x >= 0
trigger2 = numhelper(31000+ID)
trigger2 = !helper(31000+ID),inguarddist
value = 140
ignorehitpause = 1

;---------------------------------------------------------------------------
; GUARD (end)
[Statedef 140]
type = U ;Leave state type unchanged
physics = U ;Leave physics unchanged
ctrl = 1

[State 140, Hi to Lo]
type = StateTypeSet
triggerall = statetype = S
trigger1 = var(59) != 1
trigger1 = command = "holddown"
trigger2 = var(59) = 1
trigger2 = var(48) = 1
statetype = C
physics = C

[State 140, Lo to Hi]
type = StateTypeSet
triggerall = statetype = C
trigger1 = var(59) != 1
trigger1 = command != "holddown"
trigger2 = var(59) = 1
trigger2 = var(48) = 2
statetype = S
physics = S

;---------------------------------------------------------------------------
; SGUARDHIT (shaking)
[Statedef 150]
type = S
movetype= H
physics = N
velset = 0,0

[State 150, Hi to Lo]
type = StateTypeSet
triggerall = statetype = S
trigger1 = var(59) != 1
trigger1 = command = "holddown"
trigger2 = var(59) = 1
trigger2 = var(48) = 1
statetype = C
physics = C

[State 150, Lo to Hi]
type = StateTypeSet
triggerall = statetype = C
trigger1 = var(59) != 1
trigger1 = command != "holddown"
trigger2 = var(59) = 1
trigger2 = var(48) = 2
statetype = S
physics = S

;---------------------------------------------------------------------------
; SGUARDHIT2 (knocked back)
[Statedef 151]
type = S
movetype= H
physics = S
anim = 150

[State 151, Hi to Lo]
type = StateTypeSet
triggerall = statetype = S
trigger1 = var(59) != 1
trigger1 = command = "holddown"
trigger2 = var(59) = 1
trigger2 = var(48) = 1
statetype = C
physics = C

[State 151, Lo to Hi]
type = StateTypeSet
triggerall = statetype = C
trigger1 = var(59) != 1
trigger1 = command != "holddown"
trigger2 = var(59) = 1
trigger2 = var(48) = 2
statetype = S
physics = S

;---------------------------------------------------------------------------
; CGUARDHIT (shaking)
[Statedef 152]
type = C
movetype= H
physics = N
velset = 0,0

[State 152, Hi to Lo]
type = StateTypeSet
triggerall = statetype = S
trigger1 = var(59) != 1
trigger1 = command = "holddown"
trigger2 = var(59) = 1
trigger2 = var(48) = 1
statetype = C
physics = C

[State 152, Lo to Hi]
type = StateTypeSet
triggerall = statetype = C
trigger1 = var(59) != 1
trigger1 = command != "holddown"
trigger2 = var(59) = 1
trigger2 = var(48) = 2
statetype = S
physics = S

;---------------------------------------------------------------------------
; CGUARDHIT2 (knocked back)
[Statedef 153]
type = C
movetype= H
physics = C
anim = 151

[State 153, Hi to Lo]
type = StateTypeSet
triggerall = statetype = S
trigger1 = var(59) != 1
trigger1 = command = "holddown"
trigger2 = var(59) = 1
trigger2 = var(48) = 1
statetype = C
physics = C

[State 153, Lo to Hi]
type = StateTypeSet
triggerall = statetype = C
trigger1 = var(59) != 1
trigger1 = command != "holddown"
trigger2 = var(59) = 1
trigger2 = var(48) = 2
statetype = S
physics = S

;---------------------------------------------------------------------------
; AGUARDHIT2 (knocked away)
[Statedef 155]
type = A
movetype= H
physics = N
anim = 152

[State 155, 6]
type = ChangeState
triggerall = sysvar(0)
triggerall = inguarddist
trigger1 = var(59) != 1
trigger1 = command = "holdback"
trigger2 = var(59) = 1
value = 130

于是现在就是比较完善的防御系统了,自己也去试着理解一下吧

好了,今天的课就先到这,如果您有什么问题的话可以私信我,我会第一时间给您答复,或者把问题发到我的邮箱

mizore@kotori.moe, 谢谢大家!
2016.03.1011:15

AI HELPER-1,记忆前版边/后版边离自己的真实距离

Episode 1 记忆前版边/后版边离自己的真实距离

前面的AI部分先暂时休息一下,今天我们来学一个新的HELPER

先不要管这个HELPER是什么,我们先确认一下人物的AIR里面有没有空动画

比如这种的

[Begin Action 9999]
-1,0, 0,0, -1

一般HELPER用这个东西作为ANIM就可以了,看起来没有任何作用的ANIM,实际上用处很大的

那么接下来,直接给出HELPER了

;━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
;AIヘルパー
;━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[Statedef 20000]
type = A
movetype = I
physics = N
anim = 9999
velset = 10,0
ctrl = 0
sprpriority = 1

[State 20000, AssertSpecial]
type = AssertSpecial
trigger1 = 1
flag = NoShadow
flag2 = invisible

[State 20000, NotHitBy]
type = NotHitBy
trigger1 = 1
value = SCA

[State 20000, 間違いで本体が来たら立ちへ移行]
type = SelfState
trigger1 =!ishelper
value = 0

[State 20000, PlayerPush]
type = PlayerPush
trigger1 = 1
value = 0

[State 20000, Projectile]
type = Projectile
trigger1 = time = 0
velocity = 10,0
projID = 20000
projanim = 9999
offset = 0,100000
projstagebound = 0
projedgebound = 100000
projremovetime = -1

[State 20000, VelSet]
type = VelSet
trigger1 = Root,NumProjID(20000) = 0
trigger1 = time > 0
x = 0
y = 0

;-------------------------------------------------------------------------------------
[Statedef 20001]
type = A
movetype = I
physics = N
anim = 9999
velset = -10,0
ctrl = 0
sprpriority = 1

[State 20000, AssertSpecial]
type = AssertSpecial
trigger1 = 1
flag = NoShadow
flag2 = invisible

[State 20000, NotHitBy]
type = NotHitBy
trigger1 = 1
value = SCA

[State 20000, 間違いで本体が来たら立ちへ移行]
type = SelfState
trigger1 =!ishelper
value = 0

[State 20000, PlayerPush]
type = PlayerPush
trigger1 = 1
value = 0

[State 20000, Projectile]
type = Projectile
trigger1 = time = 0
velocity = -10,0
projID = 20001
projanim = 9999
offset = 0,100000
projstagebound = 0
projedgebound = 100000
projremovetime = -1

[State 20000, VelSet]
type = VelSet
trigger1 = Root,NumProjID(20001) = 0
trigger1 = time > 0
x = 0
y = 0

然后在-3下召唤出HELPER

;---------------------------------------------------------------------------
; 画面端検出ヘルパー
;---------------------------------------------------------------------------
[State -3, 画面端検出ヘルパー]
Type = Helper
Trigger1 =!NumHelper(20000+ID)
Trigger1 =!IsHelper
HelperType = Normal
Name = "Edge of Stage Right"
PosType = P1
StateNo = 20000
ID = 20000+ID
PauseMoveTime = 0
Persistent = 0
IgnoreHitPause = 1
KeyCtrl = 0
Ownpal = 1

[State -3, 画面端検出ヘルパー]
Type = Helper
Trigger1 =!NumHelper(20001+ID)
Trigger1 =!IsHelper
HelperType = Normal
Name = "Edge of Stage Left"
PosType = P1
StateNo = 20001
ID = 20001+ID
PauseMoveTime = 0
Persistent = 0
IgnoreHitPause = 1
KeyCtrl = 0
Ownpal = 1

;---------------------------------------------------------------------------
; 関係無いステートに行かないように
;---------------------------------------------------------------------------
[State -3, 関係無いステートに行かないように]
Type = ChangeState
Trigger1 = IsHelper
Trigger1 = IsHelper(20000+Root,ID)
Trigger1 = StateNo != 20000
Value = 20000

[State -3, 関係無いステートに行かないように]
Type = ChangeState
Trigger1 = IsHelper
Trigger1 = IsHelper(20001+Root,ID)
Trigger1 = StateNo != 20001
Value = 20001

原理很简单,HELPER出现时候,发射一个飞行道具,然后飞行道具到达屏幕最边缘的时候会删除,因为HELPER的初速度是10,并且不受摩擦等因素,HELPER做匀速直线运动,这个时候给HELPER设置速度让HELPER停下来,那么HELPER就达到了屏幕的最边缘处

那么HELPER达到了最边缘处,我们现在要用两个var,分别代表屏幕前边缘和屏幕后边缘,以1P朝向右为例,右方向就是前边缘,左方向就是后边缘

;-----------------------------------------------------------------------------
; ステージ前後方端までの距離
;-----------------------------------------------------------------------------
[State -3, ステージ前方端までの距離]
Type = VarSet
Trigger1 =(NumHelper(20000+ID) && NumHelper(20001+ID))
Trigger1 = TeamSide = 1
var(41) = floor(Abs(ifelse(Facing = 1,Helper(20000+ID),RootDist X,Helper(20001+ID),RootDist X)))
IgnoreHitPause = 1

[State -3, ステージ前方端までの距離]
Type = VarSet
Trigger1 =(NumHelper(20000+ID) && NumHelper(20001+ID))
Trigger1 = TeamSide = 2
var(41) = floor(Abs(ifelse(Facing = 1,Helper(20001+ID),RootDist X,Helper(20000+ID),RootDist X)))
IgnoreHitPause = 1

[State -3, ステージ後方端までの距離]
Type = VarSet
Trigger1 =(NumHelper(20000+ID) && NumHelper(20001+ID))
Trigger1 = TeamSide = 1
var(42) = floor(Abs(ifelse(Facing = 1,Helper(20001+ID),RootDist X,Helper(20000+ID),RootDist X)))
IgnoreHitPause = 1

[State -3, ステージ後方端までの距離]
Type = VarSet
Trigger1 =(NumHelper(20000+ID) && NumHelper(20001+ID))
Trigger1 = TeamSide = 2
var(42) = floor(Abs(ifelse(Facing = 1,Helper(20000+ID),RootDist X,Helper(20001+ID),RootDist X)))
IgnoreHitPause = 1

var(41)代表面对的前边缘,var(42)代表面对的后边缘

用DISPLAYTOCLIPBOARD查看效果,如果有DISPLAYTOCLIPBOARD存在的话,直接写APPENDTOCLIPBOARD即可

[State -2, DisplayToClipboard]
type = DisplayToClipboard
trigger1 = 1
text = "V41=%d,V42=%d"
params = var(41),var(42)
ignorehitpause = 1

接下来是效果

X)X[7VQKH$1}0MS{0MG7$`V

可以看到,开局的时候,有两个HELPER正在动
3LI@8[137P})9YD_{G4K{QA

然后他们会到达版边,接下来看下面的DEBUG,(CTRL+D开启DEBUG模式,前提要在DATA/MUGEN.CFG里面找到AllowDebugMode,并修改成AllowDebugMode = 1,然后就可以开启DEBUG)

OINZ6WCL000O8LYY7MCN7}W

HELPER已经到达边缘,然后看到这里的小字,var(41) = 186,var(42) = 393,说明你现在面对的前版边距离是186个像素,后方是393个
09CS8U]2~MA93K]U`7ZETFD

然后我们换个面,var(41) = 94,var(42) = 485,var(41) + var(42) = 186+393 = 94+485 = 579,说明var(41)和var(42)的和为定值,就是全图的长度,该图的长度为579

AI1DI]ZHD}3L{93$I)Q~ES

那么以后只要这么用就可以检测到真实的版边距离了,是不是很方便呢

好了,今天的课就先到这,如果您有什么问题的话可以私信我(只要是我会的,不管什么问题),我会第一时间给您答复,或者把问题发到我的邮箱

mizore@kotori.moe, 谢谢大家!
2016.03.0818:38

AI连段-2 当对手在空中的浮空连

Episode 6-2 连段(当对手在空中的浮空连)

最近可能会比较高产,我也不知道为什么_(:з」∠)_

好了闲话少说下面切入正题,今天要谈的东西会比前一篇复杂一些,浮空连,因为是需要记忆的,所以今天要提到一些变量的使用

首先是达成浮空连段必须要让对手处于type = A并且movetype = H的状态,并且确定juggle(注释①)要够用

注释①. Juggle
MUGEN有一套Juggle系统可以让你攻击已被击上空中的对手或是还躺在地上的对手。我们可以设定juggle points,内定值是15,每次人物进入掉落(fall就例如当人物被打离地面时,并不会恢复可操作状态,而会一直掉落得地面)状态时开始计算。如果人物在空中或是在还躺在地上时被攻击juggle points就会持续下降,而当攻击对方所需要的juggle points比对方剩馀的还要多时,这次的攻击就会miss掉。任何会使对手进入掉落状态的动作,从刚击中对手时juggle point就会开始减少。
例如,人物有5个juggle点数,有一个需要2个juggle point的攻击动作理论上可以让你击中对方两次,直到对手剩下一个juggle point,接下去的攻击会miss。
Juggle系统是为了要防止被无限段连续攻击。

先来用1个var来记下tag,以后要用到,双打用
;------------------------------------------------------------------
[State -3,tag]
type = varset
trigger1 = var(59)
var(58) = IfElse((!EnemyNear,Alive && NumEnemy = 2),1,0)

这样可以记下来近敌为0,远为1,用法就是triggerx = enemynear(var(58)),xxx这样的格式,因为enemynear只能读取0,1,所以用var刚好可以控制,enemynear(0)代表近的,enemynear(1)代表远的

然后再用一个fvar记录下y方向加速度

;-----------------------------------------------------------------------------
; AI用重力加速度計測
;-----------------------------------------------------------------------------
[State -2, yaccel]
type = VarSet
trigger1 = fvar(20) != enemynear(var(58)),const(movement.yaccel)
trigger1 = enemynear(var(58)),stateno != [5000,5210]
trigger1 = enemynear(var(58)),Vel Y != 0
FV = 20
value = enemynear(var(58)),const(movement.yaccel)
ignorehitpause = 1

[State -2, yaccel]
type = VarSet
trigger1 = fvar(20) != enemynear(var(58)),GetHitVar(yaccel)
trigger1 = enemynear(var(58)),stateno = [5000,5210]
trigger1 = enemynear(var(58)),Vel Y != 0
FV = 20
value = enemynear(var(58)),GetHitVar(yaccel);(打撃当てた時のyaccel値)
ignorehitpause = 1

;敵が空中じゃない時
[State -2, yaccel]
type = VarSet
trigger1 = fvar(20) != 0
trigger1 = enemynear(var(58)),Vel Y = 0
FV = 20
value = 0;(打撃当てた時のyaccel値)
ignorehitpause = 1

因为对手通常状态加速度为enemynear(var(58)),const(movement.yaccel),被打如果取得被打yaccel则变为enemynear(var(58)),gethitvar(yaccel),为了方便代入,这里用fvar直接记下来了

先用这两个var,我们来举例子,用JIN的kyo的重75改浮空连为例子

;------------------------------------------------------------------------------
;强七拾五式・改
[State -1, S 75]
type = ChangeState
value = 1250
triggerall = ......
.........

不管什么方法,让75改出招并成功击中对手,2段目的连段可以写在statedef内

一般会有这种东西,原版是这样

[State 1000, ]
type = Varset
triggerall = AnimElemtime(5) >= 0 && AnimElemtime(8) < 0
trigger1 = command = "b"
var(16) = 1

[State 1000,]
type = ChangeState
trigger1 = AnimElemtime(7) >= 0 && AnimElemtime(8) < 0
trigger1 = var(16) = 1
value = 1255

我们改一下,改成这样

[State 1000, ]
type = Varset
triggerall = AnimElemtime(5) >= 0 && AnimElemtime(8) < 0
trigger1 = var(59) = 0
trigger1 = command = "b"
trigger2 = var(59)!= 0
trigger2 = movehit
var(16) = 1

[State 1000,]
type = ChangeState
trigger1 = AnimElemtime(7) >= 0 && AnimElemtime(8) < 0
trigger1 = var(16) = 1
value = 1255

限制了一下command,可以防止AI下CPU乱按自动出招(虽然说对这个没什么用),然后还补了一行,1段目movehit后切换到2段目状态继续连段

接下来对手应该会处在一个浮空状态,在juggle够用的前提下,你可以继续追打对面,注意,juggle的计算是消耗自己的juggle,而不是消耗对面的juggle,千万别弄错了

选一套juggle够用的连招来进行浮空追打

再消耗一个var来记录75改2段目的对手浮空情况

;---------------------------------------------------------------------------
[State -3, VarSet]
Type = VarSet
TriggerAll = var(59)
TriggerAll = RoundState = 2
TriggerAll = EnemyNear(var(58)),StateType = A
TriggerAll = EnemyNear(var(58)),StateType != L
TriggerAll = EnemyNear(var(58)),MoveType = H
Trigger1 = StateNo = 1255 && MoveHit && NumTarget
var(50) = 1

当然也可以在触发75改1段(1250)的那个AI里(见上方)给条件套上var(50) := 1直接赋值,效果一样的,用法就是在每个分支trigger下补一句话
triggerx = var(50) := 1,有几个trigger补几个trigger,trigger1 = var(50) := 1,trigger2 = var(50) := 1....这样写

这样就是可以在这行trigger触发的同时给var(50)赋值1,和上面的道理是一样的,但个人感觉第一种方法好,因为你是第一段记下来的,并不代表第二段一定命中,而第一种方法直接记录第二段命中的情况,以后如果有那种只打1段击飞并要继续连的也可以这样用

然后接下来,比如我要接毒咬这招,我可以估算好x距离和y距离,比如说我举个例子

;百拾五式・毒咬み
[State -1, DOKUGAMI]
Type = ChangeState
Value = 1700
TriggerAll = var(59) = 1
TriggerAll = var(50) = 1 ;前面记录的1255击中了以后就是var(50) = 1
TriggerAll = RoundState = 2
TriggerAll = StateType != A
TriggerAll = EnemyNear(var(58)),StateType = A ;确认对手浮空,并且被打状态
TriggerAll = EnemyNear(var(58)),MoveType = H
TriggerAll = Abs(EnemyNear(var(58)),Pos X - Pos X) < 150 ;x距离,自己要量一下
TriggerAll = P2BodyDist Y = [-20-floor(17*(EnemyNear(var(58)),Vel Y)+(17*(17+1)/2)*fvar(20)),0-floor(17*(EnemyNear(var(58)),Vel Y)+(17*(17+1)/2)*fvar(20))] ;y距离使用方法(注释②)
Trigger1 = Ctrl || (StateNo = [20,22]) || (StateNo = [100,101]) || (StateNo = [120,149])

注释②,分析一下,如果把后面那一串长长的去掉,那就是P2BodyDist Y = [-20,0],但-20这个位置太低,毒咬发生有17帧,这点时间肯定来不及,怎么办呢,刚才提到的记录y方向加速度公式就起作用了,原公式其实变一下很多人就看得懂了
P2BodyDist Y = [-A-floor(B*(EnemyNear,Vel Y)+(B*(B+1)/2)*EnemyNear,GetHitVar(yaccel)),C-floor(B*(EnemyNear,Vel Y)+(B*(B+1)/2)*EnemyNear,GetHitVar(yaccel))],怎么样,是不是有点眼熟?就是原来的下落速度计算公式

用法很简单,A代表B帧以后判定击打的最上距离,C代表最下距离,B代表出招判定帧数,此处多用就能理解了

接下来毒咬2段目,3段目,4段目的连段方法和75改追击的方法相同,就不多提了

现在打完了一套,该消去这个var的值了,怎么消去呢?

分这些情况讨论,1是对手不处于被打状态,2是对手可控制的情况,3是对手处于倒地状态,不算浮空了,4是回合结束

那么接下来的消去应该这么写

[State -3, VarSet]
Type = VarSet
TriggerAll = var(59)
TriggerAll = var(50)
Trigger1 = RoundState != 2
Trigger2 = EnemyNear(var(58)),Ctrl = 1
Trigger3 = EnemyNear(var(58)),StateType = L
Trigger4 = EnemyNear(var(58)),MoveType = A
Trigger5 = EnemyNear(var(58)),MoveType != H
Trigger6 = EnemyNear(var(58)),StateNo = [0,159]
Trigger7 = EnemyNear(var(58)),StateNo = [5100,5300)
;Trigger8 = StateNo = XXXX && MoveHit ;(这里可以记录一些直接用完全部juggle值或者让自己无法继续打浮空连段的技能,比如2D,CD,JCD,独乐属这种的,当然都是因人而异的,比如刚才的毒咬,4段目打完了就可以给他记录下来)
var(50) = 0

然后还有一个问题,我要让AI在浮空连的时候不乱走,乱跳该怎么办呢?前面有提到AI走路,跑步的,现在可以用上了

再加一段文字,加在AI浮空连(var(50) = 0赋值那一段)的上面

;-----------------------------------------------------------------------
[State -3, WALK]
Type = ChangeState
Value = 19 ;AI前走
Ctrl = 0 ;不可控标记,防止AI乱动
TriggerAll = var(59) = 1
TriggerAll = var(50) = 1
TriggerAll = RoundState = 2
TriggerAll = StateType != A
TriggerAll = EnemyNear(var(58)),StateType = A
TriggerAll = EnemyNear(var(58)),MoveType = H
TriggerAll = Time > 0 && AnimTime = 0
Trigger1 = StateNo = 1160 ;(JIN这个1160是75改2段目的落地状态号,在这里记下来即可,1160结束后立即变为走路状态)

接下来75改2段落地结束后立即转为19(走路状态),但并不是Ctrl,所以要把刚才的控制那一栏改一下

把这个Trigger1 = Ctrl || (StateNo = [20,22]) || (StateNo = [100,101]) || (StateNo = [120,149])

改变成Trigger1 = Ctrl || (StateNo = [19,22]) || (StateNo = [100,101]) || (StateNo = [120,149])

这样就包括19这个状态号了,也就是说在19这个状态号里面依然可以出招

当然不能让AI一直走吧,得消去这个不可控状态

[State -3, 停止解除]
Type = ChangeState
Value = 0
Ctrl = 1 ;对手变为默认0状态并且恢复可控
TriggerAll = var(59)
TriggerAll = var(50)
TriggerAll = StateType != A
TriggerAll = StateNo = 0 || (StateNo = [19,22])
TriggerAll = Ctrl = 0
Trigger1 = P2BodyDist Y >= 0
Trigger2 = RoundState != 2

好了,一个完整的浮空连写好了,接下来还可以用自己的思路创造更多的浮空连

其实juggle记录var这种东西,要不是很多连段的就没有必要说了,一般自己算好一套不超过juggle最大值就可以了

接下来的课题可能会较为抽象,如果不懂的要及时来问我

好了,今天的课就先到这,如果您有什么问题的话可以私信我,我会第一时间给您答复,或者把问题发到我的邮箱

mizore@kotori.moe, 谢谢大家!
2016.03.0721:12

AI连段-1 当对手在地面的民工连

Episode 6-1 连段(当对手在地面的民工连)

要说AI最好写的地方,就是连段了,特别是平地民工连,也是写AI里面最简单的一个环节,这里就粗略的举几个例子再提一下该注意的地方吧,想必看我的教程的同学们之前也应该写过或者看过其他作者的AI或者AI教程了,但是要注意一点,连段和连携不同,连段是确认了的,俗称连招,连续技,连携是对面处于防御的状态,属于压制,其他没什么好谈的,唯一要注意的就是看你人物等级的定位写出不同的连段强度,十割,无限之类的连续技最好设置一个开关,免得被喷,尽量自重一点比较好

自己在写之前可以先了解一下这个人物的基本连招,也可以自己手操去摸索一下

PS:除非你是写狂级人物,不然最好限制下能无限的角色的连段,无限打起来其实很难看的,比如98艹的EX模式下无限奈落落,01八神的无限葵花,99BOSS经理的无限搬运,96,97八神的无限屑风之类的

举个例子,我们先看到CMD部分

;強百式・鬼焼き
[State -1, S ONIYAKI]
type = ChangeState
value = 1150
triggerall = var(59) = 0
triggerall = command = "強百式・鬼焼き"
triggerall = statetype != A
trigger1 = ctrl || stateno = 100
trigger2 = stateno = 205 && movecontact
trigger3 = stateno = 215 && movecontact
trigger4 = stateno = 235 && movecontact
trigger5 = stateno = 240 && movecontact
trigger6 = stateno = 245 && movecontact
trigger7 = stateno = 315 && movecontact
trigger8 = stateno = 400 && movecontact
trigger9 = stateno = 410 && movecontact
trigger10 = stateno = 430 && movecontact
trigger11 = stateno = 440 && movecontact
trigger12 = stateno = 300 && Time = [15,29]
trigger13 = stateno = 731

我们能看到一大串trigger,第一个是ctrl也就是可控制的意思,在可控状态下可以输入指令出这一招,后面的stateno = 100也就是说跑步状态下也可以出(100是跑步状态)
trigger2-11是各个stateno触摸(包括防御,击中)后可以输入指令出这一招
trigger12大家注意到了,没有movecontact这个东西,但是有个time = [15,29],也就是说stateno在300这个状态时可以空振(未触摸对手)时输入指令出这一招,也可以触摸到对手输入指令出这招
trigger13后面就什么都没有了,但可以看成stateno - 731 && 1,也就是说,自己在731这个状态号时任意时刻可以输入指令出这招,比前面的要求就更低了

虽然说这么多trigger都可以实现连段,但不是每个都得用的,比如2D确认后你不会出招(除非这招万能追打)是一样的道理

因为是写连段,所以在AI里面,要改为movehit,还可以用movehit = A或者区间[b,c]的形式来指定这招击中后的时间,比如一个多段技,你可以用time >= A来确定打满时间,也可以用movehit >= A来写,但一般都用time >= A来写吧,因为保险点(笑)

所以现在我们给这个技能来写一个连段,举例:

;強百式・鬼焼き
[State -1, S ONIYAKI]
Type = ChangeState
Value = 1150
TriggerAll = var(59) = 1
TriggerAll = RoundState = 2
TriggerAll = Random <= 450
TriggerAll = StateType != A
TriggerAll = EnemyNear,StateType != A
TriggerAll = EnemyNear,StateType != L
TriggerAll = EnemyNear,MoveType = H
TriggerAll = P2BodyDist X = [B,C] ;距离要自己算好XDDD
Trigger1 = StateNo = 205 && MoveHit = [1,6] :由于hittime是硬直结束以后随时间减少的,要赶在hittime结束前触发这招的判定,比如这招发生是6F,205的打击硬直是12F,那么保险的time就是6或者6以下,后面同理
Trigger2 = StateNo = 215 && MoveHit = [1,6]
Trigger3 = StateNo = 235 && MoveHit = [1,6]
Trigger4 = StateNo = 245 && MoveHit = [1,6]
Trigger5 = StateNo = 315 && MoveHit = [1,6]
Trigger6 = StateNo = 400 && MoveHit = [1,6]
Trigger7 = StateNo = 410 && MoveHit = [1,6]

当然也可以加一个条件TriggerAll = EnemyNear,GetHitVar(Hittime) >= 5(虽然这招是6帧,但因为刚还原到0那1帧是不可控的,也可以算进去)

然后再提一点原则,就是带连击补正的连段,尽量用已经有的变量来控制连段,比如修正,hit数这些的,如果有限制就一定要按照限制来写,没有的话,但有连击补正那种的,自己又可以一直连的这种,用TriggerAll = EnemyNear,GetHitVar(HitCount) <= A这样的来写,实在不行自己加一个变量,但不要和原有的冲突,可以用变量或者是概率来控制让连段多样化

新手的话,最好写一点比较简单的系统或者人物的连招,像KOF02UM,KOFXIII,北斗系统这样的,建议不要直接上手,因为连段比较复杂,可以试试简单的,比如KOF98ADV系统这种连段不多的系统,多看看其他人是怎么写的,多领悟一下为什么要这么写,平常看的时候也能学到一些

好了,今天的课就先到这,如果您有什么问题的话可以私信我,我会第一时间给您答复,或者把问题发到我的邮箱

mizore@kotori.moe, 谢谢大家!
2016.03.0521:01

AI投技

Episode 5 投技

想必大家对投技都有一定的了解,投技嘛,不就是所谓的不可防,快,要么0帧指令,那我们今天就来看看投技到底有多么神奇
这里的投技指的是指令投,必杀技或者以上的抓取,带attackdist的那种

先来看一段投技的statedef,(statedef人物来源,JIN氏)

;---------------------------------------------------------------------------
; 投げ
; CNS レベル: 中級

[Statedef 800]
type = S
movetype = A
physics = S
juggle = 0
velset = 0,0
ctrl = 0
anim = 800
sprpriority = 2
facep2 = 1

[State 800, AttackDist] ;不可防御属性在这里,当value = 0时为不可防御
type = AttackDist
trigger1 = 1
value = 0

[State 800, ヒット定義(投げ用)]
type = HitDef
trigger1 = Time = 0
attr = S, NT ;攻撃属性: これは Standing, Normal Throw(立ち通常投げ)(站立通常投)
hitflag = M- ;相手がやられ状態ではない地上の時でしか掴む事が出来ません(对面被打的时候无法使hitdef生效)
priority = 1, Miss ;投げなので優先度を低く、かつmissかdodgeの属性でなければなりません(优先度为1,miss或者dodge属性不可缺少)
sparkno = -1 ;-1にすればヒットスパークが表示しなくなります(无打击火花,打击火花在p1stateno内)
p1sprpriority = 1 ;攻撃が当たった時に自分の画像表示優先度を1にします(p1的图层)
p2sprpriority = 0 ;攻撃が当たった時に相手の画像表示優先度を0にします(p2的图层)
p1facing = 1 ;攻撃が当たった時の自分の向き(攻击命中时候面向对手)
p2facing = 1 ;攻撃が当たった時の相手の向き(同上,但是是对手面向你)
p1stateno = 810 ;自分のステート変更(自己的stateno状态变更,投技用的最多)
p2stateno = 820 ;相手を制御するステートに行かせる(对手的状态变更,变为被打状态)
guard.dist = 0 ;相手がガード状態に移行出来る距離(現在は0なのでガード不可)(检测防御不可,前面有提到过)
fall = 1 ;1にすれば相手がダウン状態になる(对手变为下落状态)
numhits = 0 (不显示hitcombo)

hitflag分类:M(地面),M+(地面被打),M-(地面不被打),A(空中),F(下落),L(倒地),如果没有hitflag默认是MAF
guardflag分类:M(地面,站蹲都可以),H(站立,必须站防),L(蹲下,必须蹲防),A(空防),HL结合在一起叫M,所以HLA又叫MA,如果没有guardflag默认是不可防

[State 170, 無敵]
type = NotHitBy
trigger1 = time = 0
value = SCA,NT,ST,HT
time = 1

[State 800, ステート変更]
type = ChangeState
trigger1 = AnimTime = 0
value = 0
ctrl = 1

所以各位也看清楚了,所谓的投技不可防,其实是不可检测的防御,当然JIN氏这里没有写triggerall = p2stateno = [150,155]这种话,当然JIN氏这样写就算是对面防御了也一样被投...一般的投技大概是对面处于被打硬直的时候无法使hitdef生效,那么投技的AI,很好写,以指令投为例,指令投为0帧,那么先用一个var记录一个东西

;-----------------------------------------------------------------------------
;记录对手起身时候的防投无敌时间
;-----------------------------------------------------------------------------
[State -3, VarSet]
Type = VarSet
Trigger1 = EnemyNear,StateType = L
V = 53
Value = -1 ;(对面倒地时,为了不冲突记录,给var赋值-1)
IgnoreHitPause = 1

[State -3, VarSet]
Type = VarAdd
TriggerAll = var(53) > 0
Trigger1 = EnemyNear,StateType != L
V = 53
Value = -1 ;(随着时间减少,减到0为止)
IgnoreHitPause = 1

[State -3, VarSet]
Type = VarSet
TriggerAll = var(53) = -1
Trigger1 = EnemyNear,StateType != L
V = 53
Value = 12 ;(对面属性不是倒地属性,也就是转到了站立或者其他的属性,记录下12帧的无敌时间)
IgnoreHitPause = 1

现在来说明一下这个var的用途,对手起身一般要经过两个statedef,一个是5110,一个是5120,一个是躺下,一个是起身,从躺下到起身,type都是L,也就是倒地属性,然后5120会过渡到0,也就是站立,这就是起身的过程,但是common1的statedef 5120里面有这样一个东西

[State 5120, 5] ;Can't be thrown right after getting up
type = NotHitBy
trigger1 = AnimTime = 0
value = , NT,ST,HT
time = 12

[State 5120, 6] ;Can't be hit right after getting up (short time)
type = NotHitBy
trigger1 = AnimTime = 0
value2 = SCA
time = 3

3帧的全程无敌,12帧的防投无敌,并且两个无敌之间互相不冲突,所以为了在这个无敌时间内不出投技,所以要用var记录下这段时间,那么AI就可以这么写

[State -3, Throw]
Type = ChangeState
Value = 800
TriggerAll = var(59) = 1
TriggerAll = var(53) = 0 ;对面无敌时间消失
TriggerAll = RoundState = 2
TriggerAll = StateType != A
TriggerAll = PrevStateNo != 800 ;刚才的性能,投完了以后直接转0,AI这么写为了防止持续投的鬼畜现象
TriggerAll = EnemyNear,StateType != A ;对面在地面
TriggerAll = EnemyNear,StateType != L ;对面不能倒地
TriggerAll = EnemyNear,MoveType != H ;对面不能被打
TriggerAll = EnemyNear,StateNo != [30,49] ;起跳的准备动作也有一样的防投动作,所以不能在起跳的时候出,这里30-49是一个猜测
TriggerAll = EnemyNear,StateNo != 105 ;105是back step,对面44撤防时不出投
TriggerAll = EnemyNear,Facing != Facing || EnemyNear,StateNo = [700,999] ;也是一样的,一种猜测,700-999内AB的概率比较大,所以这个时候出比较好
TriggerAll = Ctrl || (StateNo = [20,22]) || (StateNo = [120,149]) ;跑的时候不出指令投
Trigger1 = P2BodyDist X < 15 ;指令投的距离,根据作者设计的不同距离不同
Trigger1 = ifelse(EnemyNear,MoveType = I && (EnemyNear,StateNo = [700,999]),Random < var(59)*30,Random < var(59)*10)
;当对面700-999之间概率翻3倍

当然这里只是个举例,一般一直出指令投的AI会很难看,尽量能不用就不用,指令投一般保命用比较好

如果是带失败动作的必杀指令投之类的,可以限制一下对面的状态号数字来抓对面硬直,TriggerAll = EnemyNear,Movetype = A这样的,比如说加一行TriggerAll = EnemyNear,StateNo < 1000,这样的话,1000以上为必杀的概率比较大,你的指令投判定持续时间一般只有1帧,对面有个无敌就直接给你打掉了,必杀技以上的技能,有没有无敌是不好说的,还是限制一下比较好,只抓轻起手比较好,如果投技有无敌时间,起手部分全为无敌的那种,或者速度超快,基本在5帧或者以下,也一样可以用来抓硬直,条件是TriggerAll = EnemyNear,AnimTime <= -X(X为投技判定触发时间,比如5帧的投技就是EnemyNear,AnimTime <= -5),抓硬直的话,还得注意一点,就是最好加上TriggerAll = EnemyNear,PrevStateNo != [150,155]这句话,因为抓硬直自己必须有TriggerAll = EnemyNear,Ctrl = 0,也就是对面不可控时,然后EnemyNear,PrevStateNo != [150,155]这句话呢,就可能是对面防御出CD反或者AB的时候了,考虑到防御下CD或者AB可能是无敌甚至全程无敌的,最好加上这句话也会保险一点

反正呢,自己确定使用的投技的具体用处,这个投技的价值,多看看其他人的这部分AI,能学到更多东西

好了,今天的课就先到这,如果您有什么问题的话可以私信我,我会第一时间给您答复,或者把问题发到我的邮箱

mizore@kotori.moe, 谢谢大家!

アクセス

オンライン