モンスト風ゲームのアルゴリズム


今回のプログラムは、モンストの一部の再現ではありますが、かなりのボリュームになっています。

そこで、モンストの中でも重要なところに絞って、モンスターを引っぱる操作とモンスターの反射処理の解説をメインにしていきたいと思います。

 

事前知識として、数学のベクトルの概念が必要です。

ベクトルの基本的な部分のみですが、全く知らないという方には難しいと思いますのでご注意を。

 

なお、これはあくまで、モンストを再現した自作プログラムの解説です。

株式会社ミクシィ様の本家モンストのアルゴリズムではありませんのでご注意ください。

1.モンスターの移動


モンストでは、引っぱったモンスターが移動し、敵に当たることで攻撃していきますよね。

プログラムでは、少しずつ移動している様子を何枚も描画していくことで、モンスターが動いているように見せています。

すなわち、フレームレートの仕組みです。

 

フレームレートについてはこちらで解説していますので、分からないという方は見てみてください。

今回のプログラムでは、15ミリ秒 = 0.015秒に1フレーム呼び出すようにしています(1 ÷ 0.015 = 約66.7fps)。

 

また、移動にはベクトルの概念を利用しています。

モンスターは、縦横斜め、自由自在に画面上を移動できますよね。

プログラムでは、斜めへの移動を、横方向(x方向)と縦方向(y方向)に分けることで実現しています。

 

いわゆる、ベクトルの成分というものです。

例えば、右上斜め30度方向の移動は、(x, y) = (√3, 1)で表すことができます。

 

つまり、 x : y = √3 : 1の割合で移動していけば、右上斜め30度方向に移動しているようにみえます。

2.モンスターの操作


モンスターは、引っぱる操作で発射することができますね。

この引っぱる操作は、モンスターを発射する方向を決めるために行っています。

発射方向は、先ほど説明したベクトルの概念を利用すれば、簡単に求めることができます。

 

引っぱり始めた座標を(px, py)、引っぱって放した座標を(fx, fy)、
進むべき方向(進行方向ベクトル)を(rx, ry)とします。

進行方向ベクトルは引っぱった方向の真逆の方向(逆ベクトル)となるので、

 

rx = -1 * (fx - px) = px - fx,ry =  -1 * (fy - py) = py - fy

となります。

また、ここで重要なのが、モンスターが移動するスピードです。

モンストでは、どれだけ強く引っぱっても、弱く引っぱっても、スピードは同じという特徴があります。

進行方向(rx, ry)は簡単に求められましたが、大きさが異なるため、rxとryの値をそのまま速度ベクトルとしてはいけません。

 

そこで、モンスターのスピードに合わせて、ベクトルを伸び縮みさせます。

例えば、速度ベクトルを(sx, sy)、その大きさをspeedとします。

speedは、ステータスで既に決められている値であり、(sx, sy)を求めることが目的です。

 

まず、進行方向ベクトル(rx, ry)の大きさrlを求めます。これは、三平方の定理で簡単に求められますね。

rl = √(rx^2 + ry^2)

このrl とspeedの比率rateを計算し、その比率分だけrxとryを伸び縮みさせてやればよいのです。

rate = speed ÷ rl  sx = rx * rate  sy = ry * rate

 


 

よって、1フレームごとに、x方向へはsxずつ、y方向へはsyずつ移動していけばよいというわけです。

なお、今回のプログラムでは、「MsMouseListener」クラス内のメソッドで、上記の操作に関する処理しています。

3.モンスターの反射


モンスターは、壁に衝突すると反射します。

 

反射ではまず、反射方向を考える必要がありますが、これはとても簡単です。

理科や物理等で習ったかもしれませんが、反射では入射角と反射角が等しくなるという性質があります。

さらに、モンスターの移動はベクトルの成分で行っているため、修正がとても容易です。

 

左右の壁に衝突した場合は、x方向のベクトルを逆にし、上下の壁に衝突した場合は、y方向のベクトルを逆にするだけです。

左右反射:new_sx = sx * -1,new_sy = sy  上下反射:new_sx = sx,new_sy = sy * -1

 

意外と難しいのがここからで、どういう条件でこの反射処理を行うかです。

本来は、モンスターが壁に触れたら=画面の端の座標にきたら、反射処理を行えばよいはずです。

しかし、このプログラムでは、1秒間に何フレームもの処理が呼び出されており、その分だけ衝突の判定も行われています。

すると、場合によっては、反射処理を行った次のフレームでも壁に触れていると判定され、また反射処理が行われるという事態が発生してしまうのです。こうなると、壁付近でモンスターが振動したり、挙句の果てには場外へ吹っ飛んだりしてしまいます。

 

そこで、今回のプログラムでは、モンスターの速度ベクトルの方向を利用して、反射処理を行うか判断しています。

例えば、右の壁に衝突した時、x方向の速度ベクトルは右方向になっているはずです。そこで、右端に到達した、かつ、x方向の速度ベクトルが右向きならば、反射処理をすることにします。これなら、反射後は速度ベクトルが左になるため、反射処理がされずに済みます。

 

また、反射タイプは敵に当たっても反射をしますね。

この反射処理は、敵を長方形として考え、壁の反射処理と同じようにしています。

丸みを帯びていると、反射面が垂直ではなく斜めになってしまい、ベクトルの変換が複雑になるのでこのようにしています。

 

なお、今回のプログラムでは、「PalyerAttack」クラス内の「actionPerformed」メソッドにて上記の反射処理を行っています。

4.モンスターの減速


モンスターは、移動、壁との反射、敵との接触等で減速していきます。

詳細な減速率については公開されていないようなので、割と適当に計算しています。

 

どこかで見たサイトでは、減速率は次のような大小関係らしいです(しかし、どのサイトか忘れてしまいました、すみません)。

移動中 < 壁反射 < 反射タイプが敵と当たる < 貫通タイプが敵と当たる

減速の割合は分からなかったので、こちらで適当に決めました。

 

なお、今回のプログラムでは、「PalyerAttack」クラス内の「speedDown」メソッドで上記の減速処理を行っています。