■ 新・ゲーム開発講座 |
■ へっぽこプログラミング入門♪ |
■第45夜:SAVEするためのウォーミングアップ システムメニューもいよいよ大詰めとなってきました。いよいよSAVE/LOAD関係のインプリメントです。しかしSAVEするという行為は意外と大変な内容ですので、今回はその準備に留めます。SAVEメニュー本体のコーディングをする前に、いくつか決め事をしておきましょう。文字ばっかりですみませんけど…(大汗 ^^;) ■SAVEとは何か ところでSAVEというのはどういう処理でしょうか。平たくいえば、現在のゲームの進行状況を保存することです。言葉でいうととても簡単です。しかし「現在のゲーム状況」とは、プログラム的にどの程度正確に保存/再生できるものなのでしょうか。 最も単純に思いつくSAVE動作は、ありとあらゆる変数をそのままDISKに保存することでしょう。実は本HPの試作ノベル「長野旅行記」はそれに似たSAVE/LOAD方式をとっています。ほとんどすべての変数を本稿でいう Mode_stat に相当する構造体にパッケージにし、その構造体をそっくりファイルとしてHDDに書き出していたのです。今から見ると、よくもまあダサいことをやったものだと思いますが、いくつも平行して走るタスク処理をうまく保存するにはどうすればよいか…という苦悩のひとつの解だったわけで、当時はそれが一番確実だと思っていた訳です(うーむ ^^;) この方式の欠点は、とにかく構造体が無意味に巨大化して意味不明のデータの塊になりやすいことです。本稿を書くにあたって、これをそのまま踏襲したら講座としては落第(笑)だろう、と思ったので現在の
Mode_stat はループフラグとイベントフラグ程度の非常にシンプルなものに衣変えしていますこれは、平行して走るタスクをすべて「瞬間スナップショット」的に保存することを放棄した結果です。 それはともかく、ここでまずSAVE/LOADにまつわる問題点/留意点を列挙していきましょう。細かく挙げていけばキリがありませんが、くににんは主に3つくらいに集約できるのではないかと思っています。それは以下のようなものです。 |
|
ここでいうタスクとは、本稿のプログラムの Mainloop()
からちょこまかと呼ばれる各種エフェクト類の関数群を指します。Windowsのタスクマネージャで表示される個別プログラムのことではありませんので念のため…(^^;)
たとえば特殊エフェクトがいろいろ使える素晴らしいノベルツールがあたっとします。スクリプター(ツールで走らせるスクリプトを書く人)は情熱的にあらゆるコマンドを駆使して超絶美麗な画面を構成しています。256種類のBMPを次々と読み込んでちまちまと転送し、芸術的なコラージュを画面に表示しました。…さて、その画面は正確に保存/再生できるでしょうか? (@▽@) 理論上は、保存/再生はおそらく可能です…とゆーか、可能でしょう、たぶん(^^;)。しかしプログラム的なコストは非常に高くなると思われます。最終的に出来上がった表画面に表示されている部分を1枚絵として保存する…という妥協案もあるかもしれませんが、ゲームの進行状況によってはそれだけでは不十分な場合があります。LOAD処理でその1枚絵を画面に復元できたとしても、その直後に走り出したスクリプト上で、別の画面
(SAVE前にメモリ上で操作していた) から画像の一部をコピーするような命令が書かれていたら、そこで画面はぐしゃぐしゃになるかもしれません。
単純にSAVEを行ったときのスクリプト位置を再現するようなLOAD/SAVEだと、LOAD直後に支障がでる場合があります。たとえば1ページに10行のテキストが表示されるスクリプトがあって、その5行目の途中のタイミングでSAVEが行われたとします。これをLOADして、そのまま続きを表示したらどうでしょう?本来画面に見えていてほしい最初の5行ぶんの文章が存在しない状態で画面が流れ始まってしまいます。これはいけません。ページ単位でテキストをめくっていくノベル形式では、ロードしたらそのページの先頭から表示が始まってほしいとくににんは思います。ところでこれをプログラム的に実現しようとすると、メニューを開いてSAVEを実行したタイミングと、保存すべきSAVE状態に時間差があるということになります。思惑通りの動きを実現するには、なんらかの対策が必要です。 |
|
上記のような問題を解決するために、世のゲームデザイナさんやプログラマさんはいろいろな工夫を重ねています。現実との折り合いをつけ、プレイヤーに違和感を抱かせない程度にいろいろな制限を課して、プログラム的なコストを下げる努力をしてきたわけです。こうした先人の知恵に我々は敬意を払わなければなりません。現在よりも過酷で貧弱なマシン環境、SAVEメディアの制限・・・こうした歴史に鍛えられた暗黙のルールは貴重です。(ん〜なんだか、今日は風呂敷が大きいぞ:笑)
言うまでもなく、これらはSAVEしやすくLOADしやすいタイミングをつくるための制限です。いつも同じ背景の「主人公の部屋」や「キャンプモード」にSAVEポイントを限定すれば、少なくとも画面構成の再現について悩む必要はなくなります。 また脈絡なきダンジョン奥のSAVEポイント(笑)や突然ポップアップする「SAVEしますか?」は、その直後に長時間イベントの初期化処理を行っている可能性が濃厚と思われます。章の最初に戻ってしまうのも、本質は同じです。変なタイミングでLOAD/SAVEされては困るので、初期化が確実に行われるように制限をかけるのです。 ・・・まあ、細かいことを言い出すとキリがないので、ここは 「処理が楽になる暗黙のルールがあるなら積極活用する」 のが吉ではないかとくににんは思います(・ω・)ノ ところで暗黙のルールなんてものが思いつかなかったら? ・・・作ってしまえばいいのです。プログラマはゲーム世界を作り出す神の立場で君臨しています。いっつまいわーるどです。モーゼに十戒を与えたヤハウェのように、「俺がルールだ、わっはっは!」 で決めてしまって問題ありません。・・・ただし実装するのは自分ですから、なるべく穏便なルールにしたほうが無難といえますが(ぉ ■本稿におけるSAVE/LOADの取扱いについて そのような次第で、カーソルブリンク中であればいつでもシステムメニューが開いてしまう我らがへっぽこノベルツールにおいても、予想される不具合を未然に防ぎ、かつそれでいてスクリプターの自由度をあまり損ねないような仕組みを考える必要があります。といっても、複雑怪奇な仕掛けを作る(=難しい方向へずんどこ走り出す)というのは本講座の趣旨に反しますので、なるべく平易なものを目指したいところです。そのために、次のような一種の割り切った前提を掲げます。
…これで、ずいぶんすっきりしました。 要するに、SAVE/LOADに関しては最低限のケアに留め、SAVEするタイミングやLOAD後の復帰についてはある程度スクリプター側で配慮してもらおうという内容です。スクリプターの都合で「ここでSAVE/LOADされたらまずい!」というケースについては、システムメニューを一時的に強制OFFできる仕組みを提供することにします(フラグを1個用意すれば良いでしょう)。 さて、これでようやくSAVE処理のための方向性が整いました。では続いてもう少し具体的な動作のイメージを描いてみましょう。 まず、スクリプト上で明示的にSAVEポイントを表すコマンドを定めます。ここでは単純に
とでも致しましょう。 #sp は短縮表記版です(大量に設定する場合に長いと面倒なので…^^;)。#save_point コマンドは、メモリ内にSAVEデータイメージを作成します。具体的にはSAVEデータ用構造体に、必要な情報をパッケージします。ここでいう必要な情報とは、
のことです。特に画像については、読み込んだ後でメモリ上でどのような加工をしたかetc…については関知しません。背景画像の上に #g_copy コマンドで登場人物の顔をぺたぺた貼りまくったような画面を構成していたとしても、そのような操作履歴は残さないのです。画面構成をLOAD時に再現したかったら、スクリプター側で #save_point コマンドを画面構成するスクリプト列の直前に挿入しておく等の工夫が必要です。その工夫をスクリプター側にある程度担ってもらうことで、プログラム側の負担を少なくしている訳です。(なんでも自動化しろとプログラマに押し付けるシナリオ屋さんと組むと、このあたりでプログラマの苦労が倍増しそう・・・ ^0^) さて、SAVEデータのパッケージが出来たら、あとは何事もなかったようにゲームの処理を続けます。次の #save_point コマンドにぶつかったら、以前の内容は破棄してまたパッケージしなおしです。これを延々と繰り返すのがゲームの動作になります。 プレイヤーがシステムメニューを開き、SAVE命令を発したら、その時点でメモリ内にパックしてあるSAVE構造体をDISKに書き出します。改ページの直後に #save_point コマンドを記述しておけば、たとえページの終わり近くでSAVEを行ったとしても保存されるのはページ先頭になる訳です。いかがでしょう、イメージできましたでしょうか? その他、SAVE/LOADを支援する機能として、以下の機能も実装することにしましょう。
システムメニューのON/OFFはいいとして、SAVEポイントの自動生成って何?と思う方もいるかもしれません。これはあまり複雑なスクリプトを流さないことがあらかじめわかっている場合、改ページ処理を行った直後を自動的にSAVEポイントとして認識するという機能です。これならいちいち #save_point コマンドを記述しなくても、現在表示しているページの先頭が改ページ毎にパッケージ → SAVEされることになり非常に便利です。もちろん自動生成はフラグをON/OFFすることでスクリプト側から自由に指定できるようにします。これだけ機能が揃えば、ちょっとしたノベルのSAVE/LOADには十分でしょう。 さて、欲しい機能を列挙したらずいぶん盛りだくさんになりました(^^;)でも実用性を考えるとこれくらいの機能が揃っていないとどうしても使いにくいと思います。頑張って実装していきましょう。 ここで重要なのは、SAVE/LOADはコマンドひとつ作成すればOKというものではなく、常にシステムとして考えなければいけないということです。SAVEする必然性がなければ、プログラムはいかように奔放(適当?)に記述しても許されます。しかしSAVE/LOADを前提とするならば、いかに保存し、いかに再現するかを必ずどこかで考えておく必要があります。その準備を怠ると、ゲームは起動するけどSAVEはできない…という悲しい事態に陥りますので…(爆)
今回は前振りだけでえらく長い話になってしまいました(…しかも最後が超弱気
^^;)。 |