スキップフレームさせるの巻
処理が落ちたときに動きをカクカクさせることでスピードを一定に保とうとするゲームが時々あります。今回はHSPでそれをやってみましょう(余談ですが、スキップフレームというのはたぶん一般的な呼び名ではないでしょう)。
2000/05/04追記:一般的な呼び名は「フレームスキップ」。なぜ逆にしていた?
●まずは普通に
四隅からボールが出てきて、画面中を飛び回るというプログラムを普通に組んでみました。処理落ちなしで秒間30フレームで動かすことにします(実際にはいろいろな関係で29フレームまでしか行かないようですが)。なお、プログラムの実行には例によってDSOUNDEX.DLLが必要となります。
このプログラムを作者の環境で動かしたところ、ボールが最大数の512個に達した時点での処理速度は、
←秒間18フレームにまで落ちていました。さて、これをスキップフレームさせて秒間30フレームをキープさせましょう。
●1フレームで書けるだけ書く
処理を一定に保つと言うことは、1フレーム(ここでは毎秒30フレームなので約0.033秒)以上処理に時間をかけてはいけないと言うことになります。では1フレーム経過したら処理をやめて画面を更新すればよいかというとそうではありません。なぜなら、redrawを使って画面を更新するのにまた時間がかかるため、そこまであわせて1フレームでなくてはならないのです。
また、処理落ちしてもちゃんと同じようにキャラが動いてくれなくてはならないわけですので、「計算だけ先に済ませておき、画面を描画する」ことになり、必然的に計算部分は1フレーム以内に収まっていることが条件となります。
とりあえずここまでの処理の流れを簡単に書くと、
1.画面を消去する
2.キー入力や、キャラを動かす為の計算を行う
3.画面を描画する
4.画面の更新をあわせて1フレーム経過した時点で1に戻る
まあこんなかんじです。ここで一番難しいのは「画面の更新(redraw命令の実行)をあわせて1フレームというのをどう判断させるか」だと思います。DLLなしにはなかなかいい方法がないので今回はこんな風に組んでみました。
timer tm=stat if tm-tr>qq : slow=1:break loop redraw 1 timer tz=stat qq=32-(tz-tm) |
処理落ちを判断させる部分だけ抜粋したので分かりづらいですが、loopはキャラクタの表示ループです。タイマーを監視して変数qqの値以上になったら処理落ちフラグを立ててループを抜けます。その後にredrawを実行し、別の変数にタイマーの値を読み、redraw前と後にタイマーが返した数値の差から画面描画にかけることのできる時間を割り出します。こうすることで「画面の表示+画面の更新」が1フレーム以内に収まるようになります。
とりあえずこの段階まで組んだのがこれです。
←ボールの数が512個になっても28フレームで安定しています。29フレームにならないのはまあ仕方がないことでしょう。
ただしまだ「1フレーム経過した時点で処理をやめる」という方式のために問題点が残っています。それは何かというと、
←右が計算されたボールの数、左が実際に表示されたボールの数です。なんとたったの64個しか表示できていません。3Dゲームならば奥のオブジェクトを削ることで一応納得行きますが、2Dでこれじゃゲームになりませんし、そもそもチカチカして目が痛くなります。
●2フレーム目に続きを書く
ではどうすればいいか。簡単です。書ききれない分は2フレームめ以降に回し、全て表示できるまで画面を更新しなければいいのです。手順はだいたいこんな感じです。
1.処理落ちフラグが立ってなければ画面を消す
間違えて1フレームごとに画面を消してしまうと画面がチカチカするだけでなく、スキップフレームの意味が全くなくなります。
2.計算を行う
ここは同じです。
3.処理落ちを監視しつつキャラを表示する
処理落ちフラグが立ってなければはじめから表示し、立っていた時は前のフレームで表示できた分の続きから表示することになります。これを間違えると当然意味なし。
4.処理落ちしていなければ画面を更新し、していたなら表示できたキャラの数を記憶する
ここが一番大事なところです。忘れたり間違えたりすると画面が更新されなかったりキャラが全部表示できなかったり。
というわけでようやく目標達成です。実行すると見事にスキップフレームして秒間フレーム数は一定に保たれています。
←25フレーム安定。計算などの関係で処理落ちしてますが、あと200個ほどボールを発生させればフレーム数は29まで戻ってくれます(笑)。
←ちゃんとボールは512個全部表示されています。
←処理落ち2フレーム。さすがにガタガタします。
●実用性は?
今回作ったスキップフレーム処理はかなり簡単なので、実際にゲームで使うにはこれをこのまま使うのは無理があります。実用的なスクリプトにするなら表示するキャラクター数の増減に柔軟に対応できるようにしなければなりませんし、自機、敵、弾などのキャラクターの管理がしっかりできなければなりません。また、ここで取り上げた方法でスキップフレームを行うと表示にズレが生じるためかえって遊びにくくなるかもしれません。まあ、これをやると技術面で誰かがほめてくれるかもしれませんので組み込む価値はあると思いますよ(笑)。