1-6 リアルタイム処理

文章作成ソフトや画像処理ソフトはユーザーからの入力無しに勝手に処理が進むことはありません。 一方ゲームソフトは時間軸に沿ってゲームが進行していきます。 時間軸に沿って進む処理をリアルタイム処理と言います。本項ではJavaScriptによるリアルタイム処理を解説します。
(補足)最近のソーシャルゲームはユーザーがコマンドを選ぶとゲームが進行するタイプも多いですが、 入力待ち中もキャラクターや背景の演出が動き続けるというようにリアルタイムに処理を行っています。


(1)画面を定期的に更新する

前項の現在の日時を表示する例は、ブラウザの再読み込みアイコンを押すなどして画面を更新しない限り、新たな日時は表示されませんでした。 ここではデジタル時計のようにリアルタイムに時間を表示する方法を見てみましょう。
example161.html ← 動作の確認
ソースコードは次のようになります。
01<!DOCTYPE html>
02<html lang="ja">
03<head>
04<meta charset="utf-8">
05<title>JavaScriptのテストプログラム</title>
06</head>
07<body>
08<p id="output1" style="color:#4a4; font-size:80px; text-align:center;">日付表示</p>
09<p id="output2" style="color:#fc0; font-size:120px; text-align:center;">時間表示</p>
10<script>
11window.onload = update();
12/*
13Dateオブジェクトから日付と時間を取得する。
14曜日は数値(0~6)になるので“日月加水木金土”の文字列から
15charAt命令で該当する曜日を抽出している。
16*/
17function update() {
18 var da = new Date();
19 var year = da.getFullYear();//西暦を取得
20 var month = da.getMonth();//月を取得 1月は0
21 var date = da.getDate();//日を取得
22 var day = da.getDay();//曜日を取得 日曜は0
23 var hour = da.getHours();//時間を取得
24 var min = da.getMinutes();//分を取得
25 var sec = da.getSeconds();//秒を取得
26 var whatday = "日月火水木金土".charAt(day);
27 document.getElementById("output1").textContent = year + "/" + (month+1) + "/" + date + "(" + whatday + ")";
28 document.getElementById("output2").textContent = hour + ":" + min + ":" + sec;
29 setTimeout( update, 1000 );
30}
31</script>
32</body>
33</html>
JavaScriptのソースコードに開発メモなどのコメントを書いておきたいことがあります。 コメントは /* ~ */ もしくは // で記述します。
・/* と */ で囲った部分がコメントとなり複数行記述できます。
・// はその後ろに記述した1行のみがコメントになります。
コメントで記述した部分はソースコードが実行される際に全て無視されます。

今回のポイントは 11行の window.onload = 関数、日時を更新するために用意した17行以降の関数update()、そしてupdate()で使っているsetTimeout命令です。
処理の流れは

HTMLが読み込みを完了
 ↓
window.onload が働き update() が呼び出される
 ↓
update()はDateオブジェクトから日時の詳細を取得してHTMLを書き換え、setTimeout命令で再びupdate()を実行する

となります。
window.onload は ブラウザがHTMLの読み込みを完了した時点で指定した処理(関数)を実行します。
setTimeoutの引数は ( 関数, 時間 ) で、記述した時間後(ミリ秒で指定)に関数を実行します。 今回は1000ミリ秒(1秒)後に再びupdate()が実行され、時間がリアルタイムに表示されます。 setTimeout命令に記述する関数名に()は不要です。()を付けて記述すると関数が1回しか実行されません。

DateオブジェクトからgetFullYear()やgetHours()などの命令で西暦や時間を取得しています。 どの命令で何を取得できるかをソースコードにコメントで記述しました。
取得できる西暦、日、時間、分、秒は実際の値通りですが、月の値は1月が0、2月が1 ‥‥ 12月が11となりますので表示する際に+1しています。 また曜日は日曜が0、月曜が1 ‥‥ 土曜が6となりますので、26行でcharAt命令を使い「日月火水木金土」いずれかの曜日を whatday に入れています。 文字列.charAt( n )は文字列のn番目の1文字を取り出す命令で、最初の文字が0番目となります。
今回は日付と時間の表示にp要素を用い、スタイルシートで文字色のRGB値(RPG値は次の(2)で解説)、 サイズのピクセル値、表示位置(センタリング)を設定しています(08、09行)。 <p>は文章や画像を一つの範囲として定めるタグで、前後に1行分の改行が入って表示されます。

getFullYear と getYear
JavaScriptの古い解説には西暦の取得に getYear を使う方法が書いてあることがありますが、 getYearは廃止予定の命令で命令自体に癖がありますので、getFullYearを使いましょう。 どのような癖があるか興味を持たれた方はgetFullYear()をgetYear()に書き換えて挙動をご確認下さい。


setTimeoutに近い命令にsetIntervalがあります。 今回のソースコードの11行を

window.onload = setInterval( update, 1000 );

とし、29行のsetTimeout命令を削除して実行すると同じ動作をします。
setInterval命令を使えばソースコードを1行削ることができるわけですが、setIntervalとsetTimeoutには大きな違いがあることを知っておく必要があります。 それはsetIntervalは指定した時間が来ると再び関数を実行しようとする点です。 実行時間が長い処理でsetIntervalを使うと、必要な処理が終わらないうちに再び関数が呼ばれ、誤動作を起こすことがあります。 setTimeoutは指定した関数の処理が終わり、指定時間の間があった後に次の処理に移りますので、重い処理でも誤動作の恐れは少ないのです。 ですのでゲームのようなリアルタイム処理ではsetTimeout命令を使うようにしましょう。

setIntervalの補足を参考までに
ここで説明したように、setIntervalは指定した関数の処理が終わらなくても、再びその関数を実行してしまうことがあるのですが、 筆者が開発に使っているパソコン(Win7-64bit) Chrome バージョン 58.0.3029.96 で改めてテストしたところ、 かなり重い処理を入れた関数でも最後まで処理を実行した後、再びその関数を実行することが判りました。 具体的にはsetIntervalで指定する関数内に for( var i=0; i<10000000000; i++ )と100億回のループを入れ動作確認したところ、その部分で30秒近く費やすのですが、 ブラウザが停止することなく、関数の処理が最後まで行われていました。同じソースコードで Internet Explorer バージョン 11 11.0.9600 でテストしたところ、こちらはブラウザが応答しなくなりました。 ブラウザはバージョンアップするごとに処理の安定化が図られますので、Chrome は setInterval の誤動作が減るように改良されたのでは?(Google素晴らしいかも)と考えています。 とはいえ、我々のこれまでの経験からsetTimeout命令の方が安定していますので、リアルタイム処理はsetTimeout命令で行うのが間違いないでしょう。


◇コラム◇ p、div、span要素
これらの3つの要素はHTMLを記述する上でよく使います。 私はpとdivの違いをすぐ忘れてしまい、その度にググッて調べることがあったのですが(苦笑)、 pは段落(paragraph)を定義する→段落なので1行空くと覚えることで、違いがすっきり判るようになりました。 span要素は本講座で既に扱いましたが、spanは文章の一部分を定義し前後に改行は入らないと覚えておくと判りやすいでしょう。



(2)背景色をリアルタイムに変化させる

次は画面の色を変化させるソースコードです。
example162.html ← 動作の確認
01<!DOCTYPE html>
02<html lang="ja">
03<head>
04<meta charset="utf-8">
05<title>JavaScriptのテストプログラム</title>
06</head>
07<body id="mybody">
08<div id="output"></div>
09<script>
10var colR = 0, colG = 0, colB = 0;
11window.onload = fade();
12function fade() {
13 colR++; if( colR == 16 ) colR = 0;
14 colG++; if( colG == 16 ) colG = 0;
15 colB++; if( colB == 16 ) colB = 0;
16 var col = "#" + colR.toString(16) + colG.toString(16) + colB.toString(16);
17 var bo = document.getElementById("mybody");
18 bo.style.backgroundColor = col;
19 var di = document.getElementById("output");
20 di.style.color = "yellow";
21 di.style.fontSize = "120px";
22 di.style.textAlign = "right";
23 di.textContent = col;
24 setTimeout( fade, 1000 );
25}
26</script>
27</body>
28</html>
今回のポイントはRGB値での色指定と、style命令によるHTMLの装飾です。
HTMLへの色指定は16進数のRGB値(Rは赤、Gは緑、Bは青)で行い、#RGB もしくは #RRGGBB と記述します。 #と3文字で指定する場合の各色の値は0~F、6文字で指定する場合の各色の値は00~FFとなり、値が大きいほどその色味が強く(明るく)なります。
赤は #F00 もしくは #FF0000
黄は #FF0 もしくは #FFFF00
緑は #0F0 もしくは #00FF00
青は #00F もしくは #0000FF
灰色は #888 もしくは #808080
という感じです。
今回は色を扱うための変数を10行でcolR(赤の値),colG(緑の値),colB(青の値)と宣言し、色を変化させる機能を実装した関数fade()の外側に記述しています。 このように関数の外側に置かれた変数をグローバル変数といい、関数内に置かれた変数はローカル変数といいます。 ローカル変数は関数を呼び出した時点で値が初期化されますが、グローバル変数の値は変化しません。 またローカル変数は記述された関数の中だけで使え、グローバル変数は全ての関数から参照できます。 グローバル変数とローカル変数の違いはプログラミングの重要な基礎知識ですので以下の(3)にまとめます。

画面の色を変化させる処理はまず13~15行でcolR,colG,colBを1ずつ増やし、16になった時に0に戻しています。 色の指定を16進数 0,1,2,3,4,5,6,7,8,9,A(10),B(11),C(12),D(13),E(14),F(15)
※()内は10進数の値、A~Fは小文字a~fでも可
で行うために、colR,colG,colBの値を16行のtoStringという命令で16進数にし、#RGB という形にしています。
toStringは変数もしくは数を n進数にしたい時に使う命令で、変数もしくは数.toString( n ) と記述します。 nを省略すると10進数のままの文字列になります。

JavaScriptでホームページの色やフォントサイズなどを変更する場合、style命令を使います。
17、18行でホームページ全体(body要素)の背景色(backgroundColor)を#RGBで指定した値にしています。
19~23行でdiv要素内の文字を黄色、120ピクセル、右寄せとし、色の値を表示しています。 style命令の色指定は英単語(red,gree,blue,purple,white,blackなど)でも可能です。

今回のソースコードで1つ重要な点があります。 10行でcolR,colG,colBを初期値0で宣言した後、11行で window.onload = fade(); としています。 10行と11行を逆にすると変数が宣言されないうちに fade関数が機能し、初期化されていないcolR,colG,colBを扱い始める場合があります。 JavaScriptでは表示が一時おかしくなる程度の不具合で済みますが、 他のプログラミング言語で作られたソフトウェアは初期化していない変数を扱おうとすると処理が停止するなどし、 最悪コンピューターがフリーズする致命的バグが発生します。 変数は宣言し初期値を入れてから使うことを忘れないようにしましょう。

HTML(CSS)に記述するスタイルとJavaScriptのstyle命令での書き方に少し違いがありますので、主要なものをまとめます。 - がHTML(CSS)ではハイフン、JavaScriptなどのプログラミング言語ではマイナスの記号になることをイメージすると覚えやすいと思います。
HTMLのstyleJavaScriptのstyle
背景色background-colorbackgroundColor
文字の色colorcolor
文字の大きさfont-sizefontSize
文字や画像の位置text-aligntextAlign


(3)ローカル変数とグローバル変数について

プログラミングではローカル変数とグローバル変数の違いを理解し、それらを使い分けることが重要です。 使い方をゲームソフトのソースコードを想定した例で見てみましょう。

var playerX, playerY;//プレイヤーキャラの位置を管理するグローバル変数
var enemyX, enemyY;//敵キャラの位置を管理するグローバル変数

function initPosition() {//キャラクターの初期位置
 playerX, playerY 及び enemyX, enemyY の値を入れる
}

function movePlayer() {//プレイヤーを動かす処理
 var playerSpeed = 1; ← このローカル変数はこの関数内だけで使える
 playerX, playerY を変化させる
 :
}

function moveEnemy() {//敵を動かす処理
 var enemyAI = 0; ← このローカル変数はこの関数内だけで使える
 enemyX, enemyY を変化させる
 :
}

function checkCollision() {//プレイヤーと敵が接触しているか?
 playerX, playerY と enemyX, enemyY の位置関係を調べる
 :
}


変数の有効範囲をスコープという言葉で表します。 グローバル変数はソースコード全体にスコープがあり、ローカル変数は宣言された関数内にスコープがあります。 更にローカル変数は処理のどの時点で宣言したかによりスコープが変わってきます。 具体的には if や for 内で宣言した場合の有効範囲は変数を宣言した { から } の間だけになります。
(まとめ)
・関数の外側で宣言した変数(グローバル変数)は各関数から値を参照したり変化させることができます。
・関数の内側で宣言した変数(ローカル変数)はその関数内だけで使うことができます。
・if や for 内で宣言したローカル変数は { から } の間だけで使うことができます。
・ローカル変数は宣言した関数が呼ばれると初期化されますが、グローバル変数はプログラムが終了するまで値が保持されます。
※{ から } までをブロックといい、ブロック内で宣言された変数はそのブロック内で有効となります。

◇コラム◇ グローバル変数は極力使わないようにという解説もあるけど?
グローバル変数はあらゆる関数や処理から参照でき、値を変化させることができます。 そのため処理が進む中、思わぬ箇所で値が書き換わり、プログラム上の不具合(バグ)を引き起こす恐れがあります。 とは言いましたが、個人で作るゲームのプログラムでは、どの変数で何を処理するかをきちんと管理すれば問題ございませんし、 プログラマー数名で開発する商用レベルのソフトでも、プログラムリーダーがグローバル変数の名称と用途(仕様)を決め管理すれば問題ないはずです。 実際に私達はそのようにして長年ゲーム開発を行ってきました。 一方、大規模なソフト開発において複数名のプログラマーが(まずないとは思いますが) チーム内での意思疎通無しにグローバル変数を使うとバグが発生する危険性が高く、そのようなことは無論避けるべきです。



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

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