2-6 凝ったゲームを完成させる

これまで学んだ技術の集大成として「体当たりで相手を弾き飛ばすアクションゲーム」を完成させます。 内容は、4人の中から好きなキャラクターを選び、相手キャラを爆弾に触れさせ体力を奪い、最後まで残ったキャラが勝ちとします。 ゲーム中にBGMを流し、ユーザーが勝った時に勝利ジングル、負けた時に敗北ジングルを鳴らす処理も実装します。

index画面内容
0初期化
画像の読み込み
マップデータをセットする
1タイトル
キャラクターを選ぶとゲーム開始
ゲームのルール説明を見られるボタンを配置
2ゲーム開始演出
3、2、1、GO!と表示
3ゲームメイン
ユーザーはキャラ1体を動かす
他の3体はコンピューターが動かす
キャラ同士がぶつかると互いに弾き飛ばされる
爆弾が次々に出現する
爆弾に触れると体力が減り、体力0で動けなくなる
最後まで残ったキャラの勝ち
4結果
勝者名を表示
タイトルに戻る
5ゲーム説明
ルールを表示する


(1)データのセーブとロード

本格的なゲーム制作に必要なデータのセーブとロードの処理を実装しましょう。 今回はユーザーが勝った回数を記録します。 ユーザーが勝った時に回数をカウントして保存し、再度プレイした時にその値を読み込むようにします。
HTML5ではデータのセーブ、ロードをローカルストレージで実現します。 ローカルストレージを使うと、パソコンやスマートフォンの本体に変数の値を保存し、それを読み込むことができます。 ローカルストレージはキーと値を指定して保存や読み込みを行います。 今回は次のような、ローカルストレージを扱う汎用的な関数を用意しました。

var LS_KEYNAME = "ShiningStar";
function saveLS( kno, val ) {//保存する
 try {
  localStorage.setItem( LS_KEYNAME+kno, val );
 }
 catch( e ) {}
}

function loadLS( kno ) {//読み込む
 var val;
 try {
  val = localStorage.getItem( LS_KEYNAME+kno );
 }
 catch( e ) {}
 if( val == null ) val = 0;
 return val;
}

function clrLS( kno ) {//削除(クリア)する
 localStorage.removeItem( LS_KEYNAME+kno );
}

LS_KEYNAMEはキーの名称を決めるために用意した変数で、今回はゲームタイトルとしています。キーは各プログラムにおいて自由に設定できます。
localStorage.setItem(キー,値)が値を保存する命令、localStorage.getItem(キー)が値を読み込む命令です。 localStorage.removeItem(キー)で指定したキーで保存した値を削除できます。

ローカルストレージが使えない時のためにsetItem、getItemそれぞれに try~catch文 を記述しています。 try~catch文は例外処理といわれるもので、

try{ 例外が起きる可能性のある処理 } catch( e ) { 例外が起きた時の処理 }

と記述します。例外が起きた時の処理の記述は省略できます。
例外とはエラーのことです。ソフトウェアはエラーが発生すると通常、動作を停止しますが、try~catch文を使えばエラーが発生しても処理を進めることができます。 その際 catch の()内に記述した e にエラーの情報が入りますので、それを調べることでエラーの中身が判ります(今回はeを調べる必要はありません)。 try~catch文は主に開発中にバグを修正するために使いますが、エラーが起きることが予想できる場合、その処理に記述しておくと役に立ちます。 具体的にはiPhoneのSafariを「プライベートブラウズモード」で使うと、ローカルストレージにアクセスできなくなり、 localStorage.setItem()やlocalStorage.getItem()の命令でプログラムが停止します。 try~catchを入れておくことでこれを回避し、停止することなく処理を進めることができます。

ローカルストレージの補足
HTML4.0.1までブラウザでデータを保存するには、クッキーを使うか、サーバーにデータを送る仕組みが必要でした。 クッキーは有効期限が決まっており、サーバーにデータを送るにはサーバー側のプログラムも用意しなくてはなりません。 HTML5ではローカルストレージが追加され、簡単にセーブ、ロードができるようになりました。 HTML5にはセッションストレージ(sessionStorage)も用意されていますが、 これはブラウザが閉じられた時点でデータが消去されますので、 ゲームのデータのように永続的な保存が必要なものはローカルストレージを使います。



(2)「 体当たりアクション Shining Stars 」の完成

それではゲームを見てみましょう。
example261.html ← 動作の確認(ゲーム中にBGMが流れます)
ソースコードの確認 ← ソースコードは550行程度あります。こちらでご確認下さい

使っている変数を列挙します。
変数名用途詳しい説明
idx,tmrインデックスとタイマープログラム全体の流れを管理
pl_chrユーザーが操作するキャラの番号値0がランドというキャラ、1フラワー、2シェリー、3ナイト
chrX[],chrY[]各キャラの座標配列で4体分管理する
chrXP[],chrYP[]各キャラの移動量(速度)X方向、Y方向それぞれの移動量
chrDI[]各キャラの向き0上、1下、2左、3右
chrLife[]各キャラの体力最大値100、0になると動けなくなる
chrWins[]各キャラの勝利回数ユーザーが操作するキャラが勝った回数
ローカルストレージに保存、読込
winner勝者の番号最後まで生き残ったキャラの勝ち
bombX,bombY爆弾の位置新たに出現する爆弾の位置
bombT爆弾の出現時間出現するまでの時間を管理
map[][]ステージの構成(BG)壁と床を定義
爆弾がある床も定義

map[][]の値は0~9までが床(今回使っているのは0と1のみ)、10からが壁で、 マップチップの値と画像の関係は次のようになっています。


以下が今回のソースコードに実装したポイントです。

【ポイント1】 二次元配列をforループで作る

var map = new Array();
for( var y = 0; y < 20; y ++ ) {
map[y] = new Array();
for( var x = 0; x < 20; x ++ ) map[y][x] = 0;
}

JavaScriptの二次元配列は 2-4 で説明した配列宣言時に初期値を入れて定義する他に、このような方法で定義できます。

【ポイント2】 ソート処理を キャラのY座標×10+キャラの番号 で行う

var sortN = [];
function sortChr() {//ソート処理
 var i, j, t;
 for( i = 0; i < 4; i ++ ) sortN[i] = toInt(chrY[i])*10+i;
 for( i = 0; i < 3; i ++ ) {
  for( j = 0; j < 3-i; j ++ ) {
   if( sortN[j] > sortN[j+1] ) {
    t = sortN[j]; sortN[j] = sortN[j+1]; sortN[j+1] = t;
   }
  }
 }
}

2-5 で解説したように、正しい表示順番をソートで決めます。 ソートするための配列を 座標用のsortY[], キャラ番号用のsortCN[] と2種類用意しても良いのですが、 sortN[] = Y座標の10倍+キャラの番号とすれば、1種類の配列でソートできます。 ソート処理後、sontN[]を10で割った余り(sontN[]%10)が、Y座標の小さな値から順に表示するキャラクターの番号になります。

【ポイント3】 ブラウザのなるべく大きな範囲を使う & ブラウザの余白を画像で埋める
スマートフォンやタブレットは持つ方向(縦持ち、横持ち)、パソコンのブラウザはどれだけ広げているかで、ブラウザのサイズと形状はまちまちです。 今回のゲーム画面の比率はW640xH480としており、この比率の画面をなるべく大きく表示するため、 ブラウザの縦横サイズを調べてキャンバスのサイズを決めています。
横長のブラウザでの画面例縦長のブラウザでの画面例
ソースコードは次の部分で、 winH < winW*CHEIGHT/CWIDTH が成り立つならキャンバスの高さをブラウザの高さとし、 成り立たないならキャンバスの幅をブラウザの幅としています。

var winW = window.innerWidth;
var winH = window.innerHeight;
var canvas = document.getElementById("bg");
var cnt = canvas.getContext("2d");
var SCALE;
var CWIDTH = 640;
var CHEIGHT = 480;
if( winH < winW*CHEIGHT/CWIDTH )
 winW = toInt(winH*CWIDTH/CHEIGHT);
else
 winH = toInt(CHEIGHT*winW/CWIDTH);
canvas.width = winW;
canvas.height = winH;
SCALE = winW / CWIDTH;
cnt.scale( SCALE, SCALE );


ブラウザ内の余白に何も表示しないと画面が寂しく見えてしまいます。 今回はbody要素のスタイルの記述で星空の画像を並べるようにしています。

<body style="background-image:url('example261_2.png');">


【ポイント4】 ユーザーが使うキャラを有利にする
キャラクター同士の衝突を判定する処理で、 ユーザーが操作するキャラは相手を強く弾き飛ばせるようにしています。 以下がそのソースコードです。

var foruser = ( pl_chr == p ) ? 1.2 : 1.0;

及び

chrXP[i] = toInt( xp*foruser );
chrYP[i] = toInt( yp*foruser );

( 条件式 ) ? 値1 : 値2 という記述を条件演算子といい、条件式がtrueなら値1、falseなら値2をとります。 上記のコードでは、ぶつかるキャラ(変数p)がユーザーの操作するキャラ(pl_chr)であれば、foruserの値が1.2になります。 そして衝突した2つのキャラの移動量の交換で、相手の移動量をユーザーキャラの1.2倍にすることで、相手をより強く弾き飛ばせるようにしました。 これでユーザーは相手キャラにぶつかりながら押していくことができ、爆弾の床に相手を押しやるテクニックが使えるようになっています。
この他にユーザーキャラの移動量の増減は4ずつ、コンピューターキャラのそれは3ずつとし、ユーザーキャラのほうが機敏に動けるようにしています。

◇コラム◇ 一昔前のゲーム、今のゲーム
家庭用ゲームソフトの中にはゲームクリアとなる条件が高く設定されており、しっかりやり込まないと途中で行き詰るものがあります。 特にファミコン、スーパーファミコン、そしてプレステの時代までは、苦行のようなことを繰り返してやっとエンディングに辿り着けるものが多数ありました。 今ではそのようなゲームはあまり見られなくなり、ライトユーザーが気軽にプレイできるゲームが増えました。 今回完成させたゲームはポイント4のようにユーザーが有利になる仕組みを入れましたが、それでもけっこう難しいかもしれません(笑)




使っている画像は以下の3つです。
example261_0.png
example261_1.png
example261_2.png


(3)完成させたゲームについてあれこれ

これまで解説したJavaScriptとHTML5、そしてゲームを制作するために必要な知識を総動員し、ゲームを完成させました。 初めてプログラミングを学ばれている方にはソースコードに難しい部分があるかもしれませんが、 コメント文や本項の説明をご参考に、どのような処理を行っているか、ぜひ読み解いて下さい。
今回のソースコードは約550行、16kバイト程度の容量です。 500行以上もあると驚かれる方もいらっしゃるかも(?)しれませんが、 市販されるゲームソフトと比べると随分短いソースコードです。 例えば弊社では多くのスマートフォン用のRPGを開発しましたが、 一つのゲームのソースコードは(各タイトルでまちまちですが)3万行から5万行、 ファイルサイズはソースコードだけで1000k~2000kバイトあります。 そのような大きなソースコードをいきなり書くことは難しいですが、 プログラミングを続けていくと習得した技術を次の開発で使うことができ、 だんだんと大きなソースコードが書けるようになります。

今回完成させたゲームには敵が考えて行動する処理は入っていません。 それでも遊んでみると、敵に体当たりされて爆弾の位置に入ってしまい、 あたかも敵が考えて(ユーザーを狙って)行動しているように感じられると思います。 更に高度なゲームを作るには敵の思考ルーチンなども必要になりますが、 シンプルなゲームであれば複雑なソースコードを書かなくても、それなりに敵(コンピューター)に行動させることができます。
また今回はどのキャラクターも能力差は無く、プレイヤーが選んだキャラが少し性能が良い仕様になっています。 キャラクターごとに体力や移動速度の違い、相手を跳ね飛ばす強さの違いなどを設ければ、より本格的なゲームになります。 さらに各キャラクターに固有の必殺技を用意すれば市販ソフト並み(?)になりそうです(笑)
絵を用意できる方はオリジナルの画像に差し替えてみるのも面白いです。 ご自身でこのゲームを改良されることも良い勉強になりますので、ぜひ色々いじって頂ければと思います。

最後に1点補足を。音(BGMとジングル)はパソコンとiOS端末で正常に鳴りますが、Androidでは鳴りません。 1-10 で解説したサウンド出力のテクニックを発展させた、 Androidでも音を出す方法がありますが、ソースコードが複雑になるため実装を見送りました。

本ソースコードを元に作られた改良ゲームは、画像を全て差し替えたものに限り、ご自由に配信されてかまいません。 サウンドはそのまま使って問題ございません。 改良ゲームを作られた方は下記のメアドにご連絡下さい。ぜひ遊ばせて頂きたいと思います。

そして、ここまで学んで下さったあなたは、色々なゲームが作れるようになっているはずです。 ゲームはアイデア次第。ご自身の手でぜひ新たなゲームを完成させて下さい。



前のページへ / 次のページへ

お気軽にお問い合わせ下さい →