Processing.js製 弾幕シューティングを、enchant.jsに移植してみた

wise9で連載されていた弾幕シューティング作成講座がかなり面白かったですね。
第四回目の記事で、ついに弾幕が出た時は興奮ものでした!

ちょうど、enchant.jsの習作としてなにか作りたいなーと考えていたので
これの移植に挑戦してみたいと思います。

元記事一覧
http://wise9.jp/category/連載-processing-js-で初めてのゲームプログラミング

障害になるような違いはありません

Processing.jsや、ゲームのロジックは元記事を参照していただくとして
移植するには、文法的な違いを理解する必要がありそうですが
Processing.jsは、そもそもJSライブラリなので大きな違いはありません。

JSにはあまりなじみのない、型宣言とクラス定義さえおさえてしまえば簡単です。

Processing.jsのクラス定義の例
class Example {
  int x;
  Example(int dx) {
    x = dx;
  }
  void method(int dy) {
    x = x + dy;
  }
}

それぞれの意味はこんな感じ

class クラス名 {
  型 プロパティ;
  コンストラクタ(型 引数) {
    //初期化時によばれる関数
  }
  型 メソッド() {
    //戻り値がないならば、型はvoid
  }
}

つまり例示の変数 x はint(整数)型であることを宣言し
method()は戻り値をもたない関数ということになります。

これをenchant.jsで書くならば
var Example = Class.create({
	initialize: function(dx) {
		this.x = dx;
	},
	method: function(dy) {
		this.x = this.x + dy;
	}
});

Class.createにクラスの定義内容をオブジェクトで渡します。
initialize()は、継承または生成時に自動的に呼び出されるので
これをコンストラクタとして使います。
それから、型宣言はないので消してしまいます。

実際に書き直す

試しに次のソースコードを移植してみました。
サンプルプログラム03-2

enchant.jsに移植

//enchant.jsを初期化
enchant();
var mousePressed, mouseX, mouseY;
var game, tama, stage, canvas;

//setup()にあたる処理
window.onload = function() {
	
	game = new Game(320, 320);
	game.fps = 30;
	noCursor();
	tama = new Tama(160, 0, 10);
	
	//描画用canvasを追加
	stage = new Sprite(320, 320);
	stage.image = new Surface(320, 320);
	game.rootScene.addChild(stage);
	game.addEventListener(enchant.Event.ENTER_FRAME, draw);
	canvas = stage.image;
	
	//開始
	game.start();
};

function ship(x, y) {
	stroke(255, 255, 255);
	triangle(x, y - 7, x - 10, y + 7, x + 10, y + 7);
	if (mousePressed) {
		line(x, y- 7 , x, 0);
	}
};

var Tama = Class.create({
	initialize: function(x, y, r) {
		this.tx = x;
		this.ty = y;
		this.tr = r;
	},
	update: function() {
		this.ty += 10;
		stroke(255, 0, 0);
		ellipse(this.tx, this.ty, this.tr, this.tr);
		if (this.ty > game.height) {
			this.ty = 0;
			this.tx = Math.random() * game.width;
		}
	}
});

function draw() {
	clear();
	ship(mouseX, mouseY);
	tama.update();
};

//------------------------------------------------------------------------------
// Processing.jsのメソッドを簡易的に再現
// @see http://processingjs.org/
//------------------------------------------------------------------------------
/**
 * Base64形式の透明GIFを表示させて、デフォルトカーソルを消す
 */
function noCursor() {
	game.rootScene._element.style.cursor =
	 "url('data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw=='), auto";
};

/**
 * クリア
 */
function clear() {
	canvas.context.fillStyle = "rgb(0, 0, 0)";
	canvas.context.fillRect(0, 0, 320, 320);
};

/**
 * 線の色
 */
function stroke(r, g, b) {
	canvas.context.strokeStyle = "rgb("+ r +", "+ g +", "+ b +")";
};

/**
 * 三角形を描く
 */
function triangle(x1, y1, x2, y2, x3, y3) {
	canvas.context.beginPath();
	canvas.context.moveTo(x1, y1);
	canvas.context.lineTo(x2, y2);
	canvas.context.lineTo(x3, y3);
	canvas.context.closePath();
	canvas.context.stroke();
};

/**
 * 線を描く
 */
function line(x1, y1, x2, y2) {
	canvas.context.beginPath();
	canvas.context.moveTo(x1, y1);
	canvas.context.lineTo(x2, y2);
	canvas.context.closePath();
	canvas.context.stroke();
};

/**
 * 円を描く
 */
function ellipse(x, y, r) {
	canvas.context.beginPath();
	canvas.context.arc(x, y, r, 0, 2 * Math.PI, false);
	canvas.context.closePath();
	canvas.context.stroke();
};

/**
 * マウス座標
 */
document.addEventListener("mousemove", function(e) {
	mouseX = e.pageX;
	mouseY = e.pageY;
}, false);

/**
 * マウスダウン
 */
document.addEventListener("mousedown", function(e) {
	mousePressed = true;
}, false);

/**
 * マウスアップ
 */
document.addEventListener("mouseup", function(e) {
	mousePressed = false;
}, false);

コードの流れと関数名や変数をそのままになるように置き換えました。
前半部分のコードは、見た目的にサンプルと大きな違いはないと思いますが
構造的には、図形を描画するためのcanvas要素を生成して追加しています。

Processing.jsには、canvasを直感的に操作できる
triangleやellipseといった描画メソッドがあります。

後半部分は、このProcessing.jsの描画メソッドを必要な分だけ簡易的に追加してます。
これをenchant.jsとともに読み込ませると、サンプルと同じように動いてると思います。

最終的にできあがったのがこれ


safari/chrome
5/22追記 埋め込みプレイヤーだと safariでキーイベントが拾えないようです。。。 chromeは問題なし
5/24 アプリ側で対処するエントリ書きました→ http://d.hatena.ne.jp/nenjiru/20110523/1306164602

enchant.jsらしさということで、キャラクターはenchant.Spriteで表現し、
マウス操作からカーソルキー入力(バーチャルパッド)に変更してみました。

バランスを調整してるうちに、完全移植とは言い難くなりましたが
ロジック部分は無理なく移植できました。

まとめ

パソコンのキーボードで操作するぶんには問題ないですが
iPhoneのバーチャルパッドでの機体操作がちょっと厳しくなってしまいました。
これでは弾幕シューティングの売りである、よける快感がいまいちです。

元記事のタッチ操作なら、このへんのストレスがなく遊べますね。
ゲームなのでUIの選択も超重要ですね。

それから、やはりバランス調整が大変(大切)ですね。

enchant.jsが、表示やイベント管理をうまくやってくれるので
制作者がバランス調整に集中できる、すばらしいライブラリだと思います。