DXScriptでゲーム作成 その3


チュートリアル3回目は敵を表示させて弾を撃たせてみます。
完成品はこちら(sample3.zip)


あまり複雑な動きをすると面倒くさい処理が長くなるので、単純に左右に動きながら全方向に弾を打つようにします。




1.準備
せっかくなので緑をボコりましょう。
東方素材蒐の早苗魔法詠唱.pingをsanae_magic.pngとリネームし、フォルダに設置します。
あと霊夢と同様背景を透過色にしておきます。




2.敵クラスの定義
プレイヤーはSpriteクラスに変数や関数を直接追加して作成していましたが、敵のほうはきちんとクラスを定義して作成してみます。
dxsfはenchant.jsのクラス生成処理をそのまま持ってきてるので、同じクラス定義ができます。

// 敵
var Enemy = Class.create(Sprite, {    // 1
	initialize: function() {
		Sprite.call(this, 48, 64);  // 2
		
		var x1 = Enemy.X_POS1 - this.width / 2;
		var x2 = Enemy.X_POS2 - this.width / 2;
		
		this.image = game.assets['sample3\\sanae_magic.png'];
		this.x = x2;
		this.y = Enemy.Y_POS;
		this.scale = 1.5; // 1.5倍    // 3
		
		// アニメーション
		var timeline = new FrameAnimation(this, ["x"]);   // 4
		var time = 0;
		
		timeline.addEvent((time+Enemy.STOP_TIME/3) * dxs.fps, function() { this.shot(); }); // ショット
		timeline.addEvent((time+Enemy.STOP_TIME*2/3) * dxs.fps, function() { this.shot(); }); // ショット
		timeline.addEvent((time+Enemy.STOP_TIME) * dxs.fps, function() { this.shot(); }); // ショット
		
		time += Enemy.STOP_TIME;
		timeline.addPoint(time * dxs.fps, {"x": x2}); // 静止
		time += Enemy.MOVE_TIME;
		timeline.addPoint(time * dxs.fps, {"x": x1}, FrameAnimation.cos); // 左へ
		
		timeline.addEvent((time+Enemy.STOP_TIME/3) * dxs.fps, function() { this.shot(); }); // ショット
		timeline.addEvent((time+Enemy.STOP_TIME*2/3) * dxs.fps, function() { this.shot(); }); // ショット
		timeline.addEvent((time+Enemy.STOP_TIME) * dxs.fps, function() { this.shot(); }); // ショット
		
		time += Enemy.STOP_TIME;
		timeline.addPoint(time * dxs.fps, {"x": x1}); // 静止
		time += Enemy.MOVE_TIME;
		timeline.addPoint(time * dxs.fps, {"x": x2}, FrameAnimation.cos); // 右へ
		timeline.addEvent(time * dxs.fps, function() { timeline.reset(); }); // 最初に戻る
		
		this.addEventListener(ONFRAME, function() {
			timeline.update();
		});
	},
	// 弾発射
	shot: function() {
		var x = this.x + this.width / 2;
		var y = this.y + this.height / 2;
		
		for(var i=0; i<16; i++) { // 16方向に弾を発射する
			var bullet = new EnemyBullet(x, y, i);  // 5
			this.eb_group.addChild(bullet);
		}
	}
});


1-enchant.jsと同様Class.createでクラスを定義します。
敵はプレイヤーと同様Spriteクラスから派生させます。


2-Spriteクラスのコンストラクタを呼んでいます。


3-プレイヤーと同じ大きさだと敵という感じがしないので1.5倍に拡大します。
画像の中心から拡大するので、拡大するとSpriteクラスのx、y、width、heightと画像の描画位置、サイズに差異ができます。
当たり判定などで座標を扱うときは拡大した分を補正する必要があります。


4-敵を一定間隔で左右に移動するようアニメーションを定義します。
このために、dxsf.jsにFrameAnimationというクラスを追加しています。(アップローダーからdxsf.jsを落としてver1.2にしてください。)


このFrameAnimationクラスはMMD(MikuMikuDance)のフレーム(左のほうにたくさん点が表示されるアレ)をイメージしています。
使い方は、例えばあるxとyいう変数をもつインスタンスを操作したいなら、コンストラクタにそのインスタンスと操作対象の変数名の配列を渡します。

var point = {"x": 0, "y": 0}
var timeline = new FrameAnimation(point, ["x", "y"]);


そして、30フレーム後にxが100、60フレーム後にyが100になるように移動させたいなら、
引数にフレーム数と値の連想配列を指定してaddPoint()を呼びます。

timeline.addPoint(30, {"x": 100});
timeline.addPoint(60, {"y": 100});


これにより、x、yはそれぞれの指定フレームに100になるように、0から徐々に変化していきます。
xのほうが移動に掛ける時間が少ないので、yの二倍の速さで変化します。
要は、何フレーム目にどの場所に移動して欲しいか、を書けばあとは自動でアニメーションするということです。
さらにaddPoint()の第三引数に移動量計算関数を指定することで、ゆっくり→はやく→ゆっくりなど、移動の仕方も制御することができます。 →参考


サンプルでは右に10秒静止したあと左に2秒かけて移動。
左で10秒静止したあと右に2秒かけて移動。
初期位置に戻ったらリセットして無限ループさせています。
移動には移動量計算関数を指定してゆっくり→はやく→ゆっくりの移動をさせます。

さらにaddEvent()で指定フレームに関数を実行することができるので、これを使い10秒の静止時間中に等間隔で3回ショットを行います。


5-自身の中心を起点に、16方向に移動する弾を生成します。
複数の描画部品を格納できるGroupクラスに生成した弾を格納しています。
後ほど弾の当たり判定を作成するときに、弾の一覧をこのGroupから取得することにしましょう。




3.弾クラス
弾を描画するクラスを定義します。

// 敵の弾
var EnemyBullet = Class.create(Node, {    // 1
	initialize: function(x, y, direction) {
		Node.apply(this, arguments);
		
		this.x = x;
		this.y = y;
		this.r = 5;
		
		var speed = 5;
		this.vspeed = speed * Math.cos(2 * Math.PI * direction / 16);
		this.hspeed = speed * Math.sin(2 * Math.PI * direction / 16);
		
		// 弾移動関数
		this.addEventListener(ONFRAME, function() {
			this.x += this.vspeed;
			this.y += this.hspeed;
			
			if (this.x < -50 || this.x > 690 || this.y < -50 || this.y > 530) {
				// 画面外に出たら自身を削除
				this.removeAllListener();
				this.parent.removeChild(this);
			}
		});
	},
	ondraw: function() { // 2
		dxs.DrawCircle(this.x, this.y, this.r, dxs.GetColor(255,255,255), true);
	},
});


1-弾をNodeクラスから派生して作成します。
Nodeはdxsfにおける描画部品のベースクラスです。
SceneやGroupなどに追加するものはこのクラスから派生する必要があります。
Nodeは座標を持たないので、円形の物体などはこれに中心点と半径を定義すれば表現できます。


2-ondraw()を派生して定義することで、描画を行います。
この関数に描画処理を書いておけば、あとはフレームワークが自動処理します。
ここではDrawCircle関数で白色の円を描画しています。




これで弾を撃ってくる敵を作成できました。
弾を撃てるのが敵だけでは癪なので、今度はプレイヤーも弾を撃てるようにしましょう。