人生エクソダス

なんてことのない日々のこもごもをつらつらと書き連ねたり連ねなかったりする。

MUGEN小話 7 自分と相手の距離を測る

2020-09-03 16:12:10 | MUGEN

 普通に考えたらp2bodydist xとyでいいだろ、という話。だがこれはかなり大雑把なので、もう少し細かくしたいなら色々考えて記述していくことになる。が結局難しすぎてなんも振れないとなったら本末転倒なのでそこは匙加減になる。


;■P1中心軸からP2接触判定までの距離
[State -1, enemy(var(20))pos x-pos x]
Type = null
; P2距離判定 (P1地上&同一方向)  (P2がめくった時など) 
trigger1 = enemy(var(20)),facing=facing && statetype!=a
trigger1 = fvar(7):= ceil( ((enemy(var(20)),pos x*enemy(var(20)),facing*-1) -(pos x*facing*-1))*-1 )
trigger1 = fvar(7):= fvar(7) -ifelse(enemy(var(20)),statetype=a ,enemy(var(20)),const(size.air.back) ,enemy(var(20)),const(size.ground.back))
; P2距離判定 (P1空中&同一方向)  (P1がめくった時など) 
trigger2 = enemy(var(20)),facing=facing && statetype=a
trigger2 = fvar(7):= ceil( (enemy(var(20)),pos x*enemy(var(20)),facing)+(pos x*facing*-1) )
trigger2 = fvar(7):= fvar(7) -ifelse(enemy(var(20)),statetype=a, enemy(var(20)),const(size.air.front) ,enemy(var(20)),const(size.ground.front))
; P2距離判定 (対面方向)  (通常時) 
trigger3 = enemy(var(20)),facing*-1=facing
trigger3 = fvar(7):= ceil( (enemy(var(20)),pos x*enemy(var(20)),facing*-1)+(pos x*facing*-1) )
trigger3 = fvar(7):= fvar(7) -ifelse(enemy(var(20)),statetype=a, enemy(var(20)),const(size.air.front) ,enemy(var(20)),const(size.ground.front))
ignorehitpause = 1

 これは間桐桜のAIのX座標の距離の測定方法をぱくったやつだが、有効に使えてない気がするので上級者向けなのだろう…。肝は敵が背中を向けている、つまりfacing=enemynear,facing(自分と敵の向きが一緒)のときと普通に面と向かっての移動ではX座標の移動速度が違うということ。シングル戦でも”めくり”の場面ではこうなるだろうが特に考える必要はないものの、タッグ戦では敵が背中を向けているケースは十分考えられるので、そういう相手の移動速度の測り方を考えるならこうなるだろう。


;■相手の摩擦係数
[State -1, friction]
Type = null
trigger1 = enemy(var(20)),statetype=s
trigger1 = fvar(9):= enemy(var(20)),const(movement.stand.friction)
trigger2 = enemy(var(20)),statetype=c || enemy(var(20)),statetype=l
trigger2 = fvar(9):= enemy(var(20)),const(movement.crouch.friction)
trigger3 = enemy(var(20)),statetype=a
trigger3 = fvar(9):= 0
ignorehitpause = 1

;■相手の移動方向・速度
[State -1, Target Vel X]
type = null
trigger1 = 1
trigger1 = fvar(8):=0 ||1
trigger2 = numenemy
trigger2 = fvar(8):= ifelse(enemy(var(20)),movetype=h ,-enemy(var(20)),gethitvar(xvel) ,enemy(var(20)),vel x)* ifelse(facing!=enemy(var(20)),facing ,1 ,-1)
trigger2 = fvar(8):= fvar(8)*fvar(9)
ignorehitpause = 1

 constに立ち・屈み・空中の状態の時のX座標の摩擦係数、つまり時間に比例し減速する値が設けてあり、まあつまり普通は等倍で移動しないよね不自然だよねという話なのでよく考えたら当然だが、これがp2bodydist xで単純に数字を入れるとスカるきっかけにもなる。具体的には「X座標の上限ぎりぎりで技を振ったら、のけぞり距離が長かったりして当たらなかった」という場面だ。いつでも密着状態で技を振るわけではないので、これによって当たる当たらないが変わってくる。面倒ながら相手がのけぞりなのかそうでないのかでX移動速度の測定法が変わる。のけぞり中ならenemy,gethitvar(xvel)、そうでなければenemy,vel xとなる。p2で測る方法がないので式が長くなる。途中の打ち間違いに注意しないとエラーを吐く。ちなみにvel xとgethitvar(xvel)では同じ方向でもプラスマイナスが異なるらしい。

 実は↑式のfvar(8)をenemy,vel xの代わりとして掛け算しても摩擦係数は最初しかかかっていないので実数値より大きくなる。例えば15Fに攻撃判定が出る技のためにfvar(8)*15でX座標を求めても、実際そこまで大きくならない。が参照にはなる。


MUGEN小話 6 「投げ」のダメージ設定をいじる

2020-08-27 08:04:39 | MUGEN

 昔のキャラクターの場合、自分のattack値は100で相手のdeffence値も100が基本なので、投げなどの固定ダメージも単純に数値を入れればよかった。が大会動画もさることながら、自分でAI対戦を眺めているときなどもパラメータ調整をやったうえで遊ぶケースもある。

 そうした場合life・attack・deffenceなどのconstをいじるのが一番手っ取り早い。もちろんpalnoや強化スイッチによる性能調整が施してあるキャラクターならそこを調整してもよいが、特にそうしたものがない場合は基礎数値をいじることが性能調整の最短距離だ。

 普通hitdef、いわゆる打撃技は自分のattack値と相手のdefence値を参照してダメージを出すので、特に記述をいじらなくてもダメージは調整される。しかし投げや打撃ロック後の技など、ダメージの取り方がtargetlifeaddの場合、オプションの記述やvalueの値を省略するとまるでダメージが通らなくなる。具体的には「相手の防御力は加味するが自分の攻撃力は無視した調整後のダメージ」になってしまう。そうならないように工夫の必要がある。


 absolute=1 

 これは相手の防御力を無視する記述でありオプション記述であるので、デフォルトでは省略(absolute=0)してもエラーを吐かないので、書かなければそれでもよい。value値がそのままダメージに乗るので、例えば”相手の防御力を無視した大技”とか”コンボ補正〆の技でダメージを減らしたくない”とかのときに使うといいかもしれない。

 しかし後述の自分の攻撃力を反映させた値と組み合わせて使った場合、「相手の防御力は無視するが自分の攻撃力補正は受ける」というインチキ臭いダメージになってしまうので注意が必要。


 *(const(data.attack)*.01) 

 これをvalue値に乗算してやることで攻撃力を反映させられる。つまりconst(data.attack)が自分のattack値で、それを0.01倍することで、例えばattack=125だったら1.25を乗算することになり、これでようやくattack値をtargetlifeaddに反映させることができる。

 これとabsolute=1を組み合わせると、例えばattack=250みたいなキャラの超必殺投げダメージが冗談抜きで即死レベルになってしまう。例えば600ダメージを想定していたら600*2.5=1500となり、これは普通のキャラのlifeが1000であることを考えればオーバーキルである。

[記述例]
 value = -600*ceil((const(data.attack)*.01)
※damageもtargetlifeaddも整数でないといけないのでfloorかceilで少数の調整は必須。ちなみにfloor,ceilにしなくてもエラーを吐かない。valueが参照された時、値が少数になったときエラーでmugenが落ちる。


 attackmulsetと変数 

 ここまでする必要があるかわからない。しかしattackmulset(攻撃力補正)はhitdefにのみ成立し、targetlifeaddは対象外なので、さらに試合中の状況で攻撃力が変わった時も反映させたいと考えるなら導入を考えてもいいかもしれない。

 具体的には、どっかの監視ステートに

[state -2]
type = varset
trigger1 = roundstate=2 && alive
fvar(1) = 1+(lifemax-life)*.001

[state -2]
type = attackmulset
trigger1 = 1
value = fvar(1)

 ※ は各々のキャラクターの事情に合わせて記述を変える部分

 を書き加えることでattackmulsetの値をfvar(1)と同じにし、あとはtargetlfeaddのvalue値にfvar(1)を乗算すればよい。上の記述の場合、残り体力に反比例して攻撃力が上がるように設計している。(const(data.life)が1000,今のlifeが500のとき、1+500*.001=1.5)

 なので最終的に

[state ]
type = targetlifeadd
trigger1 = stateno=3000 && animelemno(3)=0
value = floor(-300*fvar(1)*const(data.attack)*.01)

 みたいにすれば、「-300の減少値(300ダメージ)*攻撃力補正*attack値倍率補正」をかけた投げやロック技のダメージにすることができる。floor(少数切り捨て)にするのはtargetlifeaddのvalueが少数入るとエラーになる(int型)から。

 ちなみにabsolute=1にしてないのは、ここまで攻撃力補正盛り盛りにして防御力無視するのはやりすぎだと思ったから。導入の是非はあなたが検討してください。

 

attackmulsetを検知して凶悪化するキャラクターがいるので、attackmulsetと同等のtriggerとvalueにしたfvarを設定して、hitdefのdamageとtargetlifeaddのvalueの値の各々にこのfvarをかけてやれば結果的に同じことになる。
 上の記述例の場合、targetlifeaddはそのままだが、hitdefのdamageが240,10とかだったら240*fvar(1),10*fvar(1)みたいにしてやればいい。


mugen小話 5 LifeAddをRoundState=2以外で使われると困る

2020-01-16 22:18:56 | MUGEN

 lifeaddというステコン(ステートコントローラ)を使って、体力を回復させることができる記述がある。

 普通は特殊技みたいなものに変数が設定されていて、その変数が時間で減っていき最終的に所定の値になったとき特殊技が解かれるようなしくみのなかで、lifeaddがついていればその特殊技中に回復して、技が終われば回復の記述も偽になるようにしてある。普通は。その特殊技もlife=0(ライフがゼロ)とかalive=0(生存フラグ=偽)だと解除されるようにしていないと、同様の現象が起こったりする。

 特によくあるのが強化スイッチとかPalNoがいくらで、例えば7P(ENTERを押しながらLとか)なら強化みたいに設定してあるキャラだと、そのキャラを選んだ時点でその効果が得られるので試合が終わるまで体力回復が続くし、roundstate=2さえない場合勝敗判定中とか勝利ポーズ中まで体力回復する。これがpoweraddなら勝敗の影響は間接的だが、lifeaddだった場合キャラがKOされても回復が続くのでさらに厄介になる。timeoverのコールが出てからしばらく勝敗判定が付かないような、俗に猛抗議と言われるような”キャラがいつまでたっても静止しない状況下”で勝敗判定がひっくり返ることもある。

 またタッグ戦で判定になった場合も同様だが、タッグ戦の場合特に厄介なことがある。
 KOしたキャラにダメージを与える方法などないし、ちょっと考えて作られたAIはKOされたキャラをずっと殴るようなことはしないのでノータッチになる。その誰も触らないKOされたキャラがずっとライフ回復するのだ。判定になり、1P3Pと2P4Pのライフの差で勝敗判定をするとき、その今までぬくぬくとライフ回復していたKOキャラのライフによって判定勝ちしてしまうことがあるのだ。

 なのでlifeaddを常時扱うようなステコンを使うなら、roundstate=2(戦闘ラウンド中)とかにして、roundstate=3(勝敗判定中)までいかないようにしたり、KOされたら止まるようにalive=1とかlife!=0とかにして、普通に生きてる状態なら動くようにしておくのがいい。そして判定の状況下で厳密に判断させるために、余計な回復をしないように[state -2]でlifesetを用いて、alive=0のときはlifeを0にする記述を加えておけばより確実だ。

[state -2]
type = lifeadd
triggerall = roundstate=2 && alive && life!=0
trigger1 = gametime%3=0 ;ゲーム時間を3で割って余りが0のとき
value = 1
absolute = 1
ignorehitpause = 1

[state -2]
type=lifeset
trigger1=!alive
value=0

みたいにしてあればいいのでないかと思う。
まあそんなものをぶっちするから強化スイッチなのだという考え方もあるだろうが。


おつかい氏のマミさん 5 空中通常技

2020-01-16 03:59:34 | MUGEN

ジャンプ小キック stateno=430

 発生4F、持続6F、のこり4F。

 X軸72までのびる横に強い攻撃判定を使って、空中ダッシュから地上の相手にさしこんでいける。空中牽制にしてもいいが縦はかなり薄いので、上部を狙うなら中P中Kか大Kのほうがいいし、下部は中Pか大Pのほうがいい。

 発生が早いことを用いて、遠くの敵がロンディーネとかの銃撃を喰らった時に追撃したい、が走っていては間に合わないしジャンプ大Pは発生9Fだし…となったときにとりあえず低空ダッシュ小Kを当ててジャンプ大Pにつなげてもいい。相手が空中やられになったらジャンプ中P→ティロ・シエーロが入れられて着地してもまだのけぞり中の見込みがある。なので横につっこむときにこれを振るのがおすすめ。


ジャンプ中パンチ stateno=410

 発生7F、持続4F、のこり10F。

 のぼりジャンプで空中牽制したり、くだりで押しつけができるくらいには分厚いY軸判定と、ほぼ前後80をカバーする横判定が持ち味。これによって、後ろにいる相手にも当てられることは当てられる。ただその場合ジャンプ大Pしか入らない。

 この技の特徴は、そのまま中P入力で出るティロ・シエーロ(stateno=415)とさらにそこから出るティロ・フィアンマ(stateno=417)。これらはX軸300超の遠距離銃撃なので相手を一方的に刺すことができる。シエーロが発生18Fと遅い。前空中ダッシュ中に振ればX移動はそのままなので前に長い硬直時間と共にふわっと飛ぶことになってしまう。シエーロの隙消しにフィアンマを重ねて逃げ、ロンディーネをつけて降りるかまだ飛ぶかを選べるのが、空中での機動性能の良いマミさんの強みだろう。

 レガーレ中の敵を下りジャンプ、というか近距離でレガーレ後にふわっと降りるときにジャンプ中P→シエーロが入る。シエーロのもう一つの強みはair.hittimeが60あること。ダウン属性じゃない攻撃で一番のけぞる攻撃なので、低空で当てた場合ちょっと走って立ち小Kで拾えて空中コンボに行けるという強みがあり、実はジャンプ中Pはそこまでのけぞり時間がなくティロ・フィナーレをキャンセルしても受け身を取られるリスクがあるので、シエーロをキャンセルしてティロ・フィナーレできれば安定して入れることができる。ただ攻撃判定がX75からなので、普通のキャラ幅が30ちょいなことを考えれば密着状態だと外れる。ティロ・フィナーレでキャンセルするためにシエーロを当てるなら、上とか後ろに飛んでジャンプキャンセルして相手との位置を適度に離す必要がある。


ジャンプ大パンチ stateno=420

 発生9F、持続4F、のこり18F。

 下部にやたら強い攻撃判定と、ジャンプ中Pほどではないにしても前後に伸びる攻撃判定が特徴。攻撃判定が出てからの残りフレームが18とかなり長いので、guard.ctrltimeが20なことを考えると-1~+2というところ。ガードされて不利ではないが何もできず、また空中の相手に当てた時にダウン属性になるので追撃がなくなる。着地まで長い場合ロンディーネをつける猶予はあるが、低空だとそれもできない。ジャンプ大Kにキャンセルできないので実質これが〆になる。下に強い判定を使って、空中コンボに移行するも自分が高すぎるときにこれではたき落としてとりあえず〆るのも手段だろう。

 地上の敵に当てた場合硬直より先に着地するので、ジャンプ大P→立ち小K→…とコンボに移行できる。マミさんのAIのプリセットには普通のジャンプで押しつけるタイプとハイジャンプでめくり空中バックダッシュからこれを当てる2パターンの運用が登録されている。わずかに落下ダメージがあるが、当てに出来る程ではない。

 攻撃属性MAFDつまりダウンした敵に当てることができる。これの他が足払いのしゃがみ大Kだが、ダウンした相手に思った以上にしゃがみ大Kが当たってくれない。なので低空ダッシュでジャンプ大Pを狙った方がまだ当たる。曲芸だが、空中ダッシュ中にロンディーネを出しておきジャンプ大Pを当てた場合、のけぞりの間にロンディーネが当たって空中やられになるので、そこから立ち小Kからの空中コンボをもう一回できる。つまり画面端だったら理論上これでループできる。


ジャンプ大キック stateno=450

 発生11F、持続4F、のこり6F。

 発生の関係で小技に潰される可能性があるものの、前に大きな攻撃判定があり上の攻撃判定は小さめなものの喰らい判定との距離がけっこう遠いので、敵が空中で自分が対空する場合は他のどの技よりリスクが少ない。当てた場合ダウン属性なのでジャンプ大P同様なにもないが、guard.ctrltime24つまりガードされて+15~+18Fなのでコンボにできないことを除けば出し得。ロンディーネを付けて普通に着地してもいいし空中動作をまだしてなかったら空中ダッシュとかもできる。

 ジャンプ大Pと違って地上の相手に当ててもダウンさせてしまうので普通の追撃はできない。一方var(13)=2の制約(のけぞり時間が1Fになる)の対象外なので、地上通常技→ジャンプキャンセル→空中ダッシュ→空中通常技が普通に入る唯一の存在。とはいえ他の技がダウン取らないから永久ループできてしまうので制限をかけているというだけのことだから、強制ダウンするこの技の硬直時間を減らすメリットがないというだけのことだ。

 かなり強引に、ジャンプ大K→マギアブート(stateno=900)→ティロ・フィナーレと4ゲージ使ったコンボもできるが、補正でコンボの〆にTFを持ってきた場合ダメージが425から大幅に減る可能性も大きい(とはいえ20ヒット目だったら0.8倍程度なので半減とかに比べれば全然たいしたことない。3ゲージ技のダメージが削れるというゲージ管理的に痛いかもしれない)し、ほほ一瞬であれこれしないといけないのでレガーレ拘束からティロ・フィナーレを生出しした方が楽。ましてダウン中の相手にティロ・フィナーレが当たることになっているが起き上がりの無敵時間に被さってスカる場面も多いので、安定して当てたいなら自分も敵も高いところで大Kが当たったとかでなければ狙い所がない。


MUGEN小話 4 mugenといえばエラー

2020-01-13 19:51:49 | MUGEN

 AI入れただけでこうなったりすることがある。


random<var(59)*(p2bodydist x)

 random…0~999を抽選、var(59)…AIレベル。人によって使うvarは違うがだいたい59でつくってる、p2bodydist x…X軸の距離 によって、X軸として遠ければ遠いほど期待値があがるように記述したつもり。が、この記述でまれにエラーを吐くことがある。
 p2bodydist のXもYも整数を返すものと認識していることがある。他の方が作ったAIを見ても、p2bodydist x=[65,120]のように、整数が入っている事が常だからだ。だが数字でこうして指定していない場合、例えば(p2bodydist x)とすれば現在の相手とのX軸の距離が分かるが、これが小数が入ることがあるようだ。
 そして、randomは整数しか入らない。なので、【整数しか入らない】<【小数出るかも】という式になりエラーになることがある。

 なので、↑の式で組むなら、random/var(59)<p2bodydist のようにどちらも少数出るかもしれないようにしておけばいい。


等号不等号の順番が違う

 p2bodydist x=[65,120]くらいの長さならこれでいいが、p2bodydist x+const(size.ground.front)+enemy,gethitvar(xvel)*10=[65*const(size.xscale),120*const(size.xscale)]とか、これよりもっと長くなったりすると、横が画面に収まらなくなって、何が書いてあるかわからないとか、直そうと思った時にこの()は何を区切っているのか…と意味が分からなくなることがある。なので長い式になるならばらした方が一画面に収まってわかりやすいということがある。
 ↑の式の内容だと、p2bodydist x>=65 && p2bodydist x<=120という事になるが、この><=の順番が違うということでエラーを吐くことがある。><は左側、=は右側と覚えておけばいい。この程度の事なのに、うっかり間違った記述をしたものをコピペしている場合の修正は、そらもう手間よ。


括弧の数が違う

 たとえばこれ
((p2bodydist x=[128,179])&&(p2bodydist y=[51,90]))||(((p2bodydist x=[180,231])&&(p2bodydist y=[91,130]))||((p2bodydist x=[232,283])&&(p2bodydist y=[131,170]))
 エラーです。どっかの(が多いからエラーによって読み込み不可になります。
 なので一行の長さが長すぎる式とか、(((が3つも4つもある式をつくると自分でもわけがわからなくなるので、例えば( () ) || ()のように半角スペースを使ってこの括弧はここからここまでというのをわかりやすくしておいた方が、直す時に便利。


全角スペースが入っている

 older than...というエラーが出た時、意外にどこにいれたかわからないような全角スペースによってエラーを吐くことがあり、そもそも視認できないというとても厄介な代物。もし最初から全角スペースとわかっていれば を検索すればいいだけだし、まあ視認に関しても全て選択して謎の青いだけのブロックがあったらそれを消せばいいが、まあうっかりこんな を入れてしまったらちゃんとその時に消すようにしよう。

 あと説明文を入れたのに行頭に;(コメントアウト)を打ち込まずにいればやっぱりエラーを吐く。