数学に強くなって、
綺麗なプログラムを組みたい・・・!
はじめてシューティングゲーム作るとき、弾の挙動ってどう動かせばよいか悩まれる方も多いと思います。
今回は、簡単な弾の挙動から始めて、最終的には「ターゲットの方向へ撃つ処理」をどのように計算するのかを説明します。
使用言語:javascript
※ゲームライブラリとして、enchant.jsというライブラリを使っています。2022年現在、使う人がほぼいない化石ライブラリとなっているのですが、計算式だけ見てもらえれば幸いです。
自分の使用予定の言語が違う場合でも、わかるように心がけているので、参考になれば幸いです。
ページ下では、実際に動作しているプログラムとソースコードが閲覧できるようになっているので、理解の一助になれば幸いです。
固定方向に撃ち続ける
See the Pen 上に向かって球を打つ処理 by ほっとフクロウ(作っちゃうおじさん) (@hothukurou) on CodePen.
ソースコードを大きく見たい方は右上のCODEPENと書かれている部分をクリックしてください。CODEPENページにジャンプして編集することができます。
また、左側のソースコードの数値を書き換えて、右下の「Rerun」ボタンを押すことで、弾の挙動を自由に変更することができます。お試しください。
・弾生成ソースコード(弾の挙動はこの関数内に書かれているので抜粋)
//TIPS:引数のspriteを発射元にして赤四角の弾を作成して返す関数
function createBullet(sprite) {
const bullet = new Sprite(8, 8); //width:8px,height:8pxのSpriteを設置
bullet.moveTo(sprite.x, sprite.y); //発射元の座標に弾を合わせる
bullet.backgroundColor = "#ff0000"; //赤に設定
bullet.onenterframe = function() { //毎フレーム毎に実行する関数
this.y -= shotSpeed; //真上に向けて移動する
if (bullet.x = -30 || bullet.x = 430 || bullet.y = -30 || bullet.y = 430) { //画面外か判定
this.parentNode.removeChild(this); //画面外なら自身を削除する
}
}
return bullet; //上で作成したspriteを返す
}
onenterframeは毎フレーム毎に行われる処理を記述するイベント処理です。
(今回は30fpsなので、1/30[s]毎に中身の関数が処理されます。)
createBullet()関数内で生成したbulletが毎フレーム毎にthis.y-=shotSpeedされています。
これにより、毎フレームにshotSpeed分だけ上方向に進むことができます。
bulletのy座標が毎フレームでマイナスされるので、上に進んでいくということですね。
プログラムを本格的に理解したい人向けに、詳細説明を書きました。
興味のある方は下の欄をクリックしてください。流し見したい方は流してください。
なんとなくわかるゲームプログラムの知識
座標系
左上が原点で、右方向がX方向、下方向がY方向になっています。
「Y軸は上向いてないのか!」と思った方もいらっしゃるとは思いますが、
画像処理ではこの座標系がよく使われています。
enchant.js の知識
下図にenchant.js特有の概念を説明します。今回enchant.jsは画像表示目的で使っています。
今回は弾の挙動を知ることが目的なので、ざっくりとした説明にとどめます。
この図を何となく見ておけば、後でソースコードを見るときに理解の一助になるかと思います。
移動角度を設定して撃ち続ける
See the Pen 角度を決めて球を打つ処理 by ほっとフクロウ(作っちゃうおじさん) (@hothukurou) on CodePen.
・弾生成ソースコード
//TIPS:引数のspriteを発射元にして赤四角の弾を作成して返す関数
function createBullet(sprite) {
const bullet = new Sprite(8, 8); //width:8px,height:8pxのSpriteを設置
bullet.moveTo(sprite.x, sprite.y); //発射元の座標に弾を合わせる
bullet.backgroundColor = "#ff0000"; //赤に設定
bullet.rad = (sprite.time / shotInterval) * rotateInterval; //現在の時間から角度を生成
bullet.onenterframe = function () {
this.x += Math.cos(this.rad) * shotSpeed; //移動角度のcosがX軸移動方向
this.y += Math.sin(this.rad) * shotSpeed; //移動角度のsinがY軸移動方向
if (bullet.x = -30 || bullet.x = 430 || bullet.y = -30 || bullet.y = 430) { //画面外か判定
this.parentNode.removeChild(this); //画面外なら自身を削除する
}
}
return bullet; //上で作成したspriteを返す
}
bullet.radに移動角度を格納し、cos,sinによりx,yの移動量を決定しています。
bullet.rad=(sprite.time/shotInterval)*rotateInterval;
でbulletの移動角度を設定しています。
sprite.timeは毎フレーム毎に加算されます。shotIntervalは何フレーム毎に弾を生成するかを決めています。
sprite.time/shotIntervalで発射された弾数が計算できるので、この値にrotateIntervalを掛け算することで、発射角度を弾数ごとに変更しています。
移動角度this.radに対して
Math.cos(this.rad)でX軸の移動量の単位ベクトル
Math.sin(this.rad)でY軸の移動量の単位ベクトル
を求めることができます。
単位ベクトルとは「長さが1のベクトル」のことです。
つまり、現在の座標値にこの単位ベクトルを足すと移動角度方向に1pxずれるということです。
これがとても都合の良い話で、この単位ベクトルに移動量shotSpeedを掛け算することで
shotSpeed[px]分だけ移動させることができます。
this.x+=Math.cos(this.rad)*shotSpeed; //移動角度のcosがX軸移動方向
this.y+=Math.sin(this.rad)*shotSpeed; //移動角度のsinがY軸移動方向
shotSpeedの値を変更すれば、移動量を自由に調節できるので、とても便利です。
いよいよ三角関数の本領発揮です!
クリックした位置に設置されるターゲット方向に撃ち続ける
See the Pen クリックした位置に向かって打つ処理 by ほっとフクロウ(作っちゃうおじさん) (@hothukurou) on CodePen.
・弾生成ソースコード
//TIPS:引数のspriteを発射元にして赤四角の弾を作成して返す関数
function createBullet(sprite) {
const bullet = new Sprite(8, 8); //width:8px,height:8pxのSpriteを設置
bullet.moveTo(sprite.x, sprite.y); //発射元の座標に弾を合わせる
bullet.backgroundColor = "#ff0000"; //赤に設定
//target方向を移動角度とするため、まずはblackRect-targetへのベクトルを計算する(終点-始点で計算可能)
const vector = {
x: target.x - sprite.x,
y: target.y - sprite.y
};
//角度を計算するためには、Math.atan2(y,x)関数を使えばよい
bullet.rad = Math.atan2(vector.y, vector.x);
bullet.onenterframe = function () {
this.x += Math.cos(this.rad) * shotSpeed; //移動角度のcosがX軸移動方向
this.y += Math.sin(this.rad) * shotSpeed; //移動角度のsinがY軸移動方向
if (bullet.x = -30 || bullet.x = 430 || bullet.y = -30 || bullet.y = 430) { //画面外か判定
this.parentNode.removeChild(this); //画面外なら自身を削除する
}
}
return bullet; //上で作成したspriteを返す
}
さて、本ページの目的、ターゲットに向けて発射する処理です。
画面をクリックすると、ターゲットの位置を変更することができます。
以下vectorはx,yを持つ連想配列と呼ばれるものです。連想配列は、まとまったデータを格納する時に使います。
さて、終点(target)-始点(発射元)でベクトルを求めることができます。
const vector = {
x: target.x - sprite.x,
y: target.y - sprite.y
}
ここで求めたvectorをMath.atan2関数に以下の順番で引数にすることで、目標方向の移動角度を計算することができます。
bullet.rotation=Math.atan2(vector.y,vector.x);
あとは、「移動角度を設定して打ち続ける」と同じく、sin,cosで移動量を決めたら終了です。
さて、これで一通りの説明が終わりました。
ここで実際に書き換えることで、今後のシューティング制作ライフも向上するはずです・・・!
以上です!よいゲーム制作ライフを!
おまけ 上記プログラムを改造した暗黒黒炎攻撃
See the Pen カッコいいエフェクトで打つ by ほっとフクロウ(作っちゃうおじさん) (@hothukurou) on CodePen.
Sprite は以下パラメータを設定できます。回転とか透明度変更とか拡大縮小ができるので、そのあたりを上手く使って作りました。
Sprite.rotation=0;//度数表記で代入値に回転
Sprite.opacity=1;// 非透明度を0~1で指定 0で透明 1で非透明
Sprite.scaleX=1;//X方向の拡大率を指定 1がデフォ値
Sprite.scaleY=1;//Y方向の拡大率を指定 1がデフォ値
Sprite.backgroundColor="rgba(0,0,0,1)"; //(red,green,blue,opacity)を設定 rgbは0~255, aは0~1で透明度設定
あとは、enchant.jsの公式リファレンスを見たり、ググったりするといいです。
さらにおまけ
以前、あほげーという一日でゲームを作るイベントで制作したシューティングゲームです。