3-2 スマートデバイスならではのゲームを作る

HTML5では位置情報を取得したり、端末の傾きを検出することができます。 本項では端末を傾けて操作するスマートフォンやタブレットなど、スマートデバイスならではのゲーム制作を解説します。


(1)端末の傾きの検出

端末の傾きを検出するには加速度センサーに関する命令を用います。
まずはサンプルをご確認下さい。端末を傾けると、acX, acY, acZ の値が変化します。
example321.html ← 動作の確認
※スマートフォンやタブレットなどでご確認下さい
ソースコードは次のようになります。
01<!DOCTYPE html>
02<html lang="ja">
03<head>
04<meta charset="utf-8">
05<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0 user-scalable=no">
06<title>JavaScriptのテストプログラム</title>
07</head>
08<body>
09<canvas style="position:absolute; top:0; bottom:0; left:0; right:0; margin:auto;" id="bg"></canvas>
10
11<script>
12
13//キャンバスの準備
14var winW = window.innerWidth;
15var winH = window.innerHeight;
16var CWIDTH  = 480;
17var CHEIGHT = 480;
18if( winW < winH )
19 winH = winW;
20else
21 winW = winH;
22var SCALE = winW / CWIDTH;
23
24var canvas = document.getElementById("bg");
25var cnt = canvas.getContext("2d");
26canvas.width = winW;
27canvas.height = winH;
28SCALE = winW / CWIDTH;
29cnt.scale( SCALE, SCALE );
30cnt.textAlign = "center";
31cnt.textBaseline = "middle";
32
33function toInt( val ) {//整数を返す関数
34 return parseInt(val);
35}
36
37function fText( str, x, y, siz, col ) {//文字表示
38 cnt.font = siz + "px monospace";
39 cnt.fillStyle = col;
40 cnt.fillText( str, x, y );
41}
42
43function fRect( x, y, w, h, col ) {//矩形(塗り潰し)
44 cnt.fillStyle = col;
45 cnt.fillRect( x, y, w, h );
46}
47
48//加速度センサー
49var acX = 0, acY = 0, acZ = 0;
50
51window.addEventListener( "devicemotion", deviceMotion );
52
53function deviceMotion( event ) {
54 var aIG = event.accelerationIncludingGravity;
55 acX = toInt( aIG.x );
56 acY = toInt( aIG.y );
57 acZ = toInt( aIG.z );
58 if( (navigator.userAgent).indexOf("Android") > 0 ) {//Android と iOS で正負が逆になる
59  acX = -acX;
60  acY = -acY;
61  acZ = -acZ;
62 }
63 fRect( 0, 0, CWIDTH, CHEIGHT, "#EEE" );
64 fText( "acX "+acX, 240, 120, 32, "#F00" );
65 fText( "acY "+acY, 240, 240, 32, "#0C0" );
66 fText( "acZ "+acZ, 240, 360, 32, "#44F" );
67}
68</script>
69</body>
70</html>

傾きを検出しているソースコードを抜粋します。

window.addEventListener( "devicemotion", deviceMotion );

function deviceMotion( event ) {
 var aIG = event.accelerationIncludingGravity;
 acX = toInt( aIG.x );
 acY = toInt( aIG.y );
 acZ = toInt( aIG.z );
 if( (navigator.userAgent).indexOf("Android") > 0 ) {
  acX = -acX;
  acY = -acY;
  acZ = -acZ;
 }
}

マウスやタップ判定と同じように addEventListener を用います。 加速度センサーの場合は window に対し "devicemotion" を addEventListener し、傾きが変化した時に働く関数を指定します。
その関数で event.accelerationIncludingGravity により加速度を取得し、そのx属性、y属性、z属性(これらが傾きの値です)を変数に入れています。 傾きの値は少数となりますが、プログラムで扱いやすいように整数に変換しています。

ポイントは Android と iOS で取得した値の正負が逆になりますので、 navigator.userAgent で Android端末かどうか判定し、 その場合は値をマイナスにして、iOSとAndroidで同じ値となるようにしています。

【ポイント】 端末の種類を判定する

if( (navigator.userAgent).indexOf("機器") > 0 ) { その端末の処理 }

"機器"のところを iPhone とすれば iPhoneの判定ができます。


(2)自動で迷路を生成する

今回はゲームをプレイするたびに迷路がランダムに自動生成されるようにしましょう。 迷路を作るアルゴリズムは色々ありますが、ここでは有名なアルゴリズムである棒倒し法を解説します。
まずは動作をご確認下さい。画面をタップするたびに新しい迷路が作られます。
example322.html ← 動作の確認
ソースコードは次のようになります。
01<!DOCTYPE html>
02<html lang="ja">
03<head>
04<meta charset="utf-8">
05<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0 user-scalable=no">
06<title>JavaScriptのテストプログラム</title>
07</head>
08<body>
09<canvas style="position:absolute; top:0; bottom:0; left:0; right:0; margin:auto;" id="bg"></canvas>
10
11<script>
12
13//キャンバスの準備
14var winW = window.innerWidth;
15var winH = window.innerHeight;
16var CWIDTH  = 330;
17var CHEIGHT = 330;
18if( winW < winH )
19 winH = winW;
20else
21 winW = winH;
22var SCALE = winW / CWIDTH;
23
24var canvas = document.getElementById("bg");
25var cnt = canvas.getContext("2d");
26canvas.width = winW;
27canvas.height = winH;
28SCALE = winW / CWIDTH;
29cnt.scale( SCALE, SCALE );
30
31//画面をタップすると新しい迷路が作られる
32canvas.onclick = function(event) {
33 initMaze();
34 makeMaze();
35 drawMaze();
36}
37
38function toInt( val ) {//整数を返す関数
39 return parseInt(val);
40}
41
42function rnd( max ) {//乱数を返す関数
43 return toInt( Math.random()*max );
44}
45
46function fRect( x, y, w, h, col ) {//矩形
47 cnt.fillStyle = col;
48 cnt.fillRect( x, y, w, h );
49}
50
51//迷路の処理
52var MAZE_W = 11;
53var MAZE_H = 11;
54var maze = new Array();//迷路用の二次元配列を作る
55 for( var y = 0; y < MAZE_H; y ++ ) {
56 maze[y] = new Array();
57 for( var x = 0; x < MAZE_W; x ++ ) maze[y][x] = 0;
58}
59
60function initMaze() {//迷路の配列の初期化
61 var x, y;
62
63 //周りの壁
64 for( x = 0; x < MAZE_W; x ++ ) {
65  maze[0][x] = 1;//上の横一列
66  maze[MAZE_H-1][x] = 1;//下の横一列
67 }
68 for( y = 1; y < MAZE_H-1; y ++ ) {
69  maze[y][0] = 1;//左の縦一列
70  maze[y][MAZE_W-1] = 1;//右の縦一列
71 }
72
73 for( y = 1; y < MAZE_H-1; y ++ ) {
74  for( x = 1; x < MAZE_W-1; x ++ ) maze[y][x] = 0;//中を何も無い状態に
75 }
76
77 for( y = 2; y < MAZE_H-2; y += 2 ) {
78  for( x = 2; x < MAZE_W-2; x += 2 ) maze[y][x] = 1;//1マスおきの壁
79 }
80}
81
82var XP = [  0, 1, 0, -1 ];
83var YP = [ -1,  0, 1, 0 ];
84function makeMaze() {//棒倒し法による迷路作成
85 var d, x, y;
86 for( y = 2; y < MAZE_H-2; y += 2 ) {
87  for( x = 2; x < MAZE_W-2; x += 2 ) {
88   d = rnd(4);
89   if( x > 2 ) d = rnd(3);//二列目からは左に壁を作らない
90   maze[ y+YP[d] ][ x+XP[d] ] = 1;//上下左右いずれかに壁を作る
91  }
92 }
93}
94
95var MAZE_COL = [ "#CFC", "#004" ];
96function drawMaze() {//迷路を表示する
97 var x, y;
98 fRect( 0, 0, CWIDTH, CHEIGHT, "#FFF" );
99 for( y = 0; y < MAZE_H; y ++ ) {
100  for( x = 0; x < MAZE_W; x ++ ) fRect( x*30, y*30, 29, 29, MAZE_COL[ maze[y][x] ] );
101 }
102}
103
104window.onload = function() {
105 initMaze();
106 makeMaze();
107 drawMaze();
108}
109</script>
110</body>
111</html>
このサンプルでは迷路の横と縦の升目の数を
var MAZE_W = 11;
var MAZE_H = 11;
と定義しています。
もう少し小さい7×7マスの迷路で棒倒し法による迷路生成を説明します。

①周りを壁で埋める(判りやすいように周りの壁を赤としています)

 ↓
②1マスおきに壁(青)を置く

 ↓
③それらの壁の上下左右いずれかの方向を壁(濃い緑)にする


ソースコードのinitMaze()関数が①と②の処理、makeMaze()関数が③の処理となります。 ③の工程が、立てた棒をいずれかの方向に倒すイメージから棒倒し法と呼ばれるそうです。

棒倒し法には注意すべきことが一つあります。 それは単にランダムに4方向に壁を作ると入れない場所ができる場合があることです。 例えば次のように壁が作られると中央の部分に入れなくなります。

これを防ぐには、一番左の列は4方向いずれかランダムに壁を作りますが、その次の列からは、上、右、下のいずれか3方向に壁を作ります。

この処理を行っているのは次のたった1行ですが、このif文を入れるだけで脱出不可能な迷路になるのを防ぐことができます。

if( x > 2 ) d = rnd(3);//二列目からは左に壁を作らない



(3)傾けてボールを転がすゲームの完成

端末を傾けて3つのボールを転がし、ゴールを目指すゲームです。
※スマートフォンやタブレットを縦持ち、画面の回転をロックしてご確認下さい。
example323.html ← 動作の確認
ソースコードの確認 ← 320行程度あります

(1)と(2)で解説した処理を行っています。 インデックスとタイマーによるゲーム進行管理は 2-1 で説明した通りです。 インデックスと使っている変数は次の通りです。

インデックス
index画面内容
0初期化
迷路を作る
変数に初期値を入れる
1タイトル
ゲーム説明を表示
タップするとスタート
2ゲーム
端末を傾けてボールを転がす処理
3つともゴールに到達すればクリア
3結果
Goal!と表示し、一定時間後、初期化処理に移行する

使っている変数
変数名用途詳しい説明
idx,tmrインデックスとタイマープログラム全体の流れを管理
gtimeゲーム時間3つのボールがゴールに到達するまでの時間をカウント
hisc最短時間ゴールに到達した最短時間
maze[][]迷路用の配列
goalX, goalYゴールの位置画面上の座標ではなく迷路の升目の位置
ballX[], ballY[]ボールの座標画面上の座標
ballA[]ボールのアクション停止中が0、移動中が1
ballD[]ボールの向き上が0、右が1、下が2、左が3
ballS[]ボールの移動速度何ドットずつ移動するか
ballT[]ボールの移動フレーム1マスを何フレームで移動するか

今回は画像を用いず、迷路、ボール共に矩形と線を表示する命令のみで描いています。
キャンバスは2枚用い、1枚に迷路を、もう1枚にボールや文字を表示するようにしています。
迷路の1マスの大きさは縦横それぞれ24ドットとし、ボールの移動速度は配列で BALL_ST = [ 0, 1, 2, 3, 4, 6, 8, 12, 24 ]; と定義しています。 例えばボールが1マスを8ドットの速度で移動する場合、3フレーム(24÷8)で隣のマスに移ります。

◇コラム◇ ゲーム性について
ゲーム性とはそのゲームがどのようなルールとなっているか、そしてどこが面白いのかを論ずる際に使う言葉です。 今回のゲームはゴールに到達するまでの時間は計っていますが、それ以外の要素は無く、ゲーム性は極めてシンプルです。 例えば、
案1)赤、黄、緑の順に1つずつゴールに入れる
案2)ボールをキャラクター化し、敵キャラを出し、敵に触れないようにしてゴールを目指す
とすると、案1ではパズル要素が強くなり、案2ではアクション要素が強くなるでしょう。 同じようなプログラムでもルールや設定を少し変えただけで全く違ったゲームになります。 今回のソースコードを元に、ユニークなゲームを作られた方は、ぜひご連絡下さい ^ ^



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

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