AI制作の手引き その5「ステートの話」 by ADI 21:29 2011/03/05 20:35 2011/03/05 書き足し。まだ途中。でもいらんきもする。 21:56 2010/05/12 初め 何度も繰り返しますが >このサイト<の>このページ(ステートコントローラー一覧(HTML))<と >このページ(トリガー情報の索引)<を参考にしています。 |
.ステートを読む 序で話してあった通りステートというのは、 [Statedef ****];という記述でステート記述を始めて、 ステートコントローラーを書いていくことで作られます。 そしてユニットのステートを別のステートからそのステートを飛ばし、 そこに書かれているステートコントローラーの処理を行わせるというものです。 つまりステートに書かれている事を読んでいけば、 どんな処理をしているのか、どういうステートなのか分かるわけです。 ( よほど凝ったキャラでもない限り。 ) .AIの為に読みたいステート とは言うもののAIで読んでおきたいステートはその一部。 ChangeState(+SelfState)にVarset関連の他には、 HitDefやProjectile、VelSet関係やPosadd関係が主。 細かい所ではPauseにSuperPauseと、HelperにTarget***関係などなど。 それらさえ読めれば、AIで知っておくべき処理も分かります。 もちろん複雑で細かいことは最初に書いたサイトで調べてもらうとして。 |
.実例 その2で軽く説明し、変更を推奨した箇所などを例に説明します。 しっかり読めている方には不要ですし、 説明の順番的にも今更ですが、一応説明させていただきます。 ;----------------------------( common1.cnsから引用 ) ; Walk 歩行ステート ( Ctrl=1&&StateType=Sの時レバーを左右に傾けると内部処理で20番ステートへ移動します。 ) [Statedef 20] ←20番ステートの宣言 type = S ←StateDefのオプション:StateTypeをS(立ち)に指定 physics = S ←StateDefのオプション:Physics(物理状態)をS(立ち)に指定 sprpriority = 0 ←StateDefのオプション:SprPriority(表示優先度)を指定 > 上二つのオプションによりTime=0で「立ち状態」のStateTypeと物理状態になります。 > 読み込んだ時に指定されるだけでStateTypeSetなどで設定しなおせば変えられます。 ・> StateTypeがSになることで、StateTypeで参照する情報がSになります。 ・> 物理状態がSになることで、横の速度へ「立ち状態の摩擦係数」が乗算されます。 ・・> 物理状態とVelの関係は、その4の「■Velの性質」にて説明しています。 [State 20, 1] ←ステートコントローラー宣言 type = VelSet ←命令:VelSet(速度を設定する) trigger1 = command = "holdfwd" ←条件:コマンド「レバーを前に倒している」 x = const(velocity.walk.fwd.x) ←命令内容:X速度を定数「前へ歩くXの速度」にする > 「向いている方へレバーを倒している場合」「前の方へ歩く速度を設定する」という命令です。 > Const(*パラメータ名*)は、CNS設定で設定した数値を参照する情報です。 ・> この場合、CNS設定[Velocity]のWalk.Fwdの数値になります。 [State 20, 2] ←ステートコントローラー宣言 type = VelSet ←命令:VelSet(速度を設定する) trigger1 = command = "holdback" ←条件:コマンド「レバーを後ろに倒している」 x = const(velocity.walk.back.x) ←命令内容:X速度を定数「後ろへ歩くXの速度」にする > 「向いている方とは逆にレバーを倒している場合」「後ろの方へ歩く速度に設定する」という命令。 > 細かいことは同上。 [State 20, 3] ←ステートコントローラー宣言 type = ChangeAnim ←命令:ChangeAnim(アニメーションを変更する) triggerall = vel x > 0 ←条件ALL:速度が0よりも高い(前へ進んでいる) trigger1 = Anim != 20 && Anim != 5 ←条件1:アニメが20番でも5番でもない trigger2 = Anim = 5 && AnimTime = 0 ←条件2:アニメが5番でアニメを表示し終えている value = 20 ←命令内容:アニメを20番に変更 > 内容を説明すると「前へ進んでいるとき」に「前進アニメ」へアニメを変更する命令です。 ・> ただし「振り向きの最中(アニメ5番)」では変更せず、振り向いた後「前進アニメ」になります。 ※> アニメーション(Anim)の番号はステート番号と異なるので混同に注意。 [State 20, 4] ←ステートコントローラー宣言 type = ChangeAnim ←命令:ChangeAnim(アニメーションを変更する) triggerall = vel x < 0 ←条件ALL:速度が0よりも低い(後ろへ進んでいる) trigger1 = Anim != 21 && Anim != 5 ←条件1:アニメが21番でも5番でもない trigger2 = Anim = 5 && AnimTime = 0 ←条件2:アニメが5番でアニメを表示し終えている value = 21 ←命令内容:アニメを21番に変更 > 内容は「後ろへ進んでいるとき」に「後進アニメ」へアニメを変更する命令です。 ・> ↑と同様に振り向き最中や既に後進アニメの場合は変更しません。 > AIではその2で説明した通り、Command条件にAIフラグが無い条件を加え、 > AIフラグとそれぞれの状況を条件とした記述を加える形で制御します。 ;---------------------------- 大体覚えたらcommon1.cnsを開いて記述だけの状態から、 どういう記述であるのか思い出しながら確認してみましょう。 ;----------------------------( common1.cnsから引用 ) ; Jump Start ジャンプ始動ステート ( Ctrl=1&&StateType=Sの時レバーを上に傾けると内部処理で40番ステートへ移動します。 ) ↑で説明しているところは大体割愛。 [Statedef 40] ←40番ステート宣言 type = S physics = S anim = 40 ←StateDefオプション:アニメを40番に変更 ctrl = 0 ←StateDefオプション:Ctrlを0に。 sprpriority = 1 [State 40, 1] type = VarSet ←命令:VarSet(Var変数を設定する) trigger1 = Time = 0 ←条件:このステートに入った時にだけ sysvar(1) = 0 ←命令内容:sysvar(1)へ0を代入。(※sysvarはシステム用の変数) [State 40, 2] type = VarSet ←命令:VarSet(同上) trigger1 = command = "holdfwd" ←条件:向いている方にレバーが倒れてるなら sysvar(1) = 1 ←命令内容:sysvar(1)へ1を代入。 [State 40, 3] type = VarSet ←命令:VarSet(同上) trigger1 = command = "holdback" ←条件:向いていない方にレバーが倒れてるなら sysvar(1) = -1 ←命令内容:sysvar(1)へ-1を代入。 > ステートへ入った直後にシステム用の変数をリセットし、 > 上ジャンプ・前ジャンプ・後ろジャンプなのかを代入する処理です。 [State 40, 4] type = VelSet ←命令: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))) ↑ 命令内容:0で上ジャンプの、1で前ジャンプの、-1で後ろジャンプのX速度でX速度を設定する。 y = const(velocity.jump.y) ←命令内容2:Y速度を定数「ジャンプのY速度」にする。 > IfElseとは「条件式」と「条件式が真(0以外)の場合の数値」「条件式が偽(0)の場合の数値」を > まとめて記述することができるというものです。ifelse(条件式,真なら,偽なら)。 ・> 上記の場合、sysvar(1)=0なら定数「上ジャンプのX速度」を、そうでないなら ・> sysvar(1)=1であるか確かめ、=1なら定数「前ジャンプのX速度」を ・> sysvar(1)!=1なら定数「後ろジャンプのX速度」を指定するという仕組みです。 > ifelseを使わない場合、分岐の数だけステートコントローラーを増やす必要があるのですが、 > ifelseを使うことで一つのステートコントローラーで処理してしまうことが可能なわけです。 [State 40, 5] type = VelSet ←命令:VelSet(同上) trigger1 = AnimTime = 0 ←条件:アニメーションの表示が終わったら trigger1 = prevstateno = 100 ;RUN_FWD ←条件:直前が100番ステート(ダッシュ)の時 trigger1 = sysvar(1) = 1 ←条件:レバーを前側に倒した状態のジャンプなら x = const(velocity.runjump.fwd.x) ←命令内容:X速度を定数「ダッシュジャンプのX速度」にする。 [State 40, 6] type = ChangeState ←命令:ChangeState(ステートを変更する) trigger1 = AnimTime = 0 ←条件:アニメーションの表示が終わったら value = 50 ←命令内容:50番ステート(ジャンプ最中ステート)にする ctrl = 1 ←命令内容オプション:Ctrlを1にする。 > 処理を分かりやすく言うと「アニメを40番、Ctrl=0にし」「ジャンプする方向をsysvar(1)に代入」、 > 「アニメーションが終了した時に速度を付けてジャンプ最中ステートへ移行」する、というもの。 > AIではsysvar(1)の代入をCommandでなく別制御する形です。 > ちなみに50番ステートはこちら。 ;---------------------------- ; Jump Up ジャンプ最中ステート [Statedef 50] ←50番ステート宣言 type = A physics = A ←StateDefオプション:Physics(物理状態)をA(空中)に。 > 物理状態とVelの関係は、その4の「■Velの性質」を参照。 > ここでも補足しておくと、物理状態がA(空中)のときに地面へ着くと > MUGEN側の処理で着地ステート(52番ステート)へ移行します。 [State 50, 1] type = VarSet trigger1 = Time = 0 sysvar(1) = 0 [State 50, 2] type = ChangeAnim trigger1 = Time = 0 value = ifelse((vel x)=0, 41, ifelse((vel x)>0, 42, 43)) ↑ 命令内容:上ジャンプか・前ジャンプか・後ろジャンプかでアニメを変更している。 [State 50, 3] type = ChangeAnim trigger1 = Vel y > -2 ←条件:上昇終了辺り〜下降中 trigger1 = SelfAnimExist(anim + 3) ←条件:下降アニメが存在するかどうか。 persistent = 0 ←命令内容オプション:同じ処理を連続では行わない設定。 value = Anim + 3 ←命令内容下降アニメに変更する。 > 補足しておくとxx(*引数数値*)形式の情報は、中に計算式や条件式があっても機能します。 > 手引きのその2でも書いてあったとおり、Var(10+Var(9))なんて記述も可能なわけです。 ;---------------------------- トリガー表などと見比べてある程度読み込んでいけば、 どんな処理を行っているのか、トリガー表を見までもなく分かってきます。 ;----------------------------( common1.cnsから引用 ) ; GUARD (start) ガード始動ステート ※ガードの処理そのものはMUGEN側の処理で行われています。 [Statedef 120] ←120番ステート宣言 type = U ;Leave state type unchanged ←オプション:StateTypeを変更しない physics = U ;Leave physics unchanged ←オプション:Physicsを変更しない [State 120, 1] type = ChangeAnim trigger1 = Time = 0 value = 120 + (statetype = C) + (statetype = A)*2 ↑ 命令内容:StateTypeに合わせて異なるガード待機アニメを表示させる。 > 条件式は真なら1、偽なら0を返すため、条件式を()で囲うことで > StateType=Cなら120+(1)+(0)*2で121に、StateType=Aなら120+(0)+(1)*2で122に、 > CでもAでもなければ120に設定する、という記述方法が可能なわけです。 [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 ↑ 3つの内容:StateTypeに合わせてPhysicsを設定する。 > 文字列を指定する所には専用の文字列以外記入できない模様。 > そのためそれぞれ別々にという面倒な方法になっている。 [State 120, Hi to Lo] type = StateTypeSet trigger1 = statetype = S && command = "holddown" statetype = C physics = C ↑ 内容:立ち状態でレバー下の場合、StateType,Physicsを屈み(C)に。 [State 120, Lo to Hi] type = StateTypeSet trigger1 = statetype = C && command != "holddown" statetype = S physics = S ↑ 内容:屈み状態にレバー下でない場合、StateType,Physicsを立ち(S)に。 [State 120, 5] type = ChangeState trigger1 = AnimTime = 0 ←条件:アニメーション終了時 value = 130 + (statetype = C) + (statetype = A)*2 ↑ 命令内容:Statetypeに合わせて異なるガード待機ステートへ移行する。 [State 120, Stop Guarding] type = ChangeState trigger1 = command != "holdback" ←条件:レバーが後ろに倒されていない。 trigger2 = !inguarddist ←条件:相手からのガード可能範囲に入っていない。 value = 140 ←命令内容:ガード終了ステートへ >120,130~132ステートなどで攻撃を受けるとガード扱いになり、 > ガードの処理やガードくらいステートへの移行が行われます。 >「Hi to Lo」と「Lo to Hi」の処理は、地上ガードステートのほぼ全てにあり、 > AIでガード方向を完全制御したい場合はそれらもAI用に改変する必要があります。 私信:-1ステートでStateType変えちゃうならStateNo=[120,159]が必要かと。 ;---------------------------- 次はKFMの攻撃ステートの一つから例を引用し細部解説。 ;----------------------------( Kung Fu Manのkfm.cnsから引用 ) ; Strong Kung Fu Palm ストロング・カンフー・パーム ; CNS difficulty: medium CNSの難しさ・中々 [Statedef 1010] ←1010番ステート宣言 type = S movetype= A ←オプション:MoveTypeをA(攻撃中)に。 physics = S juggle = 4 ←オプション:いわゆる「お手玉」を制限するための値 poweradd= 120 ←オプション:ゲージが(Power)0.12本分増加 velset = 0,0 ←オプション:速度を0に設定 anim = 1010 ctrl = 0 sprpriority = 2 [State 1010, 1] type = PlaySnd ←命令:音声を再生させる trigger1 = Time = 9 value = 0, 3 ←命令内容:sndファイルのグループ0の3番を再生。 > 技を振った時に音です。 [State 1010, 2] type = PosAdd ←命令:座標を移動させる trigger1 = AnimElem = 2 ←条件:アニメが2枚目になった時。 x = 20 ←命令内容:X座標に20を加算する。(前へ20動く) [State 1010, 3] type = PosAdd trigger1 = AnimElem = 3 trigger2 = AnimElem = 13 x = 10 [State 1010, 4] type = PosAdd trigger1 = AnimElem = 5 x = 5 [State 1010, 5] type = VelSet ←命令:速度を設定する trigger1 = AnimElem = 5 x = 4 ←命令内容:X速度を4に設定する > このPosAddとVelSetはアニメに合わせてユニットを動かす命令です。 [State 1010, 6] ;Opponent near 近い場合の攻撃判定 type = HitDef ←命令:攻撃判定の設定する。 trigger1 = AnimElem = 5 trigger1 = p2bodydist X < 40 ← 条件:相手との距離が近め attr = S, SA ←命令内容:攻撃属性:S(Stand),SA(SuperAttack)、立ち・必殺技・打撃 animtype = Hard ←命令内容:リアクションアニメの強弱:強攻撃のくらいアニメにする damage = 90, 4 ←命令内容:ダメージ:通常設定の直撃で90,ガード削りが4 priority = 5 ←命令内容:攻撃優先度:同設定が4以下の攻撃との相打ちに勝る設定 guardflag = MA ←命令内容:ガードフラグ:立ち屈み空中全部でガード可 pausetime = 15,15 ←命令内容:ポーズタイム:攻撃が命中した時の両者の停止時間(自分,相手) sparkxy = -10,-60 ←命令内容:スパーク座標:攻撃が当たっときにスパークを表示する座標 hitsound = 5,4 ←命令内容:ヒットサウンド:攻撃が直撃したときの音 guardsound = 6,0 ←命令内容:ガードサウンド:攻撃がガードされたときの音 ground.type = Low ←命令内容:地上くらいの上下:下の方へ攻撃をくらったアニメにする ground.slidetime = 12 ←命令内容:ヒットバック時間・相手が攻撃を受けて後ずさる時間:12F ground.hittime = 15 ←命令内容:ヒットよろけ時間・相手が攻撃を受けて動けない時間:15F ground.velocity = -4,-3.5 ←命令内容:地上ヒットの加算速度・ヒット時相手のVelに設定されます air.velocity = -4,-3 ←命令内容:空中ヒットの加算速度・同上:速度X-4,Y-3で吹き飛ぶ。 fall = 1 ←命令内容:倒れ落下フラグ:吹き飛んだ後、受身動作をしないとダウンする設定 [State 1010, 7] ;Opponent not near 近くない場合の攻撃判定 type = HitDef trigger1 = AnimElem = 5 trigger1 = p2bodydist X >= 40 ← 条件:相手との距離が近くない attr = S, SA animtype = Hard damage = 85, 4 ←命令内容:ダメージ:近めより5少ない priority = 4 ←命令内容:攻撃優先度(設定は1〜7まで):近めより1少ない guardflag = MA pausetime = 15,15 sparkxy = -10,-60 hitsound = 5,4 guardsound = 6,0 ground.type = Low ground.slidetime = 12 ground.hittime = 15 ground.velocity = -7 ←命令内容:地上ヒットの加算速度:近めと違い、Y速度が無いので浮かない air.velocity = -4,-2.5 ←命令内容:空中ヒットの加算速度:Y速度が+0.5違う >近めと違い、威力が弱めになっている設定。こちらはFall設定もない。 [State 1010, 8] type = ChangeState trigger1 = AnimTime = 0 ←条件:アニメーションが終わったら value = 0 ←命令内容:立ちステート(0番ステート)へ移行する ctrl = 1 ←命令内容オプション:Ctrlを1にする > PosAdd,PosSetとVelSet,VelAddの細かい違いは、 > PosAdd,PosSetは「処理を実行した時に」「一度の処理で一度だけ動かす」命令ですが、 > VelSet,VelAddは「速度を変更する」だけの命令で、すぐには動かない代わりに > 速度がある限り毎フレームMUGEN側で移動処理を行ってくれます。 > また速度は物理状態(Physics)の係数やVelSet,VelAddなどで変更されない限り継続します。 > 大きな違いだけ説明するとこんな感じ。 ・> PosAddは「決まった時に決まった数値分、動かしたいとき」に使う命令 ・> VelSetは「しばらくの間ある程度の数値分、動かしたいとき」に使う命令 ;---------------------------- ちなみに、攻撃判定の大きさやくらい判定の大きさは アニメーションを指定しているAirファイルで設定されています。 例に出したのは簡単なステートばかりです。 キャラクターによってはChangeStateやらVarやらHelperやらが混在する複雑なものもあります。 ただ極めて特殊なキャラクターでもない限り、書かれていること自体は例と同じようなことで 例と同じように命令と条件と命令内容をよく読んでいけば仕組みは分かるはずです。 なお、キャラの内部処理のCommand以外をいじるのは直接改変行為にあたるので制作者本人以外は注意。 |
|