Javascriptゲーム 04

ネズミを池の中へ落として追い出すゲーム。
マウスでネズミをポイント、クリック等するとランダムに動く。
チーズは、[Alt]+矢印キーで移動できる。 チーズを移動すると、ネズミが寄ってくる。 (Firefox用)
background
ねずみ
ねずみ
cheese

解説

ゲームらしくするために、チーズを導入した。チーズは、[Alt]+矢印キーで上下左右に移動可能である。チーズを移動するたびに、ネズミがチーズに近づく。チーズを使って、ネズミを池に誘い込むというゲームにした。

このために、以下の機能を追加しなければならない:

  1. [Alt]+矢印キーでチーズを上下左右に動かす。
  2. チーズが動いたとき、ネズミをチーズに近づける。
  3. ネズミがチーズを食べたときの処理
  4. チーズが池に落ちたときの処理

2のネズミをチーズに近づける処理を実装するために、ネズミがランダムに動くだけではだめである。 ネズミを指定した方向へ動かすことができるように、まずはJavascriptゲーム03のプログラムを書き換えるところから始めよう。

Javascriptゲーム03の書き換え [サンプル]

Javascriptゲーム 03では、onclick、mouseover、mouseup、mouseoutというイベントハンドラーの処理としてmove()関数を指定してランダムにネズミを移動していた。move()関数では、呼び出されるたびにgetElementById()関数で要素を取得していたが、これをグローバル変数として要素を表すnezumi1とnezumi2を用意して、移動のたびにgetElementById()関数を呼び出す必要がないようにした。

function coordinates(element)の定義で使用している

var position = {x: 0, y: 0};

は、Javascriptの連想配列の定義である。 これにより定義された連想配列positionは、キーxの値は0、キーyの値も0で初期化している。
例えば、連想配列positionのキーxの変数は、position.xと書く。Perlのようにposition['x']と書くこともできる(Perlの連想配列は$position{'x'}のように[]ではなく{}を使用するので、似ているというだけで同じではない)。

1. [Alt]+矢印キーでチーズを上下左右に動かす [サンプル]

イベントハンドラーと同様に、イベントの発生に対応して関数を呼び出す方法に、イベントリスナーを使用する方法がある。 キーボードのキーを押すというイベントの発生に対して、チーズを移動するという仕組みをイベントリスナーを使って実装しよう。

Firefoxでは、キーを押す(keydown)というイベントの発生に対してKeyDownFunc()という関数を呼び出すイベントリスナーは、

document.addEventListener("keydown" , KeyDownFunc);

によって設定して開始する。

function KeyDownFunc(e)の定義では、

//console.log("code:" + key_code);
//console.log("shift:" + shift_key);
//console.log("ctrl" + ctrl_key);
//console.log("alt:" + alt_key);

とコメントアウトしてある部分のコメントを解除すると、キーが押されたときにkey_codeの値をFirefoxのWebコンソールのロギングで確認できる。 これを利用して、[→]、[←]、[↓]、[↑]キーを押したときのキーのコードを調べて、チーズを動かす関数move_cheese(dx, dy)を呼び出して、チーズを動かしている。

2. チーズが動いたとき、ネズミをチーズに近づける [サンプル]

キーボードのキーを押してチーズを動かしたときに、ネズミをチーズに近づけるために動かす関数は、function move2cheese(element, Delta)で定義している。 本来はmove_to_cheeseという名前にすべきであったが、_to_で名前が長くなりすぎるので同じ発音の2を使った。プログラミングではtoの代わりに発音が同じ2を名前に使う場合がよくある。

function move2cheese(element, Delta)の定義のなかの

	if(cheese.style.display == "none" ){
		return;
	}
	if(element.style.display == "none" ){
		return;
	}

は、チーズが非表示の時、あるいはネズミ自身が非表示のときは、何もしないで戻るということで、ネズミを動かさないという意味である。

var vec = {x: q.x - p.x, y: q.y - p.y };

は、ネズミからチーズの方向へ向かうベクトルを表わす連想配列である。

var nv = Math.sqrt(vec.x * vec.x + vec.y * vec.y);

は、このベクトルの長さで、ネズミからチーズまでの距離である。 Deltaが、ネズミのチーズ方向への移動距離である。

function KeyDownFunc(e)の定義でmove2cheese(element, Delta)を呼び出すときに、

move2cheese(nezumi1, D_nezumi/2);

として、DeltaがD_nezumi/2になるように呼び出しているので、ネズミの移動量はD_nezumiの半分になっている。

3. ネズミがチーズを食べたときの処理 [サンプル]

関数function check_cheese(element)を、引数elementがチーズと重なる場合は1、そうでない場合は0を返すように定義している。
randomove(nezumi1, D_nezumi)やmove2cheese(nezumi1, D_nezumi/2)で、ネズミを動かしたとき、check_cheese()を呼び出してチェックしている。

// 位置が変更されたネズミがチーズとぶつかったか否かを判定
if( check_cheese(element) == 1 ){
	alert("Thanks for the cheese!");
	cheese.style.display = "none";
}

ネズミがチーズに重なったときは、cheese.style.display = "none"によって、チーズを非表示にしている。
チーズを非表示にするだけではなく、document.removeEventListner("keydown" , KeyDownFunc, true)を実行して、イベントリスナーを解除した方がよい場合もある。

4. チーズが池に落ちたときの処理 [サンプル]

チーズを動かす関数function move_cheese(Delta_x, Delta_y)では、ネズミと同様、チーズが穴(このバージョンでは、池)に落ちたか否かをチェックし、落ちた場合はチーズを非表示にしている。

// 位置が変更されたチーズが穴に入ったか否かを判定
if( check_hole(p.x, p.y) == 1 ){
	alert("Oops!");
	cheese.style.display = "none";
}

【参考】DOMでz-indexを指定する方法。

var element = document.getElementById("Nezumi1");
element.style.zIndex = "1";
のようにする。

CSSのプロパティz-indexと同じ名前を、DOMを使ってJavascriptで使うことはできない。なぜなら、Javascriptでは-は引き算のマイナスを意味してしまうため、z-indexはzという変数から indexという変数の値の引く引き算と見なされてしまうからである。Javascriptでは、変数名に_(アンダースコア)は使用可能なので、z_indexとしても良かったと思うのだが、DOMではzIndexとなっている(大文字と小文字の違いに注意すること)。