[Labyrinthe Noir]>[Top]>[Computer Forum]>[実用JavaScript集]>[こども工作教室]>

「スロットゲーム3」のソースと解説


<html>
<head>
<title>スロットゲーム</title>
</head>
<body>
<h3>スロットゲーム</h3>
<hr>
<form name="slot">
<table border="2">
<tr>
<th colspan="3" bgcolor="#000000"><font color="#FFFFFF"><div id="score">0点</div></font></th>
</tr>
<tr bgcolor="#CCCCCC">
<td><div id="dram0">☆</div></td>
<td><div id="dram1">☆</div></td>
<td><div id="dram2">☆</div></td>
</tr>
<tr>
<td><input type="button" value="おす" onClick="dramstop(0)"></td>
<td><input type="button" value="おす" onClick="dramstop(1)"></td>
<td><input type="button" value="おす" onClick="dramstop(2)"></td>
</tr>
<tr>
<td colspan="3"><input type="button" value="リセット" onClick="dramreset()"></td>
</tr>
</table>
</form>

<hr>
<div id="rireki"></div>

<script language="JavaScript">
img = new Array("<img src='buta.gif'>" ,"<img src='usi.gif'>","<img src='saru.gif'>"
 ,"<img src='kuma.gif'>","<img src='tora.gif'>","<img src='panda.gif'>");
kiroku = new Array();
rrk = "";
rrk_num = 0;

dramreset();

function dramreset() {
var s = "";
for (i=0; i<3; i++) {
s += img[kiroku[i]];
document.getElementById("dram" + i).innerHTML = img[0];
document.slot.elements[i].disabled = false;
}
if (rrk_num > 0) {
rrk = "<tr><td>" + rrk_num + "</td><th>" + s + "</th><th>" + scr + "点</th></tr>" + rrk;
document.getElementById("rireki").innerHTML = "<table>" + rrk +"</table>";
}
rrk_num++;
scr = 0;
}

function dramstop(btn) {
r = Math.floor(Math.random() * 6);
document.getElementById("dram"+btn).innerHTML = img[r];
document.slot.elements[btn].disabled = true;
kiroku[btn] = r;
scr += r * 10;
if ((kiroku[0] == kiroku[1]) && (kiroku[0] == kiroku[2])) {scr += r * 20;}
document.getElementById("score").innerHTML = scr + "点";
}
</script>
</body>
</html>

【この時点の slot.html を別枠で表示】

ここまででも、十分にゲームとして遊べますが、問題点も残っています。
他人が使うゲームを作る場合、想定外の使い方をする場合があります。
様々な動作を調べて、気づかなかった問題点を探し出す必要があります。

このように、動作テストをして問題点を洗い出すことを、デバッグと言います。
問題点のことを虫食いの意味でバグと呼ぶことに由来しています。

テストの結果、下記の問題点が見つかりました。
また、使い勝手として、改良も加えます。

【問題点】

「おす」ボタンを3つ押さずにリセットボタンを押したとき、その前の絵柄が履歴に出現してしまう。

リセットボタンを何度か押すと、履歴のカウンターが不正に増えてしまう。

【改良点】

「おす」ボタンを押すまで、スロットの絵柄をクルクル動くようにしたい。

履歴の絵柄を更に綺麗に揃えて表示させたい。


ここからは変更のない部分は省略して表示することがあります。追記や変更は、場所の移動はです。


(省略)

function dramreset() {
var s = "";
for (i = 0; i < 3; i++) {
s += img[kiroku[i]];
document.getElementById("dram" + i).innerHTML = img[0];
document.slot.elements[i].disabled = false;
}
if (rrk_num > 0) {
rrk = "<tr><td>" + rrk_num + "</td><th>" + s + "</th><th>" + scr + "点</th></tr>" + rrk;
document.getElementById("rireki").innerHTML = "<table>" + rrk +"</table>";
}
rrk_num++;
scr = 0;
btn_num = 0;
}

function dramstop(btn) {
r = Math.floor(Math.random() * 6);
document.getElementById("dram"+btn).innerHTML = img[r];
document.slot.elements[btn].disabled = true;
kiroku[btn] = r;
scr += r * 10;
if ((kiroku[0] == kiroku[1]) && (kiroku[0] == kiroku[2])) {scr += r * 20;}
document.getElementById("score").innerHTML = scr + "点";
btn_num++;
}

(省略)

【問題点:不正な履歴の出力(1)】

3つの「おす」ボタンを押さずに「リセット」すると、表示の出目がそのまま履歴に表示されてしまいます。

ゲームの途中で履歴を出力できないように改善したいと思います。
そのために、新しい変数を用意して、ボタンを押した回数を数えます。

まず、変数btn_numを用意します。
初期設定においても良いのですが、リセットでも使うためdramreset()内に必要です。
途中でリセットをするかどうか判定するため、邪魔にならないように最後の行に置いておきます。

変数btn_numは、「おす」ボタンを押すと1つ増えますので、dramstop()の最後に実行します。


(省略)

function dramreset() {
var s = "";
for (i = 0; i < 3; i++) {
s += img[kiroku[i]];
document.getElementById("dram" + i).innerHTML = img[0];
document.slot.elements[i].disabled = false;
}
if (rrk_num > 0 && btn_num == 3) {
rrk = "<tr><td>" + rrk_num + "</td><th>" + s + "</th><th>" + scr + "点</th></tr>" + rrk;
document.getElementById("rireki").innerHTML = "<table>" + rrk +"</table>";
}
rrk_num++;
scr = 0;
btn_num = 0;
}

(省略)

【問題点:不正な履歴の出力(2)】

あとは、リセットボタンを押したときに、変数btn_numが「3」ならば、履歴を更新します。

履歴の表示はif文で既に処理されています。ここに2つ目の条件を書き加えます。
今回は2つの条件を同時に満たす場合なので「&&」を使って、その後に2つ目の条件を足しています。

if文を2つに分けることもできます。

if (rrk_num > 0) {
if (btn_num ==3) {
...
}
}

この場合、順番に条件を判断します。「&&」は同時に判断するという違いがあります。
段階的に判断をして分岐を繰り返す場合には、このようにif文を複数組み合わせて対処します。

【この時点の slot.html を別枠で表示】


(省略)

function dramreset() {
var s = "";
for (i=0; i<3; i++) {
s += img[kiroku[i]];
document.getElementById("dram" + i).innerHTML = img[0];
document.slot.elements[i].disabled = false;
}
if (rrk_num > 0 && btn_num == 3) {
rrk = "<tr><td>" + rrk_num + "</td><th>" + s + "</th><th>" + scr + "点</th></tr>" + rrk;
document.getElementById("rireki").innerHTML = "<table>" + rrk +"</table>";
}
if (rrk_num == 0 || btn_num == 3) {rrk_num++;}
scr = 0;
btn_num = 0;
}

(省略)

【問題点:不正なカウントを止める】

リセットボタンを押すと、その分だけカウンターが増えてしまいます。

そこで、変数rrk_numが増える条件を設定したいと思います。
カウントを増やす条件は2つあります。1つは、初期設定の直後で、rrk_numが0のときです。もう1つは、ボタンが3つ押された後になります。

それぞれ「rrk_num == 0」「btn_num == 3」という条件式が書けます。
この2つの条件は同時に満たすものではありません。どちらか1つが真であれば良いので、「||」を使って2つの条件を並べています。

【この時点の slot.html を別枠で表示】


(省略)

dramreset();

dramstart();

function dramreset() {
var s = "";
for (i=0; i<3; i++) {
s += img[kiroku[i]];
document.getElementById("dram" + i).innerHTML = img[0];
document.slot.elements[i].disabled = false;

}

if (rrk_num > 0 && btn_num == 3) {
rrk = "<tr><td>" + rrk_num + "</td><th>" + s + "</th><th>" + scr + "点</th></tr>" + rrk;
document.getElementById("rireki").innerHTML = "<table>" + rrk +"</table>";
}
if (rrk_num == 0 || btn_num == 3) {rrk_num++;}
scr = 0;
btn_num = 0;
}

function dramstart() {
for (i=0; i<3; i++) {
if (!document.slot.elements[i].disabled) {
r = Math.floor(Math.random() * 6);
document.getElementById("dram" + i).innerHTML = img[r];
}
}
}

function dramstop(btn) {
r = Math.floor(Math.random() * 6);
document.getElementById("dram"+btn).innerHTML = img[r];
document.slot.elements[btn].disabled = true;
kiroku[btn] = r;
scr += r * 10;
if ((kiroku[0] == kiroku[1]) && (kiroku[0] == kiroku[2])) {scr += r * 20;}
document.getElementById("score").innerHTML = scr + "点";
btn_num++;
}

(省略)

【改良点:絵柄をクルクル回す(1)】

スロットのドラムが回転するイメージをそのままアニメーションで表現するには、データの作り直しからしなくてはなりません。それでは かなりの手間がかかるため、今回は表示する絵柄を入れ換えて変化させます。
擬似的にですが、ドラムを回しているように見えるはずです。

絵柄の選択には乱数を使っていますので、スロットの回る表現にもこれを利用します。
時間で繰り返す処理にはタイマー機能を使います。

初期設定が終わったら、そこでドラムを回させましょう。
新しいユーザー関数dramstart()を作ります。
初期設定の次にそれを呼び出します。

dramreset()の中でドラムの回転を作ることはできません。
for文を使ってクルクル回すこともできますが、タイマーを使った方が簡単です。

タイマーでは、ユーザー関数を指定して繰り返し処理をするので、独立したユーザー関数を用意します。

dramstart()では、3つのドラムを回します。
そのためfor文を使って、絵柄の入れ換えを3回行いましょう。
今回は既に作った命令文からコピーして作ります。
緑色の部分からコピーして、dramstart()に貼り付けてください。

1行目のfor文はそのまま上から持ってきます。
2行目は、上の3つめの緑の行から式の左部分を持ってきて、if文の中に入れています。この時、条件式の頭に「!」を入れて条件を否定(false)しています。
3行目は下から持ってきます。これもそのままです。
4行目は上からですが、img[0]がimg[r]に変わっています。

dramstart()の内容を説明します。

1行目のfor文は3回繰り返すという処理です。

2行目のif文で、ボタンが使用禁止かどうかを判断しています。
使用禁止ということは、ボタンを押したということですから、使用禁止じゃないときにドラムを回します。

3行目と4行目で、乱数を使って絵柄を決め、表示しています。


(省略)

function dramreset() {
var s = "";
for (i=0; i<3; i++) {
s += img[kiroku[i]];
//document.getElementById("dram" + i).innerHTML = img[0];
document.slot.elements[i].disabled = false;
}

if (rrk_num > 0 && btn_num == 3) {
rrk = "<tr><td>" + rrk_num + "</td><th>" + s + "</th><th>" + scr + "点</th></tr>" + rrk;
document.getElementById("rireki").innerHTML = "<table>" + rrk +"</table>";
}
if (rrk_num == 0 || btn_num == 3) {rrk_num++;}
scr = 0;
btn_num = 0;
}

(省略)

【改良点:いらない記述を削除する】

機能的には全く影響はないのですが、ドラムの回転によっていらなくなった部分があります。

左の緑の部分は、絵柄をハズレに初期化する部分です。
初期化のすぐ後にドラムが回るため、初期状態は画面に残りません。
これはやらなくても良い処理になってしまったのです。

行を削除するか、左のように行頭に「//」を入れて、コメントアウトして使えなくすることができます。

以降のソースでは削除してあります。


(省略)

function dramstart() {
for (i=0; i<3; i++) {
if (!document.slot.elements[i].disabled) {
r = Math.floor(Math.random() * 6);
document.getElementById("dram" + i).innerHTML = img[r];
}
}
setTimeout("dramstart()",200);
}

(省略)

【改良点:絵柄をクルクル回す(2)】

タイマーを使って、dramstart()を繰り返し実行しましょう。

ドラムを回転させるには、dramstart()を何度も繰り返す必要があります。
そこで、このdramstart() の最後にタイマーを置いて永久的に繰り返しをさせます。
ここでは、実行間隔を「200/1000秒」にしていますので、0.2秒待って次の処理を実行することになります。
およそ1秒間に5回絵柄が変わることになります。

永遠に繰り返して大丈夫なのでしょうか?
ドラムの回転はボタンを押すと止まります。これは、ドラムを回転する処理の中でif文を使っているからです。
ボタンが押されているときは、ドラムの回転が止まるのです。
でも、dramstart()は止まっていません。

【この時点の slot.html を別枠で表示】

これを動かすと、明らかに新たな問題が発生します。
絵柄によって画像のサイズが違うため、表が動いてしまって、とても気持ち悪くなってしまいました。
次にこの新たな問題に対処してみたいと思います。


(省略)

<form name="slot">
<table border="2">
<tr>
<th colspan="3" bgcolor="#000000"><font color="#FFFFFF"><div id="score">0点</div></font></th>
</tr>
<tr bgcolor="#CCCCCC">
<td width="68" height="52"><div id="dram0">☆</div></td>
<td width="68" height="52"><div id="dram1">☆</div></td>
<td width="68" height="52"><div id="dram2">☆</div></td>
</tr>
<tr>
<td><input type="button" value="おす" onClick="dramstop(0)"></td>
<td><input type="button" value="おす" onClick="dramstop(1)"></td>
<td><input type="button" value="おす" onClick="dramstop(2)"></td>
</tr>
<tr>
<td colspan="3"><input type="button" value="リセット" onClick="dramreset()"></td>
</tr>
</table>
</form>

(省略)

【問題点:絵柄の大きさが違う】

スロット用の画像を入れ換えると、それぞれの画像の大きさが違うため、それに合わせてテーブルの大きさが調節されます。
スロットを回転させると0.2秒毎に絵が入れ替わり、テーブルも動いて、ボタンもブルブルと動き、とても見難く、使いにくくなってしまいました。

スロット用に用意した画像で一番幅が大きいのはネズミで64ドットあります。
高さの方は、ウサギとウシの48ドットが一番大きなサイズになります。

画像のサイズに合わせてテーブルが動くので、これを最大のサイズに合わせて固定してしまえば、動かなくすることができます。
丁度の数字にすると、画像の周囲に余白があるようなので、全体に2ドット分の余白を見て、縦と横に4ドットの幅を足して固定サイズにします。

スロットの表示に使っている<td>タブで、幅を「width」、高さを「height」で設定します。
絵柄のサイズが同じであれば、この設定はいりません。


(省略)

<form name="slot">
<table border="2">
<tr>
<th colspan="3" bgcolor="#000000"><font color="#FFFFFF"><div id="score">0点</div></font></th>
</tr>
<tr bgcolor="#CCCCCC">
<th width="68" height="52"><div id="dram0">☆</div></th>
<th width="68" height="52"><div id="dram1">☆</div></th>
<th width="68" height="52"><div id="dram2">☆</div></th>
</tr>
<tr>
<th><input type="button" value="おす" onClick="dramstop(0)"></th>
<th><input type="button" value="おす" onClick="dramstop(1)"></th>
<th><input type="button" value="おす" onClick="dramstop(2)"></th>
</tr>
<tr>
<th colspan="3"><input type="button" value="リセット" onClick="dramreset()"></th>
</tr>
</table>
</form>

(省略)

【改良点:レイアウトの調節(1)】

細かなレイアウトの修正をしておきましょう。
表のボタン類を枠の中央に合わせて配置します。
表内の<td>を全て<th>に変更します。

<th>はヘッダーという仕様で、文字を太字にして中央に寄せます。
スロットやボタンは文字ではないため、太字の効果はありません。

もし、太字にしたくない場合は、<td>タグに「align」というオプションを使います。

<td align="center"></td>

<div>タグを使っている場合、そこに「align」オプションを使うこともできます。

<div align="center"></div>


(省略)

function dramreset() {
var s = "";
for (i=0; i<3; i++) {
s += "<th>" + img[kiroku[i]] + "</th>";
document.slot.elements[i].disabled = false;
}
if (rrk_num > 0 && btn_num == 3) {
rrk = "<tr><td>" + rrk_num + "</td>" + s + "<th>" + scr + "点</th></tr>" + rrk;
document.getElementById("rireki").innerHTML = "<table>" + rrk +"</table>";
}
if (rrk_num == 0 || btn_num == 3) {rrk_num++;}
scr = 0;
btn_num = 0;
}

(省略)

【改良点:レイアウトの調節(2)】

履歴に出力される絵柄が微妙に縦に揃っていないので、これも修正しましょう。

3つの絵柄が1つの箱に入っていますので、1つ1つの絵柄を3つの箱に入れれば、今よりも綺麗に揃って見えます。

変数rrkにテーブル用のタグ付きで出目や得点が入っています。
出目の絵柄は変数sに3つ連続で入っています。
そこで、変数sに出目を入れるときにタグを付けておきます。
中央に寄せるため<th>タグにしました。

変数rrkへ履歴を入れるときにもタグを使っているので、こちらからは余分なタグを消さなくてはいけません。
下記の緑色の部分を消して、左の青色の行と同じにします。

rrk = "<tr><td>" + rrk_num + "</td><th>" + s + "</th><th>" + scr + "点</th></tr>" + rrk;


(省略)

function dramreset() {
var s = "";
for (i=0; i<3; i++) {
s += "<th>" + img[kiroku[i]] + "</th>";
document.slot.elements[i].disabled = false;
}
if (rrk_num > 0 && btn_num == 3) {
rrk = "<tr><td align='right' >" + rrk_num + "</td>" + s + "<th align='right' >" + scr + "点</th></tr>" + rrk;
document.getElementById("rireki").innerHTML = "<table>" + rrk +"</table>";
}
if (rrk_num == 0 || btn_num == 3) {rrk_num++;}
scr = 0;
btn_num = 0;
}

(省略)

【改良点:レイアウトの調節(3)】

あと少し履歴の表示に手を加えましょう。
履歴のカウンターの数字と得点を右に寄せたいと思います。
数字のデータは右に寄せた方が桁が揃って見やすくなります。

最初の<td>と最後の<th>内に「align」オプションで指示します。

【この時点の slot.html を別枠で表示】


(省略)

<script language="JavaScript">
img = new Array("<img src='buta.gif'>" ,"<img src='usi.gif'>","<img src='saru.gif'>","<img src='kuma.gif'>","<img src='tora.gif'>","<img src='panda.gif'>");
kiroku = new Array();
rrk = "";
rrk_num = 0;
rrk_scr = 0;

(省略)

function dramreset() {
var s = "";
for (i=0; i<3; i++) {
s += "<th>" + img[kiroku[i]] + "</th>";
document.slot.elements[i].disabled = false;
}
if (rrk_num > 0 && btn_num == 3) {
rrk_scr += scr;
rrk = "<tr><td align='right' >" + rrk_num + "</td>" + s + "<th align='right' >" + scr + "点</th><th align='right' >" + rrk_scr + "点</th></tr>" + rrk;
document.getElementById("rireki").innerHTML = "<table>" + rrk +"</table>";
}
if (rrk_num == 0 || btn_num == 3) {rrk_num++;}
scr = 0;
btn_num = 0;
}

(省略)

【改良点:累計得点の表示(1)】

スロットを連続でやっていると、そこまでの累計点を知りたくなりました。
そこで履歴に累計得点の表示を付け加えます。

累計得点用の変数rrk_scrを初期設定に置きます。

表示の直前に変数rrk_scrにその回の得点(変数scr)を足して置きます。
表示は変数scrの部分をコピーして変数名を変えればできます。

【この時点の slot.html を別枠で表示】


 

【改良点:累計得点の表示(2)】

得点が並んで表示されると分かり難いので、見せ方の工夫が必要です。

いくつかパターンを考えてみましょう。

  ソース
6 rrk = "<tr><td align='right'>" + rrk_num + "</td>" + s + "<th align='right'><font color='#FF0000'>" + scr + "点</font></th><th align='right' ><font color='#0000FF'>(" + rrk_scr + "点)</font></th></tr>" + rrk;
5 rrk = "<tr><td align='right'>" + rrk_num + "</td>" + s + "<th align='right'><font color='#FF0000'>" + scr + "点</font></th><th align='right' ><font color='#0000FF'>" + rrk_scr + "点</font></th></tr>" + rrk;
4 rrk = "<tr><td align='right'>" + rrk_num + "</td>" + s + "<th align='right'>" + scr + "点</th><th align='right' >(" + rrk_scr + "点)</th></tr>" + rrk;
3 rrk = "<tr><td align='right'>" + rrk_num + "</td>" + s + "<th align='right'>" + scr + "点</th><th align='right' ><font color='#FF0000'>" + rrk_scr + "点</font></th></tr>" + rrk;
2 rrk = "<tr><td align='right'>" + rrk_num + "</td>" + s + "<th align='right'>+" + scr + "点</th><th align='right' >=" + rrk_scr + "点</th></tr>" + rrk;
1 rrk = "<tr><td align='right'>" + rrk_num + "</td>" + s + "<th align='right'>" + scr + "点</th><th align='right' >" + rrk_scr + "点</th></tr>" + rrk;
  スロット履歴 得点 累計
6 ぶた さる さる 90点 (470点)
5 うし ぶた とら 80点 380点
4 くま ぱんだ さる 100点 (300点)
3 うし ぶた ぱんだ 60点 200点
2 ぱんだ とら さる +110点 =140点
1 ぶた ぶた とら 30点 30点


右の実例に対応した番号のソースを利用します。もちろん、色は好きな色に変更してかまいません。
下記は、6番のソースを使っています。

履歴のリセットはありませんので、ブラウザのリロードでページを読み込みし直すと、初期状態に戻ります。

【この時点の slot.html を別枠で表示】


【完成】

スロットゲームの製作はこれで終了です。

デザイン的な変更はまだまだ色々考えられるでしょう。テーブルの色や大きさ、枠の幅を変えても雰囲気が変わります。
スロットの絵柄をオリジナルなものに変えて楽しんでみてください。

完成させたスロットゲームはHPなどで公開して、みんなに遊んでもらっても良いでしょう。
その場合は、「完成品を公開したい場合の注意点」を読んでください。


戻る