第25回【シューティング制作.5】


まずは動画を見てくださいね!
JavaScriptでシューティング制作です!
今回は弾の発射です!

スプライトの追加

//スプライト
let sprite = [
	new Sprite(  0, 0,22,42 ),//0,自機 左2
	new Sprite( 23, 0,33,42 ),//1,自機 左1
	new Sprite( 57, 0,43,42 ),//2,自機 正面
	new Sprite(101, 0,33,42 ),//3,自機 右1
	new Sprite(135, 0,21,42 ),//4,自機 右2
	
	new Sprite(  0,50, 3, 7 ),//5,弾1
	new Sprite(  4,50, 5, 5 ),//6,弾2
];
sprite.pngの弾の位置に合わせて新しく弾のスプライト2つを追加しました。


弾クラス作成

//弾クラス
class Tama
{
	constructor( x,y, vx,vy )
	{
		this.sn   = 5;
		this.x    = x;
		this.y    = y;
		this.vx   = vx;
		this.vy   = vy;
	}
	
	update()
	{
		this.x += this.vx;
		this.y += this.vy;
	}
	
	draw()
	{
		drawSprite( this.sn, this.x , this.y );
	}
}
let tama=[];
単純な弾クラスを作りました。updateメソッドで移動、drawメソッドで描画です。sn=5はスプライト番号です。

スペースキーで発射

スペースキーのkeyCodeは32です。
		if( key[32] )
		{
			tama.push( new Tama(this.x,this.y,  0,-2000 ) );
		}
配列に新しい要素を追加する時はarrayオブジェクトのpushメソッドを使います(末尾に追加)
これでスペースが押されている間は弾のオブジェクトが追加され続けます。。。


ゲームループに処理を追加

	//移動の処理
	...
	for(let i=0;i<tama.length;i++)tama[i].update();
	...
	
	//描画の処理
	...
	for(let i=0;i<tama.length;i++)tama[i].draw();
	...
星の移動、描画と同じ様に、tamaの移動、描画をゲームループ内に追加します。

弾の削除

このままでは、配列tamaがメモリいっぱいに増えてしまうので消さないといけません。
弾クラスに自身を消すフラグを持ちます。
	this.kill = false;
範囲をチェックしてフラグを立てます。
	if( this.x<0 || this.x>FIELD_W<<8 
		|| this.y<0 || this.y>FIELD_H<<8 )this.kill = true;
これで範囲外に出た時に「消してください」というフラグを立てました。


updateメソッドを呼ぶ処理の部分を修正します。
	for(let i=tama.length-1;i>=0;i--)
	{
		tama[i].update();
		if(tama[i].kill)tama.splice( i,1 );
	}
updateメソッドを呼んだ後にkillフラグを見て、trueの時はspliceしています。
spliceは配列内の特定の要素を消すarrayオブジェクトのメソッドですが、消した以降の要素番号が変わるため、forループの順番を逆順にしています。


デバッグ情報の表示

弾が消えたかどうか解らないのでデバッグ情報として表示します。
//デバッグのフラグ
const DEBUG = true;

...

	if(DEBUG)
	{
		con.font="20px 'Impact'";
		con.fillStyle ="white";
		con.fillText("Tama:"+tama.length,20,20);
	}
...
キャンバスに配列tamaの数を表示して、実際に配列の数をチェックします。


ついでにFPSの表示

弾が消えたかどうか解らないのでデバッグ情報として表示します。
//デバッグのフラグ
const DEBUG = true;

let drawCount=0;
let fps=0;
let lastTime=Date.now();
	if(DEBUG)
	{
		drawCount++;
		if( lastTime +1000 <= Date.now() )
		{
			fps=drawCount;
			drawCount=0;
			lastTime=Date.now();
		}
		
		
		con.font="20px 'Impact'";
		con.fillStyle ="white";
		con.fillText("FPS :"+fps,20,20);
		con.fillText("Tama:"+tama.length,20,40);
	}
毎フレーム毎にdrawCountを加算して、1000ms(1秒)経過毎にfpsを算出しています。
いまいち正確な感じはしませんが・・・。


弾の間隔など

毎フレーム毎に弾を発射するとレーザーみたいになってしまうので、一定の間隔を空ける様にします。

		this.reload = 0;
		this.relo2  = 0;
...

		if( key[32] && this.reload==0 )
		{
			tama.push( new Tama(this.x,this.y,  0,-2000 ) );
			
			this.reload=4;
			if(++this.relo2 ==4)
			{
				this.reload=20;
				this.relo2=0;
			}
		}
		if( !key[32] ) this.reload= this.relo2=0;
プロパティにreloadとrelo2を持ち、reloadは1発ごとの間隔用、relo2は連射回数に使用しています。
基本的にはreloadのフレーム数毎に発射するけど、relo2回数毎にはフレーム間隔を多めに空ける様にしています。

			tama.push( new Tama(this.x+(4<<8),this.y-(10<<8),  0,-2000 ) );
			tama.push( new Tama(this.x-(4<<8),this.y-(10<<8),  0,-2000 ) );
			tama.push( new Tama(this.x+(8<<8),this.y-(10<<8), 80,-2000 ) );
			tama.push( new Tama(this.x-(8<<8),this.y-(10<<8),-80,-2000 ) );
一回の発射で複数弾を出す様にしました。
この辺りはゲームバランスの問題なので、いろいろやってみましょう。


次回は敵キャラの表示をやっていきます。
続く...




akichon/あきちょん
Youtubeでプログラミング講座やってるゲーム好きの(元?)プログラマ...
>>open profile...
■Twitter始めました
https://twitter.com/ak1chon
■Youtubeチャンネルはこちら
akichon(Youtube)