イベント
これで、実行時に自由にHTMLドキュメント内の要素を変更できるようになりました。これまでのプログラムは、BODY要素の子要素としてSCRIPT要素を記述し、HTML ドキュメントが読み込まれている途中にスクリプトを実装してきました。しかし、ゲームはユーザーの操作や入力に反応してドキュメントを変更させる必要があります。これには、要素オブジェクトのイベントを利用します。
イベントに反応することで、マウスがクリックされた時にスクリプトを実行できるようになります。イベントは、HTML要素の属性として指定することができます。処理できるイベントは要素ごとに異なりますが、マウスやキーボードなどのイベントについては多くの要素で共通しています。onから始まるイベント属性に、次のようなものがあります。
| イベント名 | 発生条件 |
onclick | オブジェクト上でクリックされたときに発生する。 |
ondblclick | オブジェクト上でダブルクリックされたときに発生する。 |
onmousedown | オブジェクト上でマウスボタンが押されたときに発生する。 |
onmouseup | オブジェクト上でマウスボタンが離されたときに発生する。 |
onmousemove | オブジェクト上でマウスカーソルを動かしたときに発生する。 |
onmouseover | オブジェクト上にマウスカーソルが入ったときに1度だけ発生する。 |
onmouseout | オブジェクト上からマウスカーソルが外に出たときに発生する。 |
onkeydown | ユーザーがキーを押したときに発生する。 |
onkeyup | ユーザーがキーを離したときに発生する。 |
onload | オブジェクトがブラウザに読み込まれた直後に発生する。 |
この他にもいくつかのイベントが要素オブジェクトごとに存在しますが、代表的なイベントは上記の表にあるようなマウスやキーボード関連のイベントでしょう。onloadイベントはBODY要素やIMG要素に指定し、ブラウザにドキュメントやイメージが完全に読み込まれたときに実行させたいスクリプトに使います。
イベントに反応して起動するスクリプトをイベントハンドラと呼びます。イベントハンドラとなる関数を要素と関連付ける方法はいくつかありますが、最も簡単で代表的な方法はHTMLテキスト上で要素の属性としてイベントハンドラを記述する方法でしょう。例えば、イメージ上で発生したクリックイベントを処理するには次のように記述します。
<img src="ソースURL" onclick="イベント処理スクリプト">
onclick属性の値は実行するスクリプトそのものです。ここに直接スクリプトコードを記述することもできますが、HTMLソースの可読性を考えると、HEAD要素内などでスクリプトの関数を作成して、イベント発生時に対応するメソッドを呼び出したほうがスマートです。
<html> <head> <meta http-equiv="Content-Type" content="text/html; charset=shift_jis"> <title>サンプル 05</title> <script language="javascript"> function imgMouseDown() { label.innerHTML = "マウスボタンが押されました"; } function imgMouseUp() { label.innerHTML = "マウスボタンが離されました"; } function imgMouseOver() { label.innerHTML = "マウスカーソルが入りました"; } function imgMouseLeave() { label.innerHTML = "マウスカーソルが外に出ました"; } </script> </head> <body> <p id="label">ここにイベント情報を表示します</p> <img src="test00.jpg" onmousedown="imgMouseDown()" onmouseup="imgMouseUp()" onmouseover="imgMouseOver()" onmouseleave="imgMouseLeave()" > </body> </html>
このプログラムは、表示されているイメージ上でカーソルを動かしたり、マウスボタンを押すと、ユーザーの入力に反応してイベントが起動し、P要素のテキストが動的に変更されるというプログラムです。例えば、マウスのボタンをイメージ上で押すと、IMG要素のonmousedown属性に記述しているスクリプト(イベントハンドラ)が実行されます。
この他に、SCRIPT要素で直接イベントを処理するスクリプトコードを宣言してしまう方法があります。HTMLはデータ構造を記述するためのデータ記述言語なので、プログラムコードを記述するプログラミング言語との相性は良いものではありません。この記法はあまり見かけませんが、HTMLのデータ構造とプログラムを分離したいときに使うことができます。SCRIPT要素でイベントハンドラを定義するには次のように記述します。
<script for="オブジェクト名" event="イベント" language="JavaScript"> //イベント処理スクリプトコード </script>
for属性には、このイベントハンドラの対象オブジェクトのid属性と一致する識別子を指定します。event属性には処理するイベントを指定します。例えば、for属性にlabel、event属性にonclickを指定したすると、labelという名前の要素オブジェクト上で発生したクリックイベントを定義することになります。
<html> <head> <meta http-equiv="Content-Type" content="text/html; charset=shift_jis"> <title>サンプル 06</title> <script for="image" event="onmousedown" language="javascript"> if (image.alt != "test00.jpg"){ image.src = "test00.jpg"; image.alt = "test00.jpg"; } else { image.src = "test01.jpg"; image.alt = "test01.jpg"; } </script> </head> <body> <p>イメージをクリックしてください</p> <img src="test00.jpg" id="image" alt="test00.jpg"> </body> </html>
このプログラムは、表示されているイメージをクリックするとsrc属性を変更してイメージを切り替えるというプログラムです。BODY要素の中を見ると、IMG要素にはonmousedown属性を指定していません。その代わりにHEAD要素内にスクリプトを記述し、SCRIPT要素のfor属性とevent属性で、処理対象のオブジェクトとイベントを指定しています。
この記法は、HTMLデザイナとスクリプタが異なる人物で、組織的に開発を進める際に採用するべきです。デザイナは専門外のスクリプトの記述を避け、専門とするデザインに集中することができ、スクリプタもデータ構造に関係なく目的のコードの記述に専念することができます。
実際には、イベントは要素オブジェクトが保有するプロパティの一種です。onから始まるイベント用プロパティは、イベントハンドラとなる関数へのポインタを保存しているだけなので、スクリプト実行時にイベントハンドラをオブジェクトに与えたり、イベントハンドラを組み替えることができます。
<html> <head> <meta http-equiv="Content-Type" content="text/html; charset=shift_jis"> <title>サンプル 06</title> <script language="javascript"> function image_Click1() { image.src = "test00.jpg"; image.onclick = image_Click2; } function image_Click2() { image.src = "test01.jpg"; image.onclick = image_Click1; } </script> </head> <body> <p>イメージをクリックしてください</p> <img src="test00.jpg" id="image" onclick="image_Click2()"> </body> </html>
これは前のプログラムと同じように、イメージをクリックすると表示されるイメージを切り替えるプログラムですが、実装方法が異なっています。IMG要素では、onclick属性を指定してクリックイベント発生時にimage_Click2()関数を呼び出すように設定していますが、image_Click2()関数ではimageオブジェクトのonclickイベントプロパティを書き換えてimage_Click1()関数を代入しています。これは、image_Click1()関数のポインタをonclickプロパティに与えています。その後、imageオブジェクトで発生したクリックイベントはimage_Click1()関数を呼び出すようになります。同様にimage_Click1()関数が実行されるとonclickプロパティが再び書き換えられてimage_Click2()が呼び出されるようになります。実行時にイベントハンドラを変更したい場合に、この方法を使うことができます。
イベント情報
イベントハンドラを設定することで、クリックやマウスカーソルの移動など、ユーザーの入力にプログラムが反応できるようになりました。ただし、これだけでは不十分な場合が多いでしょう。マウスのクリックや移動に関するイベントを処理する時には、どのボタンが押されたのかや、カーソルの座標などの情報が必要になります。キーイベントであれば、入力されたキーの文字が何かを取得する必要があるかもしれません。
こうした情報を取得するには、イベントハンドラ内でeventオブジェクトを使います。eventオブジェクトは発生したイベントの情報を提供するオブジェクトです。そのため、イベントハンドラ内でのみ有効なオブジェクトとなります。例えば、イベント発生時のマウスのX座標はxプロパティで、Y 座標はyプロパティで取得します。この座標は、イベントが発生したオブジェクトの左上隅を0とする相対座標です。
<html> <head> <meta http-equiv="Content-Type" content="text/html; charset=shift_jis"> <title>サンプル 08</title> <script language="javascript"> function img_MouseMove() { label.innerHTML = "MouawMove: X=" + event.x + ", Y=" + event.y; } </script> </head> <body> <p id="label">イメージ上でカーソルを動かしてください</p> <img src="test00.jpg" onmousemove="img_MouseMove()"> </body> </html>
このプログラムは、表示したイメージ上でマウスカーソルが移動すると、P要素の内部テキストとしてイベント発生時のマウス座標を表示します。img_MouseMove()関数内で、eventオブジェクトからxとyプロパティの値を参照していることに注目してください。
他にも、例えばクリックイベントで押されたボタンが何かを知りたい場合は、buttonプロパティから取得することができます。buttonプロパティは0~4までの数値で押されているボタンを表します。
| 値 | 状態 |
| 0 | どれも押されていない。 |
| 1 | 左ボタンが押された。 |
| 2 | 右ボタンが押された。 |
| 4 | 中央のボタンが押された。 |
キー入力イベントであれば、押されたキーの文字コードをkeyCodeプロパティから取得することができます。こうした座標やキー情報は、ゲーム画面上でユーザーが操作するキャラクターを移動させるなどの処理に利用できるでしょう。
サンプルゲーム
さて、今回はJavaScriptとDHTMLによる動的なページの開発方法について、その中でも特に基本的な内容を解説してきました。これまでのまとめとしてタイピングゲームを作成します。タイピングゲームは、画面上に表示されているアルファベット文字列の先頭と同じキーが押されると先頭の文字が削除される簡単な内容です。そのため、キーイベントの処理と、乱数でゲームを作ることができます。
タイピングゲームでは、タイプターゲットとなる文字列を表示するためのP要素をドキュメント上で表示して、これにlabelという名前をID属性で指定するものとします。このP要素に表示している文字列の先頭のアルファベットと同じキーがタイプされると、文字列から先頭の一文字だけを取り除いて、残りの文字を再表示させます。P要素の内部テキストの変更にはinnerTextプロパティを使えばよいでしょう。
<html> <head> <meta http-equiv="Content-Type" content="text/html; charset=shift_jis"> <title>サンプル 09</title> <script language="javascript"> var successful_count = 0; var failure_count = 0; var typeTargets = new Array( "LOVE", "HAVE", "GO", "DO", "MAKE", "TAKE", "GIVE", "GET", "SAY", "COME", "BE", "SEE", "LOOK", "KNOW", "THINK", "SEEM", "LIKE", "ASK", "TALK", "SPEAK", "HEAR", "LISTEN", "USE" ); function init() { label.innerHTML = next(); } function keyDown() { var text = label.innerHTML; var code = text.charCodeAt(0); if (event.keyCode == code) { text = text.substring(1, text.length); if (text.length == 0) text = next(); label.innerHTML = text; successful_count++; } else failure_count++ successful.innerHTML = successful_count; failure.innerHTML = failure_count; } function next() { var index = Math.round(Math.random() * (typeTargets.length - 1)); return typeTargets[index]; } </script> </head> <body onkeydown="keyDown()" onload="init()"> <table border="0" width="100%" height="100%"> <tr><td>成功数=<span id="successful">0</span> , 失敗数=<span id="failure">0</span></td></tr> <tr><th height="100%"><font size="7"><p id="label"></p> </font></th></tr> </table> </body> </html>
ページ上で発生したキー入力イベントを受け取るために、BODY要素にonkeydown属性を設定しています。この属性では、キー入力時のイベントハンドラとなるkeyDown()関数を呼び出します。keyDown()関数は、eventオブジェクトを調べて、label.innerHTMLプロパティの先頭文字とコードが等しいかどうかを調べ、等しければ文字列オブジェクトからsubstring()関数を使って先頭の文字だけを取り除きます。
もし、substring()の結果、全ての文字が削除されていればnext()関数を呼び出して次に表示するべき文字列をランダムに取得します。この結果をlabel.innerHTMLプロパティに設定することで、画面上では次の単語が表示されます。画面に表示される文字列はnext()関数がtypeTargets配列オブジェクトの中から乱数を使ってランダムに選び出します。
最後に
今回作成したプログラムは、どれもHTMLドキュメントの静的な構造に、スクリプトから局所的な変更を加えるというものでした。ゲーム特有の画面を縦横無尽に動き回る演出や、BGM、効果音など、ゲームに不可欠な要素はどれも実現できていません。しかし、今回使ったinnerHTMLプロパティなどを駆使すれば、実行時に、自由にHTMLドキュメントの内容を変更することができるため、CSSによるレイアウト技術や画像演出と組み合わせることで、リッチなゲームを実現することができるようになるでしょう。
参考資料
- MSDNライブラリ 2005年4月
