■補講 Canvasを使ったゲーム(レーダー表示タイプのゲーム)
この補習講義ではドットイート型のゲームをベースにし、レーダー表示タイプのゲームのゲームを作成します。レーダー表示タイプのゲームはナムコ(現バンダイナムコゲームス)のラリーX (ニューラリーX)、ボスコニアンがあります(1980年代のゲームなのでかなり古いですが)
●ラリーX
http://ja.wikipedia.org/wiki/ラリーX
●ボスコニアン
http://ja.wikipedia.org/wiki/ボスコニアン
これらのゲームは自機/自車が常に画面の中心に表示されており、レバーを入れると背景画面が移動するようになっています。そこで、この補習で作成するゲームも自分のキャラクタ(自車)が中央に表示され、背景が移動(スクロール)するようにします。また、画面右側には自分の位置と拾うべきお金の場所をレーダーで表示します。
このゲームでは自分のキャラクタは車になっています。ドットイート型とは異なり一度押したキーの方向に移動したままになります。さらに、壁(ブロック)に当たった場合には自動的に移動可能な方向に走行するようにします。
用意しているラウンドは3つで、これは以前説明したドットイート型のゲームと同じです。
また、ゲームでも敵は出てこないので適車を出すようにしてみると面白いでしょう。
今回はドットイート型のゲームをベースにしていますので、重複する部分の解説はなるべく省略しています。
------------------------------------------------------------------------------------------------
■ラウンドデータの準備
まず、3つのラウンドのデータを用意しておきます。ドットイート型のゲームと同じようにdata.jsファイルに入れておきます。ラウンドのデータもドットイート型と同じ変数名でroundMap配列に入れます。
ラウンドのデータは0、1、2の文字で示し以下のように対応しています。
【表】
0 道路
1 壁。キャラはこれをすり抜けることはできない
2 お金
ラウンドデータは012の文字で表現し横は30文字、縦は47行で用意します。これは実際に表示される範囲よりもかなり大きいものになります。これはゲームの背景がスクロールするようにするため、実際に表示される範囲よりも大きくなくてはいけません。実際に表示されるのは320×480ピクセルになります。ブロック数にすると横10個×縦15個になります。
------------------------------------------------------------------------------------------------
■自車と背景、レーダー画面の構成
今回のゲームでは自車は常に画面中央に表示されます。そこで、これまでのようにCanvasに自車を描画するのではなくimg要素を使って中央に表示することにします。つまり、Canvas面の上に画像を表示することになります。HTML5ならではの方法です(ゲーム機/基板の場合はスプライトで処理するので、まあ似ていなくはありませんが)。
また、レーダー画面も用意する必要がありますのでHTMLは以下のようになります。
自車の表示位置はスタイルシートで設定しておきます。
#car {
position: absolute;
left: 192px;
top: 256px;
z-index: 2;
}
------------------------------------------------------------------------------------------------
■初期化
それでは初期化部分を見てみましょう。自車の座標と集金したお金の数、ラウンドデータ、マップの表示位置をし示す座標等を入れるプロパティを設定しています。
キーが押された時にキーの番号を入れるkeyプロパティには38を入れておきます。この38というのはカーソルキーの↑が押された時のキー番号です。このように設定しておくとゲームがスタートした後、自車が上に向かって走行することになります。
今回のゲームは自車が画面中央にあるため、移動するのは背景(マップ)になります。この背景の表示場所を変えることで画面が移動(スクロール)しているように見せることができます。この表示位置を示す座標を入れるのがmapX、mapYプロパティです。画面を移動させる場合は、このmapX, mapYプロパティの値を変えます。
var game = {
round : 1, // ゲームの面(ラウンド)
carX : 6, // 自車のマップX座標からのオフセット
carY : 8, // 自車のマップY座標からのオフセット
key : 38, // 押されたキーのコード(最初は↑キーと同じにする。つまり上方向に走行)
roundData : new Array(), // ラウンドマップのデータを格納する配列
moneyCount : 0, // 金の総数
charSize : 32, // 画像の幅(32×32)
mapX : 1, // マップの描画位置(X座標)
mapY : 30, // マップの描画位置(Y座標)
mapWidth : 30, // マップの横幅(roundMap[0][0].length)
mapHeight : 47 // マップの縦幅(roundMap[0].length)
};
------------------------------------------------------------------------------------------------
■ページ読み込み後の処理
ページが読み込まれた後にはドットイート型のゲームと同じように敵の座標の初期化とキーダウンイベントを設定します。
今回はレーダーがあるため、そのCanvasのコンテキストを取得しraderContextに入れておきます。また、ラウンド1のマップデータを処理するためにinitMapData()関数を呼び出します。
あとは、これまでのゲームと同様にタイマーを起動します。なお、0.1秒では処理速度が速く難しいかもしれません。0.2秒や0.3秒ごとに処理するようにした方がよいかもしれません。
window.addEventListener("load", function(){
var canvasObj = document.getElementsByTagName("canvas")[0];
context = canvasObj.getContext("2d");
raderContext = document.getElementById("rader").getContext("2d"); // レーダー画面用Canvas
window.document.addEventListener("keydown", moveMyChar, false);
initMapData(game.round); // 最初はラウンド1
timerID = setInterval("moveCar()", 100);
}, true);
------------------------------------------------------------------------------------------------
■ラウンドのデータをマップデータ配列に
initMapData関数ではマップデータの初期化のみ行い描画は行いません。これは自車が中心に表示され背景が移動するタイプのため、移動するたびに背景を描画する必要があるためです。このため、初期化部分と描画部分は分離されています。
関数内では最初に集めるお金の合計数を0にしておきます。
function initMapData(n){
game.moneyCount = 0; // お金の合計
次にラウンドデータをgame.roundData配列に入れます。そのままラウンドデータを使っては駄目なのかと思う人がいるかもしれません。これは元のラウンドデータを書き換えてしまうと、自車がやられた場合などにマップを元の状態に戻せなくなってしまうためです。今回は敵車がいないので、自車がやられるような事はありませんが、元データは書き換えず一時的に処理する配列等に入れるのがよいでしょう。
ラウンドデータは文字列になっているので、それぞれ1文字ごとに分解した後game.roundData配列に入れていきます。また、ラウンドデータが"2"の場合はお金ですからgame.moneyCountに1を加算して、お金の総数を求めます。
for(var y=0; y 40){ game.key = 37; }
これは非常に手抜きな方法で、カーソルキーが擬似的に押された事にしているのです。カーソルキーの押下番号は37〜40ですから、その範囲で値を変化させます。すると、ブロックがない方向に自動的に走行を開始します。
これでレーダー表示タイプのゲームの完成です。今回のゲームのように自分の操作するキャラクタが画面中央に表示されているゲームは多くあります。このサンプルゲームを元にして、いろいろなゲームを作成してみるとよいでしょう。マップをRPGにするだけでも雰囲気が変わります。また、Web Socketを利用して他のユーザーのキャラクタも動き回れるようにすると面白いかもしれません。
------------------------------------------------------------------------------------------------
■HTML (index.html)
------------------------------------------------------------------------------------------------
レーダータイプのゲーム
------------------------------------------------------------------------------------------------
■JavaScript(rader.js)
------------------------------------------------------------------------------------------------
// 集金ゲーム2(レーダー画面タイプのゲーム)
// Game用の変数
var context = null;
var raderContext = null;
var timerID = null;
var game = {
round : 1, // ゲームの面(ラウンド)
carX : 6, // 自車のマップX座標からのオフセット
carY : 8, // 自車のマップY座標からのオフセット
key : 38, // 押されたキーのコード(最初は↑キーと同じにする。つまり上方向に走行)
roundData : new Array(), // ラウンドマップのデータを格納する配列
moneyCount : 0, // 金の総数
charSize : 32, // 画像の幅(32×32)
mapX : 1, // マップの描画位置(X座標)
mapY : 30, // マップの描画位置(Y座標)
mapWidth : 30, // マップの横幅(roundMap[0][0].length)
mapHeight : 47 // マップの縦幅(roundMap[0].length)
};
// ページが読み込まれた時の処理
window.addEventListener("load", function(){
var canvasObj = document.getElementsByTagName("canvas")[0];
context = canvasObj.getContext("2d");
raderContext = document.getElementById("rader").getContext("2d"); // レーダー画面用Canvas
window.document.addEventListener("keydown", moveMyChar, false);
initMapData(game.round); // 最初はステージ1
timerID = setInterval("moveCar()", 100);
}, true);
// 移動&表示処理
function moveCar(){
var tx = game.mapX, ty = game.mapY; // 自分の座標(一時的に利用する)
raderContext.clearRect((game.mapX+game.carX)*5, (game.mapY+game.carY)*5, 5, 5);
// 自車の移動処理(マップ内で移動させる。自車は常に画面の中央なので)
if (game.key == 37){ tx = tx - 1; }
if (game.key == 39){ tx = tx + 1; }
if (game.key == 38){ ty = ty - 1; }
if (game.key == 40){ ty = ty + 1; }
document.getElementById("car").className = "key"+game.key; // クラス名で車の回転方向を決める
var mp = (tx+game.carX)+(ty+game.carY)*30; // マップ上での自分の位置を算出。30はマップ内での横幅
if (game.roundData[mp] != "1"){
game.mapX = tx;
game.mapY = ty;
if (game.roundData[mp] == "2"){
game.roundData[mp] = "0"; // お金を集金したので0にして空にしておく
game.moneyCount = game.moneyCount - 1;
if (game.moneyCount < 1){
alert("ラウンドクリア");
game.round = game.round + 1; // ラウンド数に1を足す
if (game.round >= roundMap.length){ // 最終面をクリアした
clearInterval(timerID); // タイマーをクリア
alert("全面クリアしました");
return;
}
// 次にラウンドデータを表示する
initMapData(game.round);
game.mapX = 1; // キャラクタの位置を再度設定する
game.mapY = 30;
game.key = 38; // 最初は上方向に移動(走行)
return;
}
}
}else{
// ブロックで進めない時の処理
game.key = game.key + 1;
if (game.key > 40){ game.key = 37; }
}
drawMapData();
// レーダー画面に自車を表示
raderContext.fillStyle = "red";
raderContext.fillRect((game.mapX+game.carX)*5, (game.mapY+game.carY)*5, 5, 5);
}
// 自分の移動処理
function moveMyChar(evt){
game.key = evt.keyCode;
}
// ラウンドマップを初期化
function initMapData(n){
game.moneyCount = 0; // お金の合計
for(var y=0; y