十進 BASIC でアニメーションを
アニメーションのプログラムを作成するには、VSYNC*1 (ディスプレイの垂直同期信号) に同期して
割り込みプログラムを作成するのが定石ですが、十進 BASIC には VSYNC 割り込みやタイマ割込みに同期する機能がありません。
*1VSYNC ぶいしんく: ディスプレイの表示に影響がない期間。テレビは約 1/60 秒ごとに発生する。
ここでは、同期信号に頼らずプログラムの実行速度を維持するプログラムを作成します。
具体的なアルゴリズムは以下で、pygame.time.Clock() の tick(60) 関数や、SmileBASIC の VSYNC 命令のようなものです。
当 BogusTick は、時刻の比較により処理に要した時間を計算し、長さの異なる調整時間を自動的に作る副プログラムです。
- 「前回の時刻」を記憶しておく
- 今回の時刻が「前回の時刻 + 指定した間隔」に到達するまで何もしない。
制限事項
命令 | 機能 |
TIME | その日の 00:00:00.00 からの経過秒数 (0.00~86399.99) を返す。 |
WAIT DELAY | 指定時間が過ぎるまでプログラムの実行を停止する。 |
処理間隔の調整方法
プログラムを一定間隔で動作させるには、VSYNC 等の割り込みのほかに WAIT (ウエイト = 調整時間) を意図的に入れる方法があります。
(カスタネット演奏に例えると「たん・うん」の「うん」に相当。「たん」だけではリズミカルに進行できない)
「実行したい処理」の直後に WAIT を挿入しますが、「実行したい処理」はその時々によって動的に処理時間が変化します。
これに従い、調整をするための WAIT も都度長さを変更する必要があります。
VSYNC 割り込みやタイマ割込みがハードウェアにより機械的に作り出されるのに対し、この方法はソフトウェアで WAIT を計算しているため、
航空機の制御等の精密な処理には向きませんが、アニメーションやゲームの処理に役立ちます。
この図は 1 秒間に繰り返される、実行したい処理と WAIT の関係を表しています。
実行したい処理 [n] の時間 を t[n] とすると、WAIT[n] = 1/fps - t[n] となる。
←------ 1 秒間 ------→ 1/fps 秒 1/fps 秒 ··· 1/fps 秒 ← 実行したい処理 [1] → ← WAIT[1] → ← 実行したい処理 [2] → ← WAIT[2] → ··· ← 実行したい処理 [n] → ← WAIT[n] →
このとき、n は 1,2, .., fps。
例えば、fps = 10 の場合 (1 秒に 10 回の処理 = 0.1 秒間隔) は、以下のようになります。( WAIT[1~10] それぞれの調整時間に注目)
コンピュータの処理は速いので、実際は 0.09 秒もかからない。(← そうならないときはアルゴリズムか仕様のどこかがおかしい)
←------ 1 秒間 ------→ 1/10 秒 [1]
(0.1 秒)1/10 秒 [2]
(0.1 秒)··· 1/10 秒 [10]
(0.1 秒)← 実行したい処理 [1] →
(0.05 秒)← WAIT[1] →
(0.05 秒)← 実行したい処理 [2] →
(0.08 秒)← WAIT[2] →
(0.02 秒)··· ← 実行したい処理 [10] →
(0.09 秒)← WAIT[10] →
(0.01 秒)
近年は FPS ゲームの影響によりコンピュータの処理速度に表示が追従することも増えていますが、動作の基本は一定間隔です。
プログラムと使い方
下記プログラム中の「サンプルプログラム」を参考にして実装してください。
ループの最初に WAIT を挿入していますが、「前回の時刻」を -1 で初期化しているので最初は空振りします。(このとき「前回の時刻」に正しい値が入る)
*2このプログラムは自由に使用して構いませんが、学習者がオリジナルへ戻ることができるよう、version の行を消さないことを希望します。
!--- サンプルプログラム ここから --- DECLARE EXTERNAL SUB BogusTick ! この行は無くても動作するが作法に従う。 LET BogusTick_before = -1 FOR i = 0 TO 9 CALL BogusTick(BogusTick_before, 15) ! 15fps の場合 PRINT TIME$, TIME NEXT i END !--- サンプルプログラム ここまで ---
! プログラムの実行速度を維持する副プログラム。 ! 使い方: ! DECLARE EXTERNAL SUB BogusTick ! LET BogusTick_before = -1 ! 前回時刻は -1 (または TIME) で初期化すること。 ! LET fps = 2 ! これは 2 fps の場合。(0.5 なら 1/0.5 = 1f/2sec になる) ! DO ! CALL BogusTick(BogusTick_before, fps) ! ! いろいろ処理 ! LOOP ! BogusTick version 1.0 written by fuku@rouge.gr.jp EXTERNAL SUB BogusTick(before, fps) IF before >= 0 AND fps > 0 THEN LET current = TIME IF before > current THEN LET before = before - 24 * 3600 LET this_wait = 1 / fps - (current - before) IF this_wait > 0 THEN WAIT DELAY this_wait END IF LET before = TIME END SUB
- IF before > current THEN LET before = before - 24 * 3600
日をまたいだ時の処理。ここで一瞬 before < 0.00 になるが、当該副プログラム最後の before = TIME で正になる。
- IF this_wait > 0 THEN WAIT DELAY this_wait
処理落ちになっていなければ調整時間を挿入する。WAIT DELAY 命令は負の値を指定するとエラーになる。