Archive for the ‘ ゲームプログラミング ’ Category

10分で作るWebGL対応の3Dシューティング with enchant.js


前回に続き、今度は3Dシューティングを10分で仕上げるライブコーディング・・・。gl.enchant.jsすごいな。

↑ 10分ほどの映像です。

作っているのはenchant.jsの生みの親、高橋さんですね。解説の一人はshi3zさんですが、微妙にvim嫌い?w

途中まで「大丈夫・・・かな?」と思いつつ、最後に一気にたたみかけてくるのはさすがですね。そしてやっぱり人の開発環境とか参考になりますね。そろそろvimかなぁ。

» wise9 › 挑戦者、高橋諒が3Dシューティングを10分で作る動画

『enchant.jsでシューティングゲームを10分コーディングする映像』がすごい・・・


shi3zさんによるライブコーディング映像がすごいですね・・・。enchant.jsを使って、たった10分でシューティングゲームを作っちゃっています。

↑ というわけで10分間の映像です。

どう実装すべきかを考えつつ、デバッグもこなして最後にはカタチにしてしまうとは・・・。10分間ではありますが(個人的には)手に汗握る展開で、あっという間でしたよ。コーディングされる方は是非見てみてください。

■ 今週末は秋葉原でガチンココーディングバトルが!

そしてこのライブコーディング対戦が来たる土曜日のleapfestでも展開されるらしいですよ・・・(enchant.js応援中なので告知協力)。しかも対戦にはenchant.jsの開発者、高橋諒さんが登場とのこと・・・。enchant.js作った人がどういうライブコーディングするか、って興味ありますよね。

個人的には先約があって見に行けないのですが(ぐぅ・・・)、9leap投稿者なら500円で入場できますので、プログラミングやゲーム作りなんかに興味がある方はいかがですかね。詳細は以下からご確認ください。

» wise9 › 挑戦者募集!君は10分間でゲームを作ることが出来るか!?最強HTML5プログラマーのプライドを賭けた戦いが勃発する!

ゲームはプログラミング教材としても最適なので、enchant.jsのドットインストールレッスンも増やしていかねば・・・(最近はなんと3D対応までしちゃっています。進化がすごい・・・)。

enchant.js公式サイトに待望の「逆引きリファレンス」が登場!


HTML5+JSでクロスプラットフォームのゲームが簡単に作れるenchant.jsですが、逆引きリファレンスが登場したようですよ。

Enchant js  HTML5 + JavaScript Game Engine

↑ 目次も充実していますね。

Enchant js  HTML5 + JavaScript Game Engine 1

↑ 具体的なコードも掲載されていてわかりやすいです。

enchant.js、とってもいいのですが、ドキュメントがもうちょい、という印象だったのでぐっとわかりやすくなりましたね。先日のmeetupにも行ってきましたが、社会人も挑戦できるコンテストも開催されるということで、ちょっとプログラミングでも・・・という方はいかがでしょうかね。

というわけで逆引きリファレンスは以下からどうぞ。

» enchant.js – HTML5 + JavaScript Game Engine

enchant.jsで今まで作ったゲームを『9leap』仕様にする方法


9leapのプログラミングコンテスト、iPadが当たったりして良い感じなのですが、ちょいと癖があるので投稿する際には以下に気をつけましょうね、というお話ですよ。

■ 最新版のenchant.jsを使う

ちょこちょこバージョンアップしているようなので最新版を使うように心がけましょうね。ダウンロードは以下からどうぞ。

» Home – GitHub

なお、いつ更新したか知りたい人は上記サイトのRSSを登録しておけば確実ですね。

■ プラグインを読み込む

9leap用のプラグインを読み込んでおくといろいろ便利です。ゲーム終了画面からスコアの投稿&ツイッターへの流し込みなどができるようになります。

まず、index.htmlに次のような記述を加えます。

<script type="text/javascript" src="plugins/nineleap.enchant.js"></script>

↑ game.jsより前に書きましょうね。なお、ディレクトリ構成はご自身のものにあわせてください。

それからgame.endを次のように変更します。

game.end(点数を格納した変数, 終了時のメッセージ);
// 例:game.end(score, 'Your score is '+score);

便利&簡単ですね。

■ バーチャルパッドの導入

9leapの前提条件として「スマートフォンで遊べること」というものがあります。したがって方向キー系の操作はバーチャルパッドへ変更する必要があります。

まずはindex.htmlに次のように記述します。

<script type="text/javascript" src="plugins/ui.enchant.js"></script>

↑ index.htmlにこれを追加。これもgame.jsより前に記述しましょう。

次にgame.js内に次のような記述を加えます。

var pad = new Pad();
pad.x = 0; // 位置の指定。ご自由に。
pad.y = 220;
game.rootScene.addChild(pad);

ここらへんはいいですよね。

これまでこのブログではツイッターへの投稿機能とか、「いいね!」ボタンなんかを独自でつけていましたが、コンテスト投稿用には省いておいた方が良さそうです。

あくまでミニゲームの内容に集中し、スコア管理などは9leapのプラグインに任せた方が良ろしいかと。9leapでは、プレイ回数なんかも記録されるので、作りがいがありますね。連休中にチャレンジしてみてはいかがでしょうかね。

» 9leap : トップページ

iPad 2が当たるプログラミングコンテストサイト『9leap』にゲームを投稿する方法


さて先日お知らせしたプログラミングコンテスト、9leapのサイトで投稿が可能になっていますね。iPad 2なんかも当たるので腕に覚えのある方は挑戦してみてはいかがでしょうか。

なお、せっかくなので9leapに投稿するまでの流れを紹介しておきますよっと。

↑ こちらのサイトですね。右上のログインからどうぞ。Twitter認証なのでアカウントが必要です。

↑ ツイッター認証したあとはこちらの登録画面へ。規約に同意したりなどのオプションを選びます。

↑ 登録後のホーム画面。投稿&設定ができます。とりあえず投稿してみます。

↑ こんな感じで必要な情報をアップします。ファイル群はざっとZIPであげればOKです。スクリーンショットもあげることができますね。

↑ 投稿完了!みんなに遊んでもらえます。

それから投稿後にプログラムを変更することも可能です。その場合はざっくりすべてをZIPで上書きしちゃえばOKです。なお、過去のバージョンはすべて保存されるようなので「あ、間違えた!」というときもさくっと戻せて良い感じです。

というわけで先日のクルマゲームを9leaps仕様に変更したものを以下にアップしておきました。良かったら遊んでみてくださいね。

» 9leap : 最速でクルマを乗り回すゲーム by taguchi

【enchant.js】 「最速でクルマを乗りこなすゲーム」の技術的解説


さきほど公開した「最速でクルマを乗りこなすゲーム」ですが、一応技術的な解説をしておきますよっと。

これね。

ざざっとソースをみていく感じすかね。あ、それからenchant.js、ちょこちょこバージョンアップしているようなので、最新版をダウンロードしておくと吉です。

» Home – GitHub

というわけでソースの解説ですな。ちょこちょこコメントいれつつ紹介していきます。

enchant();
window.onload = function() {
    var game = new Game(320, 320);
    game.preload('chara4.gif', 'numbers.gif', 'tiles.png', 'title.gif', 'replay.gif');

ここらへんはいいですよね。なお、title.gifとreplay.gifはGIFアニメにしてゲームっぽいものにしています。ゲームはこういう一手間でぐっと印象が違ってきますよね。

	game.onload = function() {
		var time = 0; // クリアまでの秒数
		var currentNumber = 10; // 今おっかけるべきタイルの数値
		var carType = 0; // クルマの色
		var startButton = []; // クルマを選ぶとき用のボタン
		var startX = 100; // クルマの出現位置
		var startY = 100;
		var titleScene = new Scene(); // タイトル画面
		var gameScene = new Scene(); // ゲームのメイン画面
		var scoreScene = new Scene(); // プレイ後のスコア画面

まずは初期設定ですね。今回のポイントはenchant.jsのSceneですな。

このSceneですが、レイヤーのようなもので、画面を重ねてその間を行き来することができます(pop, push的に)。

このゲームでは、プレイ前のスタート画面(クルマの色を選べる)、プレイ画面、プレイ後のスコア画面を3つのSceneを作っています。

↑ タイトル画面。

↑ プレイ画面。

↑ スコア画面。

では次のコードをみてみます。

		// setup title scene
		var titleAnim = new Sprite(320, 100); // "Pick your car to start"のボタンを作ります
		titleAnim.image = game.assets['title.gif']; // 画像を指定。
		for (i=0; i<4; i++) { // クルマの画像を並べてボタンにします。
			startButton[i] = new Sprite(32, 32);
			startButton[i].image = game.assets['chara4.gif'];
			startButton[i].frame = i; // クルマ画像のFrameを指定。
			startButton[i].x = 160 - 64 + (32 * i);
			startButton[i].y = 160 - 16;
			startButton[i].id = i;
			startButton[i].addEventListener('touchstart', function() { // クリックされたときの挙動
				carType = this.id; // これがクルマの色になります。
				game.pushScene(gameScene); // ゲームのメイン画面(gameScene)を前面に持っていきます。
			});
			titleScene.addChild(startButton[i]); // ボタン(クルマ)を追加。
		}
		titleScene.addChild(titleAnim); // これらをtitleSceneに追加。

ここではタイトル画面の部品を配置しています。タイトル画面(titleScene)自体を表示させるのはもうちょっと後になります。

では次にいきましょう。

		// setup score scene
		var finalScore = new Label('00.00'); // スコア用のラベル(前面)
		finalScore.font = "24px 'Arial Black'";
		finalScore.color = '#ff0000';
		finalScore.x = 40;
		finalScore.y = 70;
		var bgFinalScore = new Label('00:00'); // スコア用のラベル(背面)
		bgFinalScore.font = "24px 'Arial Black'";
		bgFinalScore.color = '#cccccc';
		bgFinalScore.x = finalScore.x + 1; // 1ずつずらしてドロップシャドウのような効果を出します。
		bgFinalScore.y = finalScore.y + 1;
		var replayAnim = new Sprite(320, 100); // "Replay?"のボタンを作ります。
		replayAnim.y = 130;
		replayAnim.image = game.assets['replay.gif'];
		replayAnim.addEventListener('touchstart', function() { // リプレイをクリックされたときの挙動。
			time = 0; // いろいろリセットしています。
			currentNumber = 10;
			number.frame = currentNumber;
			car.x = startX;
			car.y = startY;
			car.speed = 2.0;
			car.setDegree(-90);
			$('#post_to_twitter').remove();
			game.popScene(); // 今のScene(scoreScene)を外して背面のScene(gameScene)を表示します。
		});
		scoreScene.backgroundColor = 'white';
		scoreScene.addChild(replayAnim);
		scoreScene.addChild(bgFinalScore);
		scoreScene.addChild(finalScore);

ここではゲーム後のスコア画面を作っています。スコア用のラベルは前面と背面を作ってドロップシャドウのような表現にしてみました。これも大事な一手間かと。

それからReplayボタンを押したときの挙動も書いています。popSceneを使うことで現在のシーンを外して背面のシーンを表示させることができます。

さて次にいきましょう。

		// setup game scene
		var label = new Label('00.00');
		label.font = "24px 'Arial Black'"; // スコア用のラベル(前面)
		label.color = '#ffffff';
		label.x = 5;
		label.y = 280;
		var bgLabel = new Label('00:00'); // スコア用のラベル(背面)
		bgLabel.font = "24px 'Arial Black'";
		bgLabel.color = '#333333';
		bgLabel.x = label.x + 1;
		bgLabel.y = label.y + 1;
		var bgPattern = new Sprite(320, 320); // 背景画像の設定
		bgPattern.image = game.assets['tiles.png'];
		var number = new Sprite(16, 16);
		number.image = game.assets['numbers.gif'];
		number.frame = currentNumber; // 数字のパネルを配置
		number.x = Math.random() * (320-16); // 位置はランダムに。
		number.y = Math.random() * (320-16);

ここからメイン画面ですね。まずはスコア表示用のラベルを配置し、ランダムな位置に数値パネルを置きます。

		// car logic inspired by http://d.hatena.ne.jp/nakamura001/20110423/1303565014
		var car = new Sprite(32,32);
		car.image = game.assets['chara4.gif'];
		car.x = startX;
		car.y = startY;
		car.degree = -90;
		car.vx = 0;
		car.vy = 0;
		car.speed = 2.0;
		car.setDegree = function(degree) {
			this.degree = degree;
			this.rotation = degree + 90;
			this.vx = Math.cos(degree * Math.PI / 180);
			this.vy = Math.sin(degree * Math.PI / 180);
		};
		car.setDegree(car.degree);
		car.addEventListener('enterframe', function() {
			car.frame = carType; // クルマの色をcarTypeにあわせて設定。
			var isMoving = false;
			if (game.input.up || game.input.down) isMoving = true;
			if (isMoving && game.input.left) {
				this.degree -= 4;
				this.degree = Math.min(this.degree, this.degree+360);
				this.setDegree(this.degree);
			}
			if (isMoving && game.input.right) {
				this.degree += 4;
				this.degree = Math.max(this.degree, this.degree-360);
				this.setDegree(this.degree);
			}
			if (game.input.up) {
				this.x += this.vx * this.speed;
				this.y += this.vy * this.speed;
			}
			if (game.input.down) {
				this.x -= this.vx * this.speed;
				this.y -= this.vy * this.speed;
			}
			if (this.x+this.width < 0) this.x = game.width;
			if (this.x > game.width) this.x = -this.width;
			if (this.y+this.height < 0) this.y = game.height;
			if (this.y > game.height) this.y = -this.height;
			if (this.within(number)) { // 数値パネルをとったときの処理。
				currentNumber--; // 数値を一つ下げて・・・。
				number.x = Math.random() * (320-16); // ランダムな位置に新しいパネルを表示。
				number.y = Math.random() * (320-16);
				number.frame = currentNumber;
				this.speed += 0.5; // さらにクルマのスピードをあげています。
				// console.log('hit');
				if (currentNumber < 0) { // ゲームクリア時の処理。
					finalScore.text = bgFinalScore.text = 'Your Score: '+(time++/game.fps).toFixed(2); // スコアの表示
					game.pushScene(scoreScene); // スコア画面を表示
					// 以下、TwitterにPostする部分を追加。
					var statusText = '『最速でクルマを乗り回せ!』ゲームのスコアは【'+(time/game.fps).toFixed(2)+'秒】でした! http://www.ideaxidea.com/enchantjs/cars/ #enchantcars';
					var postDiv = $('<div id="post_to_twitter"></div>').append(
						$('<a href="" target="_blank"><span class="css3button">このスコアをTwitterに投稿する?</span></a>')
						.attr('href', 'http://twitter.com/home?status='+encodeURIComponent(statusText))
						);
					$('body').append(postDiv);
				}
			}
			// console.log(this.x + ' ' + this.y);
		});
		gameScene.addChild(bgPattern); // もろもろ追加。
		gameScene.addChild(number);
		gameScene.addChild(car);
		gameScene.addChild(bgLabel);
		gameScene.addChild(label);

ここらへんが本当のメインですね。クルマを動かすところのロジックは正直「強火ですすめ」さんのところをざっと拝借したのでそちらをご覧ください。

逆に僕が作り込んだのは、数値パネルとの衝突処理と、最終的にクリアしたときの処理です。

最後にツイッターにスコアを登録できる部品を追加していますが、それはjQueryで実現しています。あらかじめcss3buttonクラスのスタイルもCSS(CSS3 Button Generatorを使いました)にて指定しておくと良い感じです(詳しくはindex.htmlを見てください)。

そこまで来たらあとは最後の部分ですな。

		// タイマー処理
		game.addEventListener('enterframe', function() {
			label.text = bgLabel.text = (time++/game.fps).toFixed(2);
		});
        // タイトル画面を表示
		game.pushScene(titleScene);
	}
	game.start(); // ゲームスタート!
};

ここらへんはいいですよね。

ふぅ、以上ですかね。本当はハイスコアを保持して・・・なんかも考えたのですが今回はやめておきました。というわけでご参考までに。

【enchant.js対決#002】 「最速でクルマを乗り回せ!ゲーム」を作ったよ!


さて前回はなんと惨敗だった@fkojiとのenchant.js対決ですが、第2回を開催します。いまから24時間以内にたくさんの「いいね!」を集めた方が勝ちです!

なお、今回のテーマは「クルマの画像を使うこと」。制作猶予はのんびり4日間ぐらいだったかな。

というわけで僕が作ったのは「最速でクルマを乗り回せ!ゲーム」です。

矢印キーでクルマを運転しつつ、10から00までのパネルをいかに速くとれるかを競うゲームです。細かいコードの解説なんかは別途エントリーにまとめますが、とりあえずプレイしたい方は以下からどうぞ。

» Attack the numbers

前回の「いかにもさくっと作ったよ」的なゲームと異なり、ちゃんとスタート画面やリプレイの仕組みを用意したのが今回工夫した点かな。また、前回@fkojiが実装していた「結果をツイッターでつぶやく」もつけておきました。

なお、クルマの操作部分については以下のブログを大変参考にさせていただきました!(というか、ほぼそのまんまw)。作者の方にもちゃんと許可をとりましたのでここにリンクを張りつつ、感謝の意を表したいと思います。

» 車の移動処理 – 強火で進め

さて、対決相手の@fkojiが作ったのは「クマさんをよけて突っ走れ」ゲームです。クルマを操作して10匹にあたってしまうとゲームオーバーですな。

» クマさんをよけて突っ走れ – enchant.jsの練習 @fkoji

ソースをみると独自関数なんかを作っていて参考になりますな・・・なるほど・・・。

以上、今回も「いいね!」対決なので、もしよろしければ良かったゲームで「いいね!」してもらえるとうれしいです。はてさて、今回はどうなるかな・・・。

enchant.jsのサンプルコードを解読する(RPG編その4)


その1その2その3に続いての第4弾。今回で最終回ですよ。

さて最後の方のコードをちょろりと見ていきましょう。

var pad = new Pad(); // Padに対応させるよ
pad.x = 0; // x座標
pad.y = 220; // y座標
game.rootScene.addChild(pad); // シーンに追加

これはタッチパッドに対応させるための記述ですね。なお、これを使うにはHTMLの方で「ui.enchant.js」を読み込んでおく必要があります。

↑ 左下のこれ。

これはまぁ、そのままなのでOKでしょう。

で、お次はこちら。マップのスクロール処理ですね。

このゲームの場合、ゲーム画面が320×320、マップが480×480なので概念的にはこういうことになります。

↑ このゲーム画面を上下左右に動かしていきます。

そこでこの処理ですが・・・。

game.rootScene.addEventListener('enterframe', function(e) {
    var x = Math.min((game.width  - 32) / 2 - player.x, 0); // playerの位置がマップ半分まで到達?
    var y = Math.min((game.height - 32) / 2 - player.y, 0);
    x = Math.max(game.width,  x + map.width)  - map.width; // playerの位置がマップ右端まで到達?
    y = Math.max(game.height, y + map.height) - map.height;
    stage.x = x; // stageの座標をセット
    stage.y = y;
});

うむ・・・はっきりいって全然わからないw。

しょうがないので、ゼロベースで自分で考えてみました。話を単純化するためにStageのx軸だけで考えてみます。stage.xを算出するにはプレイヤーの位置によって次の3つのパターンがあることがわかります。

↑ こんな感じの3つのパターンっすよね。

つまり、プレイヤーが左側(ゲーム画面の半分より左)にいるときはマップがスクロールしないので、stage.xは0です。同様に右端の場合はスクロールしきっちゃっているのでstage.xは-160になります。

その間に関してはマップの真ん中からプレイヤーが動いた距離分だけなので(ちょっと複雑ですが)stage.xは図の通りになります。

ただ、そう考えると、多分、Webプログラマだと次のように書いちゃうと思います(ちょっと強引で汎用性ないですが)。

var x = 0;
if (player.x < (game.width/2-16)) {
    x = 0;
} else if (player.x-16 > (game.width)) {
    x = -160;
} else {
    x = (game.width/2-16)-(player.x);
}
stage.x = x;

案の定、これでもきれいに動くのですが、サンプルコードのように書くとは・・・。ゲームプログラミングってなんかmaxとかminとかの定石が多いような気がしますよ・・・。

きちんとロジックわかったうえで見るとまぁ、そうかな、という気がしますが、これってゼロからは絶対(僕には)書けないですよ・・・。というわけでスクロールゲームつくるときはもうこれをコピペすることにしちゃいましょう!(だめかw)

ふー・・・というわけで全4回にわたる解説エントリーが終了です。あとマリオのサンプルゲームもありますが、こちらも解読していきますかね。解読するのはいいのですが、その説明をブログに書くのが大変w。ちょっとのんびりになりますが、またアップしますね。

追記

いつも参考にさせていただいている「」にて補足記事が。こちらもあわせてどうぞ!

enchant.jsのサンプルコードを解読する(RPG編その3)


その1その2に引き続きの第3弾。今回もプレイヤーの挙動を見ていきます。

では次のコードを見ていきましょう。

player.isMoving = false; // プレイヤーが動いているかどうかのフラグ。最初止まっているのでfalse
player.direction = 0; // プレイヤーの方向
player.walk = 1; // プレイヤーの歩行画像パターン

directionとwalkの組み合わせで前回紹介したプレイヤーの画像を特定しています。それらの関係については次のようになっていますよ。

↑ まぁ、わかりやすいですね。

初期値だとプレイヤーの方向が「0=下向き」、歩行画像パターンが「1」なので次のようになっています。

↑ ですね。

この関係を頭にたたき込んだ上で次のコードを見ていきましょう。

player.addEventListener('enterframe', function() { // フレーム毎の処理。
    this.frame = this.direction * 3 + this.walk; // プレイヤー画像をdirectionとwalkで設定。

プレイヤーのframeは元画像とSprite設定したときのサイズによって次のように規定されています。

↑ またしても無駄に作り込んだぞ・・・w。

というわけで、direction*3でどの行を選ぶかが決まり、walkによってどの列が選ばれるかが決まるってわけですな。

さて次にいきますか。だんだん複雑になっていきますよ。

if (this.isMoving) { // プレイヤーが動いているとき
    (ちょっと後回し)
} else { // プレイヤーが動いていないとき
    this.vx = this.vy = 0; // x,y軸でどれだけ動くかの変数。まず0で初期化。
    var n = Math.random(); // これなんだ? → 【追記】 消し忘れっぽい
    if (game.input.left) { // 左矢印が押された!
        this.direction = 1; // directionを1に設定(プレイヤーの画像設定に使う)
        this.vx = -4; // 左へ4ピクセル移動しようぜ!
    } else if (game.input.right) {
        this.direction = 2;
        this.vx = 4; // 右へ4ピクセル移動しようぜ!
    } else if (game.input.up) {
        this.direction = 3;
        this.vy = -4;
    } else if (game.input.down) {
        this.direction = 0;
        this.vy = 4;
    }
    (後述)
}

さてここではまずプレイヤーが動いている場合とそうでないかで処理が分岐します。

最初は「isMoving=false」なんで下の方からみていきましょう。上のコードを見ればわかりますが、まずはvx, vyを初期化し、キー入力によってdirectionとvx, vyの値を変えています。directionは上述したようにプレイヤーの画像を特定するのに必要ですね。

次にvxやらvyを使って移動後の座標を計算し、衝突判定をしています。

if (this.vx || this.vy) { // vxかvyが0じゃない場合=動こうとしている場合
    var x = this.x + (this.vx ? this.vx / Math.abs(this.vx) * 16 : 0) + 8;
    var y = this.y + (this.vy ? this.vy / Math.abs(this.vy) * 16 : 0) + 8;
    if (0 <= x && x < map.width && 0 <= y && y < map.height && !map.hitTest(x, y)) {
        this.isMoving = true;
        arguments.callee.call(this);
    }
}

ここらへんは実際の数値をあてはめてみるとわかりやすいかもです。

まず初期状態だとキャラクターのx, yはそれぞれ6*16, 10*16+8、mapの広さは480*480です。その状態から「右矢印」を押したと想定してみます。この場合、this.vxが4、this.vyが0になります。したがって・・・。

if (4 || 0) { // 4がtrueなので続行。
    var x = (6*16) + (4 ? 4 / Math.abs(4) * 16 : 0) + 8; // 120 = (6*16) + 16 + 8 = 現在地から16ピクセル右にいって、さらに8ピクセル右。
    var y = (10*16+8) + (0 ? 0 / Math.abs(0) * 16 : 0) + 8; // 176 = (10*16+8) + 0 + 8 = 現在地から8ピクセルだけ下。
    if (0 <= 120 && 120 < 480 && 0 <= 176 && 176 < 480 && !map.hitTest(120, 176)) { // 衝突していないから続行
        this.isMoving = true; // 動いているフラグを立てる
        arguments.callee.call(this); // 呼び出し元関数を再度呼ぶ
    }
}

大丈夫ですかね・・・プログラミング初心者だと必ずはまるw三項演算子はいいかな?「(this.vx ? A : B)」というのは、「this.vxがTrueだったらA、FalseだったらB」という意味です。

あと、ちょっとわかりづらいのが衝突判定をするマップが16×16であるのに対し、キャラクターが16×24である点ですね。y方向に必ず8が足されるのはそのせいですな(逆にいうとx方向に8足されるのはなんで?って気がしないでもない・・・問題はないですが → 【追記】 コメント欄参照のこと)。

ついでなのでちょいと図解してみました。右に行く場合ね。

↑ 図を作るのが面倒でついに手描きにw。

これ、上下左右でシミュレーションしてみるとわかりますが、必ず衝突判定したいマスの上部中央で判定しているようですね。左上で判定しても良さそうなものなのですが・・・詳しい人がいたら教えてください。

それから次の「arguments.callee.call(this);」は呼び出し元の関数を呼ぶ決まり文句ですね(「arguments.callee();」で良くなかったっけ?)。これ、なぜこうしているかというと・・・なんでだろw。ぶっちゃけわからないので詳しい人、教えてください・・・(汗)。 → 【追記】 コメント欄参照のこと。

そこは他力本願にしつつ、今度はちょっと前に戻ってisMovingがTrueだった場合のコードを見てみます。

if (this.isMoving) { // isMovingがTrueの場合
    this.moveBy(this.vx, this.vy); vx, vyの分だけキャラを移動
    if (!(game.frame % 3)) { // 現在のフレーム数を3で割ったあまりが0じゃない時=1か2のとき現在のフレーム数を3で割ったあまりが0のとき=3フレームに一回、ですな。
        this.walk++; // walkを1増加
        this.walk %= 3; // walkを3で割った余りに=0,1,2のどれかにする
    }
    if ((this.vx && this.x % 16 == 0) || (this.vy && (this.y-8) % 16 == 0)) { // 次のマスに移動しきったら
        this.isMoving = false; // isMovingをFalseに
        this.walk = 1; // walkを1に
    }
}

ここの処理は前半と後半に分かれますね。

まずは前半でキャラの歩行画像パターンを切り替えています。フレーム毎にwalkを1増加させつつ、3で割ったあまりにすることで、walkのパターンを0,1,2に振り分けています。ただし個人的には「this.walk = game.frame % 3;」でいいんじゃね?と思ったり。なぜこうするんだろ。せわしない動きになるからかな・・・。 → 【追記】 コメント欄参照のこと。

後半は「キャラが次のマスに動いたら停止する」という処理をしていますね。ここでもマスの大きさとキャラの大きさが違うのでy方向では8ピクセル引いています。

ふー・・・プレイヤー部分についてはこんなところですかね。最後にパッドについてと、マップの移動についての記述をして終わりかな、っと。

なぜこうなる?

【追記】 コメント欄にてすべて解決しました!ご協力いただいた皆様、感謝感激です!

それから今回はよくわからない点が多かったので個人的に「???」だった点をまとめておきます。誰かわかる人がいたら教えてください・・・。

  • 衝突判定のxを求めるときに最後に+8するのはなぜ?なくてもよくない?
  • 「arguments.callee.call(this);」ってなんであるの?
  • this.walkを計算するところって「this.walk = game.frame % 3;」でもよくない?
  • 途中にある「var n = Math.random();」ってなんだ?

enchant.jsのサンプルコードを解読する(RPG編その2)


前編に引き続きenchant.jsのサンプルコードを読み解いていきますよ。

マップの配列が終わったので次はプレイヤー部分ですね。早速コードを見てみましょう。

        var player = new Sprite(16, 24); // プレイヤーの大きさを16×24に設定
        player.x = 6 * 16; // プレイヤーのx位置。左から6マス目ですね。
        player.y = 10 * 16 + 8; // プレイヤーのy位置。上から10マス目よりちょっと下ですな。
        var image = new Surface(48, 96); // プレイヤー画像用のSurfaceを作成。
        image.draw(game.assets['player.gif'], 0, 0, 48, 96, 0, 0, 48, 96); // 画像の一部をSurfaceに読み込み。
        player.image = image; // それをプレイヤー画像にします。

ま、xとyの配置はいいですよね。

↑ こういうことです。

さて次のSurfaceが若干複雑です。

まず、元画像(player.gif)を見てみましょう。

↑ これです。

これだけ見ると、ちょっとでもenchant.jsをいじったことがある人なら次のようなコードでいいんじゃね?と思っちゃいますよね。

game.preload('player.gif');
var player = new Sprite(16, 24);
player.image = game.assets['player.gif'];

ただ、あとのコードを読み解くとわかるのですが、実はこのサンプルゲームでは、この画像の左半分しか使っていないのです・・・。

そのためにいったんSurfaceを経由して左半分だけの画像をつくり、それをplayerにセットしている、という裏技を使っています。

ちなみに「Surfaceって何?」と思う人が多いかと思いますが(ドキュメントにもあまり説明がないので)、これはHTML5のCanvasのラッパーです。なのでdrawではCanvas的な文法になります。

context.drawImage(image, sx, sy, sw, sh, dx, dy, dw, dh)
imageの(sx,sy)地点からsw分の幅、sh分の高さの画像を、(dx,dy)地点のdw幅、dh高さの領域へコピー。

なぜこうしたのかの理由は「元画像がそう作ってあるから」というものだと思いますが、テクニックとしてはおもしろいですね。覚えておきたいところです。

さて次にいきたいところですが、ちょっと出かけねばいけないので続きはのちほど・・・。