2020年02月02日

  生存報告兼mugenの加速度と予想到達距離予測AIについて

最近ツイッターを利用せずdiscord内の仲間内とだけ会話しながら製作してるし、
その製作自体も生放送をやらなくなったせいで自分が生きてるかどうかもわからないと思う。
とりあえず近況としては生きてます。
逆式の不具合修正をメインに大ポートレイト用の絵の作成をやっています。

久々の記事投稿が単なる生存報告なのもあれなんで、次回更新の逆式にも搭載させる予定の
相手の次F予想到達距離をやるにあたってのレポートを纏めてみたいと思います。

この話をする前に、基本的な事は算数と数学と理科学になります。
算数知識を要するので用語は調べながら御覧ください。

【概要】
プレイヤー間の距離と速度を考慮して発動間隔を調整すると微妙に狂います。
これはmugenでの速度の処理が現実的な速度処理をされていない事に起因します。
速度による到達距離まで計算に含めるとなると精密な計算を要求されます。
ここではそんな問題を解決してしまおうという事です。

【速度の機序】
速さとは、その位置に到達するために物体がその位置の方向へ進んでいく力を差します。
ポイントとして、「速度」「距離」「移動後到達地点」「時間」の4つがあります。
日本の義務教育ではこれを「道のり」「速さ」「時間」の3つで「みはじ」として教わった事と思います。
更に、物体は速度方向への干渉が存在しない状況では速度が一定となる「等速直線運動」、
地上にいる場合は地面からの「地上摩擦」を受けること、大気圧からの「空気抵抗」を受けること、
地球の質量が持つ引力である「重力」、
物体が回転している場合は地球の自転に影響される「コリオリ効果」を受けること…
と上げていけば速度は沢山の環境的要因による影響を受けます。

ここで話を現実からmugenに戻しますが、mugenにおける速度方向への環境による干渉は

1.等速直線運動(physics=N)
2.地上摩擦(physics=Sで、立ちとしゃがみで摩擦係数を変えられます)
3.重力加速度(空中状況でのphysics=AとステコンのGravity)


の二点になります。空中摩擦はかからない為、空中では等速直線運動をします。
なのでX軸移動に重力は影響されません。ここでは1.2.だけを考えます。
そして1フレームでの速度の処理順は

0.フレームの開始、def-3-2-1の処理
1.個別ステートでの加速度(Velset,VelAdd,VelMul,TargetVelAddセット)、加速度をセット
2.全プレイヤー全ヘルパーの処理終了
3.physicsでの設定を反映。
 立ち・しゃがみ摩擦係数をphysics=SならX軸加速度に掛ける、
 重力加速度をphysics=AならY軸加速度に掛ける。
4.加速に則った移動地点へ移動
5.フレーム終了、次フレームへ


となります。学識豊富で賢い方ならこれだけでもう分かったと思います。
自分は考え方だけが分かってて後はどう組むかで四苦八苦しました。

【実際の計算~式を考える~】
ここまで見た利口な方なら分かると思いますが、かなり厄介で面倒です。
しかし不可能ではありません。1つのステコンだけで計算可能な問題です。
前提条件として式を立てて計算をやるのはヘルパーです。
本体は相手の行動後のVelをリダイレクトで見ても処理順の問題から安定しないためです。

まずは要点を纏めます。
1.加速度は個別ステートでのVel系のステコンによって変動する
2.速度のセットは個別ステートで、移動は全プレイヤー全ヘルパーの個別ステート終了後
3.physicsのセットは全プレイヤー全ヘルパーの個別ステート終了後から移動処理前
4.physicsが掛かってるかどうかはリダイレクトでの確認が不可能

という点です。特に4番目がとても厄介です。

さて、ここで速度による移動の処理の式は2つ考えられます。
aを速度、bをphysicsでの係数、cを相手プレイヤーの移動距離とします。

1. b≠0の場合 a*b = c
2. b=0の場合 a = c


です。等速直線運動の場合は次Fで速度分移動するという事になります。
さて、式だけ見れば単純ですがこれはmugenなので全て変数に置き換え直す必要がありますが
単純に置き換えができないものが一つあります。
それが厄介だと言ったphysicsです。これを解決するためには計算によってphysicsがかかっているかどうかを証明する必要があります(メンドくさいから%nした方が早いよ)
まずは式を立てるのに必要な要素を全部変数に置き換えてやります。

fvar(0) = Enemy,Vel X(相手の速度)
fvar(1) = Enemy,StateType=S*Enemy,Const(Movement.Stand.Friction) (立ち状態なら立ちの摩擦係数)
fvar(1) = Enemy,StateType=C*Enemy,Const(Movement.Croutch.Friction) (屈み状態なら屈み摩擦係数)

式に必要なのはこの2つですが、ついでなので距離まで求めるのに必要なのも変数にいれましょう。

fvar(2) = Enemy,ScreenPos X(相手の画面上のX軸地点)
fvar(3) = Root,ScreenPos X(本体の画面上のX軸地点)

さて、ここまで置き換えをやりましたがfvar変数を使っている理由は通常変数では小数点計算で狂いが生じてしまうためです。必ずfvar変数を使いましょうここから先の計算はfvarでないとエラー落ちします

そして最後に式を組み上げるにあたって重要なのが加速度です。
加速度とは初速という瞬間から時間が経過し、経過した後の速度の上昇・下降の具合のことです。
これを把握するには「前Fでの速度」と「現在フレームでの速度」を見ます。

fvar(4) = Enemy,Vel X(前F速度)
fvar(5) = Enemy,Vel X(現在F速度)

fvar(4)は計算用ステコンの終了後に更新し、fvar(5)は計算用ステコンの前に更新します。
こうする事で前Fと現在Fとで差を出しながら変数にセットできます。
最後に加速についての補足ですが、加速にはパターンが存在します。
1.速度が変更される(Vel系が実行されて大幅な更新)
2.一定的に加減速(VelAddで一定値をセットし続ける)
3.階段状に加減速(VelMulやphysicsなどで係数セット)
です。2と3は見破る方法があり、これを漸化式計算というもので推理します。
漸化式計算とは比例していく数を数式によってどれだけ上がってるのか見るというものです。
例えば1,2,4,8,16と上がっていたら現在速度×2で上がっている事になります。
1,3,5,7,11,13と上がっていたら現在速度+2で上がっている事になります。
前例のような状態の比例を等差数列と呼び、後者のような状態の比例を等比数列と呼びます。
この計算は比例式がわからんけど数列は分かるという状態で次の数字を割り出す、その為に現在の数字になにをやれば比例式として成立するかを見ます。
当然比例式にならない式には当てはめられません。

【実際の計算~式を組み上げる~】
では式を組みたいと思います。
まず最初に加速度を解決します。これが解決しないとphysicsを解決できませんので。

fvar(6) = ( ( fvar(4)/fvar(5) ) / fvar(1) )
fvar(6) = ifelse( (fvar(5)*fvar(6) = fvar(4)) (fvar(4)/fvar(5)) ,0 )
fvar(7) = ifelse( fvar(6)=0 ,fvar(4)/fvar(5) , fvar(6) )

と上から順番にセットします。上から順に
(現在加速度/摩擦係数) / 初速度 = 予想加速度 とします。次の計算が重要で
求めた答えを実際に計算して現在VelXになるか確認、ならなければリセット、なったら保存しています。リセットの理由は等差でも等比でも無い式であった場合は係数は掛かっていないと判断する為です。
最後の式がリセットしたなら現在加速度 / 初速度 = 予想加速度 この式ならどんなセットのされ方でも加速度を割り出せます。

さて、加速度の割り出しはできました。次にphysicsを解決します。

fvar(6) != 0の場合
fvar(7) = fvar(6)*fvar(5)*2/(1-fvar(5))+(fvar(4)-fvar(6)*fvar(5)/(1-fvar(5)))*(1-fvar(5)**2)/(1-fvar(5))

この式は
加速度×摩擦係数×総フレーム数/(1-摩擦係数)+{初速度-加速度×摩擦係数/(1-摩擦係数)}×{1-摩擦係数^総フレーム数}/(1-摩擦係数)}
です。しかしこの式で確認している数列は前Fと現Fの2つの数列だけです。なのでフレーム数は2という定数なので*2や**2は定数としてセットしています。最初から常にフレームを監視するなら変数に経過F数とその時のVelXを保存しておいて計算すると出来るでしょう。
さて、最後に実際の移動距離を図ります。

sysfvar(0) = ifelse( (fvar(3)>fvar(2)) ,(fvar(3)-fvar(2)) ,(fvar(2)-fvar(3))
sysfvar(1) = sysfvar(0) - fvar(7)

まず本体と相手の距離を図ります。Screenpos Xを利用すればP2リダイレクト不具合(プレイヤーヘルパーを本体と誤認してヘルパーにもリダイレクトする不具合)を解決できます。
その間合いから割り出した速度を引いてあげます。移動は現在速度分の移動になり、
最初の計算で相手と自分の距離は求めたのでその値が小さくなれば接近、大きくなれば離開となります。


【実際の計算~応用~】
最後に、実際に自分が利用しているステコン1個でやるこの計算を出します。
コピペで出力しましたがもし流用したければ自分で書き直してください。

[State -2, 予想到達距離判断]
type = VarSet
TriggerAll = PlayerIDExist(Root,Var(19));Root,var(19)=敵本体のID
; 対象が次Fでどの位置にいるのかを確認しなくてはならない。
; ヘルパーは行動順の問題で相手のState記述の実行後かつ反映前を確認できるので、
; 相手の行動前に位置・速度・摩擦・重力の4点から次Fの到達地点を予想しておく。
trigger1 = 1 || fvar(34) := ifelse( (Root,ScreenPos X>PlayerID(Root,var(19)),ScreenPos X) ,(Root,ScreenPos X-PlayerID(Root,var(19)),ScreenPos X) ,(PlayerID(Root,var(19)),ScreenPos X-Root,ScreenPos X) )
trigger1 = 1 || fvar(35) := ifelse( (PlayerID(Root,var(19)),StateType=S) ,(PlayerID(Root,var(19)),Const(Movement.Stand.Friction)) , fvar(35))
trigger1 = 1 || fvar(35) := ifelse( (PlayerID(Root,var(19)),StateType=C) ,(PlayerID(Root,var(19)),Const(Movement.Crouch.Friction)) , fvar(35))
trigger1 = 1 || fvar(35) := ifelse( (PlayerID(Root,var(19)),StateType=A) ,(PlayerID(Root,var(19)),Const(Movement.Yaccel)) , fvar(35))
; ①相手と自分の距離を把握すると同時に相手の状態からくる摩擦と重力加速を見る
trigger1 = 1 || (fvar(39)!=0)&&(fvar(37):=((PlayerID(Root,var(19)),Vel X)/fvar(35))/fvar(39));(現在加速度/摩擦係数) / 初速度 = 予想加速度
trigger1 = 1 || (fvar(39)!=0)&&(fvar(37):=ifelse( fvar(39)*fvar(37)=(PlayerID(Root,var(19)),Vel X) ,((PlayerID(Root,var(19)),Vel X)/fvar(39)) ,0 ));求めた答えを実際に計算して現在VelXになるか確認、ならなければリセット、なったら保存
trigger1 = 1 || (fvar(37)=0)&&(fvar(38):=((PlayerID(Root,var(19)),Vel X)/fvar(39)));現在加速度 / 初速度 = 予想加速度 この式なら等差でも等比でも加速度が出てくる
; ②相手の加速度を見極める。
; この計算を確立させるには前F加速度と現F加速度が必要になる。
; この計算で用いる漸化式計算は1を基準として計算を始めるが、VelSetの関係で1が存在せずいきなり始まる。
; この為、VelSetされたその瞬間を基準として次Fでの加速を見極めなければならない。
; この計算の上でどれにも当てはまらない場合はVelSetがなされたとして算出不能とする。
; なぜここまで面倒な事をしているのかというと、摩擦と予想移動距離だけは全ステート処理実行後に行われる為で、
; 最終的な計算を漸化式でまた求めるが、階差だけは3F分の計測が必要で、そんなに待ってるとぶっ飛ばされてしまうため。
trigger1 = 1 || (fvar(37)!=0)&&(fvar(38):=fvar(37)*fvar(35)*2/(1-fvar(35))+(fvar(39)-fvar(37)*fvar(35)/(1-fvar(35)))*(1-fvar(35)**2)/(1-fvar(35)))
; ③求めた加速度から推進距離を求める。
; 式は摩擦が加わっている場合は加速度×摩擦係数×総フレーム数/(1-摩擦係数)+{初速度-加速度×摩擦係数/(1-摩擦係数)}×{1-摩擦係数^総フレーム数}/(1-摩擦係数)}
; 摩擦が加わっていない場合は速さ×時間=距離だが、時間が1Fなので速度をそのままセットする
trigger1 = 1 || fvar(39) := PlayerID(Root,var(19)),Vel X
; ④相手の前F横軸速度を更新
sysfvar(1) = fvar(34) - fvar(38)
; 現在の間合い-次Fの相手の予想移動距離 = 次Fの相手との間合い
ignorehitpause = 1



総評:めんどくさかった。これ書き纏めるだけで4時間掛かったし、これ考えるだけで2,3日使ったわ。二度とやりたくねえ。


masaki1994 at 16:19コメント(0) 

コメントする

名前:
URL:
  情報を記憶: 評価:  顔   星
 
 
 
プロフィール

Unknown37564

MUGENというフリーゲームとか、後は色々適当に書いてます。

最新コメント
アクセスカウンター
  • 累計:

アクセスカウンター

    記事検索
    • ライブドアブログ