4.追尾弾について

はじめに…
今回の解説では微妙に数学とか物理とかの話が出てきます。
mugenに直接関係する部分は赤字で書きますので、そちらだけ参照してくださっても結構です。
なお、数学や物理に関する部分は、解釈が間違っている可能性もありますので参考程度に…。

まず、追尾弾の概念ですが、
「毎フレームで相手の位置に向き、ベクトルを指定」ということになります。
したがって、まずは「相手の位置に向き、ベクトルを指定」することが必要になります。

ここで必要な処理は二つ。
・相手の方向へ向く。
・相手の方向への速度を求める。


一つずつ見ていきます。

1.相手の方向へ向く

この処理を行うために必要なsctrlは次の二つ。
angleset と angledraw です。


anglesetは角度を指定するsctrlです。
このsctrlのvalueに自分が向くべき角度を入れることになります。
なお、ここでの角度は度数法での度(360度を一周とする、普段私たちが使う度)になります。

この角度を求めるのはatanという記述内の指定した記述により行うことが出来ます。
atanはアークタンジェントといい、対象となる点Pのx座標とy座標を与えることにより
原点からPへの角度θが求まります。
なお、ここでの角度はラジアン角度です。
これをmugen的に解釈するならば、
自分の軸位置(上記における原点に該当)から相手の軸位置(同点Pに該当)へのx距離とy距離をatan内に与えてやることにより、
自分の軸位置から相手の軸位置への角度θが求まることになります。
これをさらにatanを使って書き表すならば、
atan(P2dist x/P2dist y)

ただ、このままではanglesetに入れる角度としては正確なものになりません。
atanで求まった角度はラジアン角度、angleで使用する角度は度数法の角度だからです。
したがって、次はこのatanで求まった角度を度数法の角度に置き直してやる必要があります。
それを数式にするならば…
((atan(P2dist x/P2dist y)*180)/pi)-(90*ifelse(P2dist Y<0,-1,1))
前半部分はラジアン角度の度数法への置き換え、後半部分は前半部分だけだと90度のズレがあるためその修正になります。
後はこの数式を元に色々弄っていくことになります。
なお、このままを使用すると、P2distが0の時は落ちますので、その対処が必要になります。

私が実際に使用したものとしては…
[State 7000, VarSet]
type = VarSet
trigger1 = time = 0
fvar(2) = ifelse((root,P2dist x=[0,1]),1,ifelse((root,P2dist x=[-1,0]),-1,root,P2dist x))


[State 7000, VarSet]
type = VarSet
trigger1 = time = 0
fvar(3) = ifelse((root,P2dist y=[0,1]),1,ifelse((root,P2dist y=[-1,0]),-1,root,P2dist y))+20


[State 7000, VarSet]
type = VarSet
trigger1 = time = 0
var(0) = ceil((atan(fvar(2)/fvar(3))*180)/pi)-(90*ifelse(root,P2dist Y<0,-1,1))


ヘルパで使用したためP2distにはrootをつけ、
atanに入れる値が0の場合と式が長すぎる場合は落ちるため、atanにで使用する値をvarに入れ、varに入れる際に分岐。
+20の部分はP2dist Yへの補正ですね。

後はこの値をanglesetのvalueに入れ、anglesetすることにより相手の方へ向くことができます。
(この部分は実際にやってみていただいた方が早いです)


2.相手の方向への速度を求める。

これも相手との距離を見ることにより求めることになります。
まずは物理としての計算方法から。

運動する方向(角度θ)が分かっている場合、その角度θを用いて角度θへ1進む際のx方向への運動量、y方向への運動量をそれぞれ求めることが出来ます。
いわゆるベクトルの分解というやつですね。
xベクトルの場合はcos(θ)、yベクトルの場合はsin(θ)といった式を用います。
ここでの角度はラジアン角度になります。
したがって、手順1で用いた式を用いるならば、
xベクトル…cos(atan(fvar(2)/fvar(3))-(.5*pi*ifelse(root,P2dist Y <0,-1,1)))
yベクトル…sin(atan(fvar(2)/fvar(3))-(.5*pi*ifelse(root,P2dist Y <0,-1,1)))

となります。
ここで求まった値は角度θへ1進む際、xとyへそれぞれどれだけ進んでいるか…ですので、
あとはこの値に飛び道具としての速度をそれぞれ掛ければ速度が求まります。

私が実際に使用したものとしては…
[State 7000, VelSet]
type = VelSet
trigger1 = time = 0
x = abs(10*cos(fvar(0)))
y = -10*sin(fvar(0))

(fvar(0)は上記のcos内及びsin内に記したものと同じです)
これで「角度θへ10の速度で進む」ことになります。

なお手順1と手順2で指定した角度は、実際の角度を正確に入れると相手が真上にいた時とかの撃ち出しがとんでもない方向になったりするので、
一旦varに入れ、ifelseで上限下限を定めておくと良いでしょう。
[State 7000, VarSet]
type = VarSet
trigger1 = time = 0
ignorehitpause = 1
fvar(0) = atan(fvar(2)/fvar(3))-(.5*pi*ifelse(root,P2dist Y <0,-1,1))

[State 7000, VarSet]
type = VarSet
trigger1 = time = 0
ignorehitpause = 1
fvar(0) = ifelse(fvar(0)>(.33*pi),.33*pi,ifelse(fvar(0)<(-.33*pi),-.33*pi,fvar(0)))


上のsctrlで角度をvarに入れ、下のsctrlで上限下限を定めています。

以上二つの手段により、「相手の方向へ向いて指定した速度で飛ぶキャラクター」を作成することが可能になります。


3.ホーミング

では、ホーミングするにはどうすればよいかというと…
上記処理を毎フレーム行うことで可能になります。
毎フレーム相手の位置を参照し、その方向へ向いてその位置へ飛ぶことによりホーミングするというわけです。

ただ、相手の位置が極端に変わると軌道が急激に変化し見た目に違和感を感じるかもしれません。
その他、「当たるまで延々追い続ける」といった事態も生じかねません。
したがってその場合は角度をvarに入れ、VelSetする前に現在の速度と角度から求まった今回設定すべき速度との差を取り、
やはりifelseによって大きすぎる(小さすぎる)場合は一定の範囲内に値を限定した上で、
先ほど求めた差をVelAddしてあげれば、急激な軌道の変化は避けられます。

また、この場合はtime=0に簡単なprojを発しただけでは攻撃判定がついてきてくれません。
hitdefで処理するのも手ですが、juggleやhit関連が本体管理にならない点で私はあまりオススメしません。
projremovetime=1に設定したprojをtrigger1=1等により毎フレーム発射することで、擬似的な飛び道具に見せかけることを私は良く用います。
多段ヒットさせたい場合は、contactごとに特定varをaddし、特定varが規定数に達したらprojを発射しているhelperをdestroyselfすれば、
多段飛び道具に見せかけることができます。
ただしこの場合、唯一proj同士の相殺にだけは対応できませんが、この点はhitdefで攻撃判定を処理しても同様だと思います。


4.補足

手順1においてangledrawにより角度を変更しましたが、angledrawを使うとairにて設定した透過指定が無効化されます。
Dos版においては対処法はありませんでしたが、Win版ではTransというsctrlが追加され、
このsctrlを使うことでairとは別個に透過指定をすることが可能です。
私も自分が使う範囲内でしか試していませんが、
[State 7000, Trans]
type = Trans
trigger1 = 1
trans = add

このような記述により、airにおけるAと同じ効果が得られます。
なお、このtransの命令においてSやDを指定する処理をした場合処理が重くなるとの事なので
これらの使用はひかえるべき…との事です。


最後に、手順1、手順2、手順4は私が製作しました超サイヤ人3孫悟空の気弾にて実際に使用した処理です。
参考にしたい場合はご自由にご覧になってください。

■トップへ戻る