sugoroku.htmlから改良を加えましょう。
すごろくらしく、止まった位置によってイベントが発生して、コマが進んだり戻ったりするように仕掛けます。
テーブルの上に文字を入れて、名前の入力を促しましょう。
<h3>すごろく</h3>
<hr>
<p>名前を入れてから始めよう。</p>
<form name="form1">
<p></p>は1つの段落であることを示しています。
ゴールをするとすごろくが初期化されます。
このとき、最初にサイコロを投げるプレイヤーも初期状態に戻ってしまうため、毎回同じプレイヤーから始まります。
これでは最初のプレイヤーが有利という状況が続いてしまいますので、これを改善して、ゴールした次のプレイヤーから始めるようにしましょう。
<script type="text/javascript"> img = new Array("★","◆","●","■"); koma = new Array(); goal = 20; user_num = 0; pre_num = 0; pre_r = 0; timer = 0; restart(); function restart() {user_num = 0;pre_num = 0;pre_r = 0;timer = 0;for (i=0; i<4; i++) { document.getElementById("brd"+i).innerHTML = img[i]; koma[i] = 0; } sai_reset(); }
また、同時に2つの問題も発生しています。
1つは、複数で遊んでいると、2番目以降に赤い出目が残ったままになることです。
もう1つは、途中で0番目(一人目)の名前を消しても、ゴールすると0番目からサイコロがスタートしてしまうことです。
プレイヤーの順番を管理しているのは、変数user_numと変数pre_numです。
また、前の出目を管理するのに、変数pre_rを使っています。
ついでですが、変数timerもここでの初期化は必要なさそうです。
そこで、これら4つの変数をrestart()から外して、初期設定の最初の位置に戻しましょう。
先の処理だけでは、初期化はされなくなりましたが、ゴールをしたプレイヤーから新しいゲームが始まってしまうことに代わりはありません。
これはゴールをしたときの分岐で、次のプレイヤーを探す前に終わってしまったからです。
function getNum(num) { clearTimeout(timer); document.getElementById("sai"+pre_num).innerHTML = pre_r; pre_num = num; pre_r = r; document.getElementById("sai"+num).innerHTML = "<font color='#FF0000'>" + r + "</font>"; koma[num] += r; var s = "" for (i=0; i<koma[num]; i++) { s += "."; } document.getElementById("brd"+num).innerHTML = s + img[num];if (koma[num] < goal) {sai_next();} else {goalin();}} function sai_next() { user_num++; if (user_num == 4) user_num = 0; var s = document.form1.elements["user"+user_num].value; if (s != "") { sai_reset(); } else { if (user_num != pre_num) { sai_next(); } else { sai_reset(); } } }
そこでず、ゴールに飛ぶタイミングを見直します。
誰かがゴールをしたという判定をユーザー関数getNum()の最後で行うのではなく、ユーザー関数sai_next()の中で、次のユーザーを見つけてからに変更します。
ユーザー関数sai_next()では次のユーザーが見つかるまで繰り返し処理をしています。
次の正しいユーザーが見つかったらユーザー関数sai_reset()が実行されるので、その前にゴール判定を行って分岐処理すると良いようです。
しかし、ユーザー関数sai_reset()は2ヶ所あります。同じ分岐を両方に書く前に、先にこの部分を整理してみましょう。
ユーザー関数sai_next()に分岐する条件は、名前が空欄(s == "")の場合で、更に次回のプレイヤー(変数use_num)と前回のプレイヤー(変数pre_num)が同じではないとき(user_num != pre_num)となります。
その条件文を1つにまとめます。
if (s == "" && user_num != pre_num) { sai_next(); } else { sai_reset(); }
このように条件式を書き替えてから、ユーザー関数sai_reset()にゴール判定の分岐を付け加えます。
ただし、if文の条件式はそのまま移し替えても機能しません。変数numがないからです。変数pre_numに同じ数値が入っていますので、こちらに差し替えます。
function sai_next() { user_num++; if (user_num == 4) user_num = 0; var s = document.form1.elements["user"+user_num].value; if (s == "" && user_num != pre_num) { sai_next(); } else { if (koma[pre_num] < goal) { sai_reset(); } else { goalin(); } } }
これで3つの分岐ができあがりました。
先の修正によって、ユーザー関数goalin()にも修正が必要になります。
変数user_numがユーザー関数sai_next()の処理によって次のプレイヤーになっているため、ゴールした人の名前も次のプレイヤーになってしまいます。
function goalin() {
alert("ゴール!!\n優勝は" + document.form1.elements["user"+pre_num].value + "さんです。");
restart();
}
ここも変数pre_numに置き換えることで対処できます。
プレイヤーが一人の場合、サイコロが回転しない不具合が発生します。
function goalin() {
alert("ゴール!!\n優勝は" + document.form1.elements["user"+pre_num].value + "さんです。");
if (user_num == pre_num) pre_r = 0;
restart();
}
ユーザー関数goalin()の中でユーザー関数restart()に飛ぶ前に処理を入れましょう。
変数user_numと変数pre_numが同じときが、プレイヤーが一人だと判断できますので、そのときに変数pre_rを「0」にします。
変数pre_rが「0」のときは、sai_start()でサイコロが回ります。
if文を使わずに、常に変数pre_rを「0」にしてしまうと、プレイヤーが複数の場合に、前のプレイヤーのサイコロの出目が「0」になってしまいます。
プレイヤーが止まった位置は配列変数komaで管理しているので、イベント用のマスも配列変数で管理すると付き合わせるのも簡単です。
次に各イベントの内容を考えます。
あるコマに止まると、「何コマ進む」「何コマ戻る」という表示をして、コマをその数だけ動かします。
そうするとイベントの内容を「進む」と「戻る」に限定すれば、数値で表すことができます。正の数なら進むイベント、負の数なら戻るイベントとします。
あと、イベントがないマスもあったほうが良いでしょう。これは「0」で表現することとします。
イベント用のマスを管理する配列変数を作りましょう。
ゴールの位置を20と決めていますので、止まったマスによるイベント数は19個になります。
<script type="text/javascript">
img = new Array("★","◆","●","■");
koma = new Array();
ev = new Array(0,0,2,0,5,0,-3,-5,0,3,0,-2,2,-5,0,-2,0,2,-2,0);
goal = 20;
restart();
配列変数evに初期設定で19個の数値をイベントとして「,」で区切って並べます。配列変数の宣言と同時に要素にデータも入れてしまいます。
文字列ではないので数字を「""」で囲む必要はありません。
「0」はイベントがありません。
「2」「3」「5」などの正の数はマスを進むイベントです。
「-2」「-3」「-5」など、負の数はマスを戻るイベントです。
イベントを呼び出すときの注意点を先に考えておきましょう。
コマが1マス進むと、1番目のマスに止まります。しかし、配列変数の要素番号は0番から始まるので、「1番目のマス」は「0番」となります。そして、「19番目のマス」は「18番」です。
このようにコマの位置と、イベントマスの位置には1つずれがあります。これを変数で考えると次のようになります。
コマが5マス進んだとき、配列変数koma[num](変数numはプレイヤー番号)は5ですが、イベントマスの番号は4なので配列変数ev[4]からイベントを取ってくるということです。これを1つにまとめると「ev[koma[num]-1]」が、コマ数に対応したイベントです。
コマを進めたときにイベントが発生するので、ユーザー関数getNum()にその処理を追加します。
function getNum(num) { clearTimeout(timer); document.getElementById("sai"+pre_num).innerHTML = pre_r; pre_num = num; pre_r = r; document.getElementById("sai"+num).innerHTML = "<font color='#FF0000'>" + r + "</font>"; koma[num] += r;var s = "";for (i=0; i<koma[num]; i++) {s += ".";}document.getElementById("brd"+num).innerHTML = s + img[num];move_koma(); sai_next(); } function move_koma { var s = "" for (i=0; i<koma[pre_num]; i++) { s += "."; } document.getElementById("brd"+pre_num).innerHTML = s + img[pre_num]; }
しかし、そのイベント処理を追加する前に、1つ手を加えておきましょう。
それと言うのも、イベントというのはコマを動かす処理のことです。すでにコマを動かす処理は書かれています。
今後はサイコロによる移動の後、イベントによる移動と、2回の移動処理が発生しますので、それぞれに対応できるように移動の処理をユーザー関数にまとめることにしましょう。
ユーザー関数move_koma()を作って、青字取り消し部分を移動します。元の場所にはユーザー関数を呼び出すようにします。
ユーザー関数move_koma()の中では変数numが使えませんので、変数pre_numに書き換えます。
ここまでに多くのユーザー関数を作成しましたが、その並び順は処理の流れとは関係なく、呼び出されたときに実行されます。
ただ、できるだけ流れに沿って、関連する物を並べた方が、後で修正したいときに見やすくなります。
イベントを扱うユーザー関数を作りましょう。
function getNum(num) { clearTimeout(timer); document.getElementById("sai"+pre_num).innerHTML = pre_r; pre_num = num; pre_r = r; document.getElementById("sai"+num).innerHTML = "<font color='#FF0000'>" + r + "</font>"; koma[num] += r; move_koma(); check_event(); sai_next(); } function check_event() { var n = ev[koma[pre_num]-1]; if (n > 0) alert(n+"マス進む"); if (n < 0) alert(n*-1+"マス戻る"); koma[pre_num] += n; move_koma(); }
ユーザー関数check_event()を作成し、ユーザー関数getNum()で、コマを動かした後に呼び出します。動いた後のマスでイベントが発生するためです。
ユーザー関数check_event()の中でイベントを確認し、ダイアログでプレイヤーに内容を知らせます。
1行目でロカール変数nに、コマ数に対応したイベントを取り出しています。
変数pre_numがプレイヤーの番号で、そのプレイヤーのコマ数が配列変数komaに入っています。コマ数に対応したイベント用コマはそこから「-1」をした場所でした。
次に2つのif文で、正の数なら「進むイベント」、負の数なら「戻るイベント」として処理を分けています。ここではどちらもアラートを使ってメッセージを表示するだけです。配列変数evが0のときはイベントも発生しないので、メッセージもありません。
3行目のメッセージでは、表示するマスの数を「n*-1」としています。負の数を正の数にして表示するためです。
例えば、「n = -5」の場合、-5×-1=5となります。これで「5マス戻る」と表示されますが、そのままだと「-5マス戻る」と表示されてしまいます。
4行目、ここでイベントの数だけコマの位置を動かします。変数nを配列変数komaに足しています。計算式は足し算と引き算で分ける必要はありません。戻る場合は負の数なので、足せば良いのです。
5行目は、イベント発生後のマスへコマの表示を書き直すため、ユーザー関数move_koma()を呼び出しています。
さて、ここで重要なことを忘れている気がしませんか?
イベントが「0」の場合については何ら処理が書かれていません。
しかし、実は何もしなくていいのです。1行目で変数nが0となり、2行目と3行目のif文は無視されます。4行目も0を足すだけで変化がないため5行目を処理しても同じコマ数が表示されるだけです。
もし、途中にある処理が長くて時間がかかる場合なら、処理をさせないために「0」なら処理をしないようにしますが、今回はそのまま流れに任せています。
ここで動作テストをすると、それまでにない現象が起こります。ゴールのダイヤログが出たとき、すでにコマの位置が初期化されて、ゴールの位置にありません。
ダイアログが出ているので判りにくいのですが、これはcheck_event()を追加するまでは起きていなかったので、その中に問題があると考えられます。
function check_event() { var n = ev[koma[pre_num]-1]; if (n) { if (n > 0) alert(n+"マス進む"); if (n < 0) alert(n*-1+"マス戻る"); koma[pre_num] += n; move_koma(); } }
テスト用にユーザー関数check_event()の最後に「alert(n);」入れてみると、変数nが「undefined」となります。
これは、存在しないデータを呼び出したり計算できない形式のデータを計算したときに起こるエラー内容です。今回変数nには配列変数evが入っていますので、存在しない要素番号を使った可能性があります。
それでは、果たしてそんなことが起こりえるのでしょうか?
配列変数evは最初に19個設定しました。そのため要素として指定できるのは「0」~「18」までです。コマ数で言うと19コマまでです。ところが、ゴールの位置は20以上のコマ数となります。やはり配列変数evが存在しない要素番号を指定しています。
そこで、if文を使って、変数nに正しいデータが入っているか判断させています。
if文の条件式は「if (koma[pre_num] < goal) 」と書いても良いでしょう。コマ数とゴール位置を比較しています。
この場合、変数nのエラーの後では解決になりませんので、if文を1行目に置かなければなりません。
一回のゲームを長くするために、コマ数を増やしてみましょう。
<script type="text/javascript"> img = new Array("★","◆","●","■"); koma = new Array(); ev = new Array(0,0,2,0,5,0,-3,-5,0,3,0,-2,2,-5,0,-2,0,2,-2,0,3,-3,0,5,0,-3,-2,0,2,0,3,0,2,0,-3,2,0,-2,0,5,0,-3,0,-2,0,0,-3,0,0); goal = ev.length+1; restart();
配列変数evの初期設定で、イベントの要素を追記して増やします。それに合わせて変数goalの数値も変更します。
しかし、わざわざゴールの位置を合わせて考えるよりも、自動的に数えさせるという方法があります。
配列変数evの要素数(ev.length)を取得することで、これに1を足せば、ゴールの位置が判ります。
将来考えられる問題点として、コマ数を戻しすぎてマイナスになったときに、対処が必要でしょう。
現時点では、戻すイベントが発生した場合にコマ数がマイナスになってしまった場合、エラーが発生します。配列の要素は「0」から始まりますので、マイナスというものはありません。例えば、1つ進んだところに「-5」があると、配列変数komaは「-4」になってしまいます。
もちろん、イベントを作る段階で計算して作業をすれば良いのですが、気づかないところでこの不具合が発生する可能性もあります。また、将来イベントを自動生成することも考えられます。
そこで、コマ数がマイナスになる場合、戻る位置は全て「0」にして、すべて「スタートに戻る」というメッセージに統一したいと思います。
function check_event() { var n = ev[koma[pre_num]-1]; if (n) { koma[pre_num] += n; if (koma[pre_num] <= 0) { alert("スタートに戻る"); koma[pre_num] = 0; } else { if (n > 0) alert(n+"マス進む"); if (n < 0) alert(n*-1+"マス戻る"); }koma[pre_num] += n;move_koma(); } }
まず、配列変数komaの足し算をメッセージよりも先に行います。青の位置に移動します。
次にif文を使って、条件式でコマ数が「0」以下であることを調べます。真ならば、次の2行で「スタートに戻る」ことを表示して、コマ数を「0」にしています。偽ならば、elseの中を実行します。
テストをする場合は「ev = new Array(-1,-2,-3,-5,-10,0);」と、結果が判りやすいように要素を設定しましょう。サイコロが1~5だとスタートに戻されます。
「-10」のように要素数より大きな数をマイナスにすると確実にスタートに戻ることになるので、何個戻るか考えなくて済みます。
テストをする場合など、条件を替えて複数のパターンを検証したいときに、その度にデータや命令文を書き換えるのは面倒です。
そこで、コメントアウトの機能を使って、複数の設定を切り換えると便利です。
//ev = new Array(0,0,2,0,5,0,-3,-5,0,3,0,-2,2,-5,0,-2,0,2,-2,0,3,-3,0,5,0,-3,-2,0,2,0,3,0,2,0,-3,2,0,-2,0,5,0,-3,0,-2,0,0,-3,0,0); ev = new Array(0,0,2,0,5,0,-3,-99,0,3,0,-2,2,-5,0,-2,0,2,-99,0,3,-3,0,5,0,-3,-2,0,2,0,3,0,2,0,-3,2,0,-2,0,5,0,-3,0,-2,0,0,-3,0,0);
コメントアウトとは、「//」を書いた所から行の最後までが注釈として扱われ、命令文として無視される状態になります。
そのため、条件を替えた複数の命令文やデータを用意して、「//」と付けたり外したりすることで処理や設定を切り換えます。
if文をコメントアウトするときは、最後の「}」もコメントアウトしなければエラーが発生しますので注意しましょう。
さて、突然ですが、コマの移動跡には「.」を使っていますが、これを変更するにはどうすれば良いでしょうか。
ユーザー関数move_koma()の中でコマ数を計算して長さを決めていますので、その位置にある「.」を書き換えれば済みます。しかし、それは覚えているから簡単にできことです。
プログラムを沢山書いていると、色々な部分を試行錯誤で替えてみたり、テストをしてみたりします。また、JavaScriptの場合、利用者が自分で好みに応じた修正をすることも可能です。
そんなときに、どこで何をしてるのか、目的を探すのに時間が掛かってしまいます。そういう手間を一ヶ所に集約することで作業効率を上げることができます。
img = new Array("★","◆","●","■"); ato = "."; //ev = new Array(0,0,2,0,5,0,-3,-5,0,3,0,-2,2,-5,0,-2,0,2,-2,0,3,-3,0,5,0,-3,-2,0,2,0,3,0,2,0,-3,2,0,-2,0,5,0,-3,0,-2,0,0,-3,0,0); ev = new Array(0,0,2,0,5,0,-3,-99,0,3,0,-2,2,-5,0,-2,0,2,-99,0,3,-3,0,5,0,-3,-2,0,2,0,3,0,2,0,-3,2,0,-2,0,5,0,-3,0,-2,0,0,-3,0,0); koma = new Array(); goal = ev.length+1; (省略) function move_koma() { var s = ""; for (i=0; i<koma[pre_num]; i++) { s += ato; } document.getElementById("brd"+pre_num).innerHTML = s + img[pre_num]; }
スクリプトの最初に初期設定を行います。ここで、全体に関わる変数の宣言や規定値(固定値)を設定します。
スクリプトの中身が判らなくとも、そこは目にも止まりやすい場所です。ここに変更しやすい値を変数に入れて用意して置くと、非常に便利です。
利用者はユーザー関数の中を探さなくても良いし、プログラムの中身を間違って変更する危険性も減ります。作った本人さえも時間が経つと忘れてしまうので、メンテナンスも楽になります。
では、具体的にどうするかというと、まず、変数atoを初期設定内に作ります。ここで変数atoに文字列「.」を入れて置きます。
あとは、それをそのままユーザー関数内で使用します。「"."」の部分を変数atoに置き換えました。
このように途中で変更されない固定値の入った変数として使います。
ついでに、配列変数komaの宣言を下へ移動し、変更可能な規定値だけを上に寄せて置きましょう。
こうして見ると、デザイン変更はここだけで色々とできることが判ります。
変数atoの中身を 「~」「>」「_」「)」など、色々替えて遊んでみてください。
ブラウザにはそのままでは表示できない文字があります。それらは使わないようにしましょう。
特に「<」「>」「&」は要注意です。タブや参照コードで使用する文字だからです。それぞれ「>」「<」「&」と表記します。
初期設定の整理ができたらあと少し、コメントアウト(//)を使って注釈を入れて置けば、ここを見るだけで簡単に仕様の変更が可能になります。
//コマ img = new Array("★","◆","●","■"); //移動跡 ato = "."; //イベント(コマ数は自動取得) //ev = new Array(0,0,2,0,5,0,-3,-5,0,3,0,-2,2,-5,0,-2,0,2,-2,0,3,-3,0,5,0,-3,-2,0,2,0,3,0,2,0,-3,2,0,-2,0,5,0,-3,0,-2,0,0,-3,0,0); ev = new Array(0,0,2,0,5,0,-3,-99,0,3,0,-2,2,-5,0,-2,0,2,-99,0,3,-3,0,5,0,-3,-2,0,2,0,3,0,2,0,-3,2,0,-2,0,5,0,-3,0,-2,0,0,-3,0,0); //以下は変更不可 koma = new Array(); goal = ev.length+1;
このようにどれだどの役割か分かりやすく表記したり、注意書きを入れて置くと、利用者に優しいだけでなく自分自身の備忘記録としても役立ちます。
注釈は自由に書けますので、各変数の役割、if文など分岐の解説、テスト用の変数や命令、修正した日付、参照したURLなど色々とメモをして置くと良いでしょう。
コメントアウトの記号は行の最後にも書くことができます。
ato = "."; //移動跡
ダイアログボックス(アラート)に対象のプレイヤーの名前を表示させましょう。
(省略) function check_event() { var n = ev[koma[pre_num]-1]; if (n) { koma[pre_num] += n; var s = document.form1.elements["user"+pre_num].value + "さん\n"; if (koma[pre_num] <= 0) { alert(s+"スタートに戻る"); koma[pre_num] = 0; } else { if (n > 0) alert(s+n+"マス進む"); if (n < 0) alert(s+n*-1+"マス戻る"); } move_koma(); } } (省略)
ユーザー関数check_event()にあるalert()でダイアログを表示する際にプレイヤーの名前も表示しましょう。
ユーザー名はフォームから取得して、変数sに入れます。あとは、3ヶ所のalert()で変数sを頭に追加します。
今後の改良点として以下のようなことも考えられます。
・自動的にイベントを作成する
・参加人数を自由に選択できる
3ヶ所にあるalert()を1つにまとめてみましょう。同じ内容でもまとめ方によって手順が変わります。
function check_event() { var n = ev[koma[pre_num]-1]; var s = ""; if (n) { koma[pre_num] += n; if (koma[pre_num] <= 0) { s = "スタートに戻る"; koma[pre_num] = 0; } else { if (n > 0) s = n+"マス進む"; if (n < 0) s = n*-1+"マス戻る"; } if (s != "") { alert(document.form1.elements["user"+pre_num].value + "さん\n" + s); move_koma(); } } }
変数sを空で用意します。
次に、元々のalert()があった位置で変数sにメッセージ用の文字列だけを入れて行きます。
これら3カ所はイベントが発生したときに通りますので、変数sが空ならばイベントはありません。
最後のif文で、変数sが空でなければイベントが発生したということなので、alert()を実行して、コマの移動を行います。
最初に変数sをローカル宣言して「""」にしておくことが重要です。
これによって、イベントがないことを基準値としているのです。
ユーザー関数move_koma()は新しいif文の中に入れましたが、その後に残してあっても問題なく動作します。
その場合でも、イベントは発生してないときは、同じものが出力されるからです。ただ、それ自体はいらない動きですし、イベント時にコマを動かすことが目的なので、if文の中にある方が意味合いとして正しいことになります。
ダイアログボックスに名前を表示させましたが、コマの図柄も表示させてみましょう。
ただし、配列変数imgの初期設定でコマにタグを使用して色を着けたり、画像を使っている場合、それらは表示できません。タグのまま表示されてしまいます。
function check_event() { var n = ev[koma[pre_num]-1]; var s = ""; if (n) { koma[pre_num] += n; if (koma[pre_num] <= 0) { s = "スタートに戻る"; koma[pre_num] = 0; } else { if (n > 0) s = n+"マス進む"; if (n < 0) s = n*-1+"マス戻る"; } if (s != "") { alert(img[pre_num] + document.form1.elements["user"+pre_num].value + "さん\n" + s); move_koma(); } } } function goalin() { alert("ゴール!!\n優勝は" + img[pre_num] + document.form1.elements["user"+pre_num].value + "さんです。"); if (user_num == pre_num) pre_r = 0; restart(); }
ユーザー関数check_event()のalert()の中にコマの表示を追加します。配列変数imgにはコマの絵柄が入っており、変数pre_numに直前のプレイヤーの番号が入っていますので、これらを使います。
ユーザー関数goalin()にもダイアログがありますので、こちらにもコマの表示を入れましょう。