時計よ動け!

アニメの原理

突然ですが、アニメがどうやってできているのか、という話をします。要はぱらぱら漫画と一緒で、少しずつ変化させた画像を高速で切り替えてみせることによって、動いていると脳に認識させているのです。

だからなんじゃ、という話ですが、これを応用すればプログラムでもアニメーションをさせることが出来ます。今回は時刻が次々新しいものになってくれればそれでいいので、変わって行く途中の画像を用意するなんて面倒な事はしませんが。

要するに、「描く消す描く消す→…」と繰り返してやればいいわけです。

しかし、一つ困ったことがあります。HSPのソースコードは、上から順番に実行されて一番下まで行くと勝手に止まってしまいます。もちろん、「消す→描く」という同じソースコードを繰り返し書いていけば書いた分だけ繰り返されますが、いつかは止まってしまいますし、同じソースコードの中にそんなに何度も同じ部分が出てくるのはなんだか無駄な気もします。

何らかの方法で、実行の流れを制御して、前のところに戻すことが出来れば全てが解決するはずです。

一番下まで行けば否応なしに止まります

そんな都合のいい方法があるのか?あります。goto命令と「ラベル」です。例によってまずは完成品のソースコードを見てみましょう。

	;ウインドウの準備
	title "デジタル時計"
	width 24/2*12,24
	font msgothic,24,16

*main

	;★ちらつき防止
	redraw 0

	;背景を描く(前の時刻表示を消す)
	color 0,0,0
	boxf 0,0,319,24

	;時刻を取得して表示
	color 255,255,255
	pos 0,0 ;★これ大事
	mes "   "+gettime(4)+":"+gettime(5)+":"+gettime(6)

	;★ウインドウに反映
	redraw 1

	;★1秒待つ
	wait 100

	;★繰り返し
	goto *main
	

色々でていますが、ソースコードの実行の流れを制御しているのはgoto *main*mainだけです。このようにプログラムの流れを変更する命令の事を「プログラム制御命令」と呼びます。

ちなみに、今回は時刻を調べるのに変数を使わないようにしてみました。一度きりしか使わないような情報なら、このような方法でもおkです。

飛んでけー!

gotoはパラメータで指定した場所に実行の順番を変える命令です。goto命令が実行されると、次に実行される行が、パラメータで指定された場所になります。その場所を指定するためのものがラベルです。ラベルは、行頭に*と半角英数、_を置いて隙名前を付けることが出来るものです。

例えば上の例で言えば、goto *mainと書いてあると、次に実行する場所が*mainとかいてある行に移動されるわけです。

*mainへ移動します

これでおしまいです。このような何回も繰り返し実行する部分を「ループ」と呼びます。これで途中で止まってしまう問題は解決です。

このループという構造は非常に重要です。ほぼすべてのプログラムが何かしらのループを持っているといっていいでしょう。例えばシューティングゲームやRPGのマップ移動も、全てループによって実現されています。キーボードやマウスのクリックを待つ時も、ループしているのです。

さて、他の細かい部分を見て行きましょう。

ちらつき防止

最初の新出命令はredraw命令です。これは第一パラメータに0を指定すると、ウインドウの中身をかきかえても画面に反映されなくなります。描画が終わったら第一引数に1を指定して再びredrawを実行すると、描き変わった反映されます。

何でこんなことをしているのかは、二つのredrawを「コメントアウト」(一時的に行頭に;をいれてコメントにしてしまう事。その行を消してしまうのと同じ効果があるが、中身が残るので元に戻すのが簡単)して実行してみればわかります。ウインドウが一瞬チカッと点滅している感じで、見ているとなんだか目が疲れます。これは、boxfを実行した瞬間の真っ黒なウインドウが一瞬見えてしまっているために起きる現象です。

一瞬まっさらな背景が見えると点滅して見えるのも脳と目の仕組みの上では仕方のない事でしょう。

このようにアニメーションを行う時には、画面を描きかえるソースコードをredraw 0redraw 1ではさむのが普通です。

カレントポジション

それから、pos命令が出てきています。これは第一パラメータと第二パラメータで指定した場所に「カレントポジション」と移動するための命令です。カレントポジションとは、次に文字やオブジェクト(また後で出てきます)が出てくる座標の事です。

カレントポジションは、一番最初は(0,0)です。そして、mesなどで何かしらを表示するたびに勝手に下へ下へと進んでいきます。

	mes "おはようございます。"
	mes "今日はいいことあるといいですね。"
	

こんな風に書くと、1行目のmesの後にカレントポジションがその真下に移動して、2行目のmesで描く文字が重ならないように調整してくれます。

しかし、今回はずっと同じ位置に時刻を表示し続けたいので、posで毎回表示位置を戻しています。ためしにこのposをコメントアウトしてみると、2度目の時刻表示で文字が消えてしまいます。カレントポジションが下に行ってしまって文字がウインドウからはみ出してしまったためです。

くるくる回って目が回る

最後の新出命令waitは楽勝で、その名の通りただ待つだけです。第1パラメータはどのくらい待つのかを数値で指定します。単位は0.01秒で、100を指定すれば1秒待ちます。

今回は時計なので1秒待ってから時刻を更新するようにしています。でも、わざわざ待たなくてもいいんじゃないか?waitなんていらないんじゃないか?結論から言うと、入れておかないとまずいです。理由は二つあります。一つは「他のアプリに時間を譲るため」、もう一つは単純に「入れておかないと動作がおかしくなるから」。

これは、OSの仕組みと深くかかわっています。

普段コンピュータを使っている時、アプリは複数同時に動いているように見えます。例えばコンピュータで音楽を再生しつつレポートを書いたり、インターネットを見ながら動画をダウンロードしたりできます。しかし、コンピュータには一度に一つの事しか命令することが出来ません。実は、それぞれのプログラムを決めた時間でちょっと動かしては切り替える、という操作を高速で行っているのです。この同時に動かす(あるいはそう見せかける)仕組みを「マルチタスク」と言います。

簡単に言うと、まず一つのアプリが連続で使える最大の時間が決められています。そして、例えばアプリA,B,Cが同時に起動されている時は、Aの命令を切りのいいところまで実行して次のBへ移り、Bもきりが良いところまで行ったらCを実行し、Cもきりが良くなったらAへ戻る、というような感じです。ただし、きりのいいところまで来る前に時間切れになると、強制的に次のアプリに移ります。この切り替えがものすごく速いので、あたかも複数のプログラムが同時に動いているように見えるのです。

それでも、中には高速な処理を目いっぱいまでしたいアプリもあります。動画のプレイヤーやグラフィカルなゲームなどです。そこで普通アプリは、自分の処理がきりのいいところまで終わると「もうすることないから残りの時間はいらないよ」とOSに返します。そうすることで、忙しいアプリに時間を沢山割いて、暇なアプリはお休み、というふうにうまく共存することが出来るのです。これが最初の「他のアプリに時間を譲るため」ということです。

そんなわけで、我らがデジタル時計も、時刻の表示が終わったら1秒は何もしなくていいのですから、OSにもういいよ、とwait命令で知らせているわけです。

もう一つの「入れておかないと動作がおかしくなるから」の方はどういう事でしょうか?waitの時間を短くする分には全く問題ありません。wait 1とかでも問題ありません。ところが、waitを完全にとってしまうと、途端に動きがぎこちなくなったり、応答なしになったりして、強制終了(ctrl+alt+del→タスクマネージャ→HSPのアプリを終了、またはしばらく待っていると終了するかどうかOSから聞かれる)するしかなくなります。何故でしょう?

ユーザー(コンピュータを使っている人)がマウスやキーボードを操作したとき、その情報は直接プログラムには渡されずにまずOSに届きます。そして、アプリは自分のタイミングでその情報をOSから貰ってきて、それに合わせた動作をします。例えばウインドウの右上の×印を押してプログラムを終了する時も、アプリがOSから「×が押されましたよ」という情報を受け取ってからです。

HSPの場合、そのタイミングの一つがwaitです。それがまったくない状態でずっとプログラムの処理が続いてしまうと、ユーザーがウインドウを動かしただのキーボードを押しただの×を押しただのといった情報が全くHSPのプログラムに伝わらず、終了やウインドウの移動といった処理が行われません。なので「(ユーザーやOSの指示に対して)応答なし」ということになるわけです。

集中しすぎている人に声をかけても無視されてしまうのと同じ状況です。

さて、段々時計らしくなってきました。しかし、13時とか23時の表示では正直時間が解りにくく見辛いです。午前ならAM、午後ならPMと表示するようにしましょう

コラム:ウインドウズアプリケーションとHSPの関係

ちょっと寄り道して、Windowsのアプリケーションがどのような仕組みで動いているのかを見てみましょう。

Windowsのアプリケーションは、Windowsが用意している様々な機能(APIやCOM)を呼び出すよう設計されています。ウインドウやメニュー、ボタンといったものが皆大体同じデザインなのはそのためです。これはWindowsが「ウインドウを作る」という命令を用意していて、それをアプリが呼び出してウインドウを作っているからです。これらの機能は「DLL(ダイナミックリンクライブラリ)」というファイルに入っています。

こうしたアプリケーションには大抵一つはウインドウがあります(例外ももちろんありますが)。ユーザーはこのウインドウに向かって指示をして、コンピュータを操作しています。例えばインターネットで何かを検索する時は、ブラウザのウインドウをクリックして、その中の入力用の枠に文字を打ちこんで、「検索」をクリックします。

プログラミングとは」でも説明した通り、コンピュータの操作をアプリケーションが直接受け取ることはありません。そういう難しい事はWindows(OS)が全部やってくれます。そしてその情報は、「メッセージキュー」という場所にどんどん積まれていきます。例えば「×が押されました」とか「キーボードのAが押されました」とかいうメッセージです。このメッセージを「ウィンドウメッセージ」と呼びます。

Windowsアプリケーションのほとんどは、そのメッセージに対応して様々な処理をすることで動いています。したがって、一通りプログラムが動く準備が整うと、あとはひたすらメッセージを処理するだけの無限ループに入ってしまいます。

ゲームのようなユーザーの入力が無くても動いているプログラムも、自分で「○○秒後にメッセージで教えてねー」とWindowsにお願いして、あとは同じようなメッセージ処理のループに入ってしまいます。

このような形で動くプログラムのプログラミングを「イベント駆動型プログラミング」、イベントドリブンと呼びます。

目次へ戻る