ゲームプログラミング/特殊イベント
特殊イベントのフラグの作り方
[編集]フラグの基本
[編集]RPGなど、ゲーム中で1回しかおきない特殊なイベントとかを作りたい場合があるでしょう。RPG以外でも、シミュレーションゲームなどで特殊イベントを実装したいこともあります。たとえば、もし日本の中世の戦国時代シミュレーションゲームで「桶狭間の戦い」が3回も起きたりしたら困ります。
RPGの場合、1人しかいない中ボス敵(生物)を倒して殺すイベントは、1回しか起きなくては困ります。そして、たとえば中ボスを殺すと、大ボスの居城に行けるようになるストーリーだとしましょう。
どうやって、こういった特殊イベントを実装すればいいでしょうか?
とりあえず、C言語で手段を問わずに単にこれを実装するだけなら、プレイヤーが中ボスを殺したときに tyuubosDead=1
とかセットして書けばいいわけです(もし tyuubosuDead=0 なら、まだ中ボスを倒していないとする)。
こういうイベントの判定などの処理のために使う変数のことを、ゲーム業界用語で「フラグ」といいます。
ある行動をまだしてない状態のとき、フラグをOFFだとします。フラグがOFFの状態を数値「0」とします。
したら、以降はそのフラグがONになります。ONの状態を数値「1」だとします。
あとはif文などで、フラグ変数をもとに、イベントの発生の有無を判定するシステムにすればいいだけです。
原理的に、特殊イベントの処理はすべて、フラグ変数の読み書き処理で可能です。後述する様々な手法も、フラグ変数の読み書きを基礎にしています。
ゲームでは3段階のフラグも必要
[編集]ゲームでは3段階のフラグも必要です。
一般にIT産業では「フラグ」と言った場合、状態 on または off の意味で、数値「1」か「0」の二つのいずれかの使います。
このような2個の数値で管理する方式を、数学の記号論理学の用語を使って「2値論理」と言います。
ですがゲームでは数学とは異なり、
- イベント状態 の「未発動」
- または「イベント発動中だがイベント未クリア」
- または「イベントクリア済み」
の3種類の状態を使うこともあります。
たとえばゲーム中では、「ある用事を頼まれたけど、まだその用事を解決していない」のような場合もあります。
さて、ゲーム中で何かの用事や依頼のイベントを扱う場合、もしONかOFFかの2通りだけだと不都合です。なぜなら、
- そもそも、その用事を依頼すら、されてない段階.
- 用事を依頼されたが、まだ解決していない段階.
- 用事を解決した段階.
という3段階の区別が必要なのに、ON/OFFの2通りだけでは区別できないからです。
このようにゲームでは、情報科学の伝統的な理論とは異なり、イベント変数としてのフラグ変数を「2」または「1」または「0」の三つのうちのいずれかを使って記述する場合もあります。
なので、必要に応じて、3段階のフラグをなんらかの方法で実装する必要が生じます。
3段階フラグ変数をどう実装するか?
[編集]ゲームでは、3段階のフラグが必要になる場合もあります。
では、どうやって、ゲーム用の3段階のフラグ変数を用意するのでしょうか?
下記のような、いろいろな方式があります。
どの方式も一長一短ですので、あまり上手い方法はありません。 なので最終的には、ご自身の制作されるゲームに適した方式を使ってください。
3値変数の方式
[編集]もっとも単純なのは、整数変数(たとえば int ibento[配列指数] みたいに宣言)を用意して、値が0なら未発動、値が1なら発動中、値が2ならイベントクリア済み、のように判定する方式です。
つまり、
- int ibento[配列の番号] = 0 なら、そのイベントが未発動
- int ibento[配列の番号] = 1 なら、そのイベントが発動中
- int ibento[配列の番号] = 2 なら、そのイベントがクリア済み
たとえば依頼イベントなら、とりあえず、
- 依頼されてない段階: 0
- 依頼されたが解決してない段階: 1
- 解決した段階: 2
とでも、数値の意味を設定しておけば大丈夫でしょう。
C言語でゲームを作るなら、配列を使って、0/1/2の三値のフラグ変数を管理すると、ラクに管理できるでしょう。
この方式の欠点は、メモリを余分に使うことです。数値は0、1、2しか使わないのに、整数型を宣言しているので、確保したメモリのほとんどが使われていません。
ただし、現代では、下記コラムのような理由で、メモリが十分にあまっているし、真/偽の2値しかとらない論理型であるbool型で宣言したとして8ビット(0~255あるいは-128~127の幅)をメモリ利用しますので、あまり気にせず、通常のint型かまたは論理型のbool型で宣言するのが安全だろうと思います。
なお、後述するビット列の手法は、バグ耐性の悪さから、あまり推奨できません。
Visual C++ で定められている8bit整数型 __int8
などは非標準です。
アンダースコアで始まる整数は、非標準であることを意味します。[1]
なお、__int8
で -128 ~ 127 の数値を扱えます。
__int8
は 1バイトです。1バイト以下の数値型は、現代のWindowsでは、もはや用意されていません。
(つまり、__int4 や __int2 のようなものは無い。 )
通常の int は、Windowsでは 4バイトです。
通常のint では -2,147,483,648 ~ 2,147,483,647 の数を扱えます。(マイナス約21億~プラス約21億です)
通常int と__int8
では扱える数に21億近い差があるので誤解しがちですが、
しかしバイト数ではたったの3バイト差しかないので、なるべく気にせず通常のintを使いましょう。
けっして21億ビットや21億バイトの差があるわけでは、ありません。
そのほか short という数値型もありますが、これもWindowsでは2バイト数値型ですので、大したバイト数の差はありません。
char 型で1バイト整数を扱えますが、しかし、ぱっと見だと、文字列と紛らわしいです。
処理系によっては int8_t という型がありC99でも定義されており #include <stdint.h>
で利用可能とされていますが、しかし実装の有無が処理系に依存しますので、あまり互換性が良くありません。実際、windows7のvisual studio 2019 では int8_t 型が動作しませんでした。
なお、bool型は真と偽の2値(1ビット)しか取りませんが、しかしWindowsでは1バイト型(8ビット)です。つまりbool型で宣言したところで、Windows内部では少なくとも0~255あるいは-128~127の幅に相当するメモリ使用量が、bool変数1個あたりに確保されています。
このため、int型で宣言しようがbool型で宣言しようが、常識的な普通のゲーム中での変数としての使用法なら、大して使用するメモリ量は変わりませんので、
メモリ量などは気にせず、自分の管理しやすいほうを使うのが良いでしょう。
なお、数学でいう「3値論理」とは、上述イベント変数のような手法とは、本来は意味が異なる。1950~1970年ごろ、かつて三値論理学が大学などで研究された事もあったが、現代では滅多に使わないので、気にしなくていい。
2個の2値変数を使う方法
[編集]2個の2値変数を使って、イベントの3段階の状態わけ(未発動/発動中/クリア済み)を実装する方法もあります。
たとえば、配列で「イベント発動判定フラグ」配列 hatudo[]および「クリア判定フラグ」配列 kuria[]を用意して、
たとえばイベント番号が8番のイベントを発動させるなら hatudo[8]=1 にセットするような方式です。
- ※ 厳密には配列は0番から数えるので8個目は hatudo[7] にするべきかもしれないが、説明の単純化のため無視した。
その後、イベント8番をもしプレイヤーがクリアしたなら、」kuria[8]=1 にセットするというわけです。
この方式の利点は、フラグの意味が明確になる事と、もうひとつは値をブール型で宣言すればメモリの節約になることです。
短所として、変数または配列の数が2倍になります。
また、イベントクリア後に発動判定フラグをどう処理するか(0に戻すか、それとも1のままにするのか)、人・会社によって方針が異なるかもしれません。
ビット列
[編集]これからビット列の話をしますが、しかしゲーム制作では、できれば(ビット列の処理ではなく)配列でイベント変数を実装するほうが安全です。
もしビット列を使うなら、考え方は下記のようになるでしょう・・・・・・・
配列の代わりに、ビット列を使う方式もありますが、しかしゲームのフラグ管理では、あまり実用的ではないです。
まず、情報科学にはビット列という概念があります(たとえば 1011 0010 みたいな数値列)。
製作ツールやコンパイラなどで用意されているビット列は、2値ビットです。
なので、たとえば
たとえば、
- 1110
という2値ビットを、
- 4個目(いちばん左)のイベントはクリア済み、
- 3個目のイベントはクリア済み、
- 2個目のイベントはクリア済み、
- 1個目(いちばん右)のイベントは未クリア、
という意味だとしましょう。
これをもし、現在未発動である1個目のイベントをこれから発動させようとして、本来なら1110 に 0001を足して2値ビット列を本来なら「1111」にすればいいのですが、
もしケタをひとつ間違えて、0010 を足してしまうと
1110 + 0010 ----- 10000
というふうに、プラス1をミス入力した2ケタ目だけでなく、さらに繰り上がりにより、ひとつ上の3ケタ目もプラス1してしまい、バグが連鎖的に波及してしまいます。
もしかしたらファミコン時代の古いゲームなら、メモリの節約のためビット列で処理する事もあったかもしれませんが、現代なら配列でイベント管理するほうが安全でしょう。(ファミコン時代のRPGによくあるバグで、メモリのオーバーフローを起こしてイベントフラグのズレるバグが幾つかある。)
なお、ツクールやウディタでなくプレステ作品などのゲーム開発でも、ゲーム会社はRPG制作ではイベント処理に会社内製スクリプトを使っている場合があります。
C言語では16進数(0からFまで)を扱えるので原理的には16進ビット列(正確には「16進バイト列」というべきだろうが)等もありえますが、しかし慣習的にゲームのイベント管理で使うビット列は、よく聞くのは2値ビットまでです。それ以上は寡聞にして聞きません。
フラグのバトンリレーによる方法
[編集]一本道シナリオのゲームなら、イベントのクリア判定と、次のイベントの発動とを兼用するという、バトンリレーのような方法も可能です。
バトン1個の代わりに、イベント1個につき、on/offの状態分けだけで配列変数を1個だけしか使わない方法も、ゲームのジャンルによっては可能です。
これはこれで利点もある方式で、一本道シナリオのゲームならバグ防止にもなり、ゲーム中で本来なら発動すべきイベントが未発動になるバグなどを未然に防げる長所もあります。
ですが短所として、一本道でないフリーシナリオ的なイベント攻略順序の自由なゲームの設計が、バトンリレー方式では、なかなか難しくなるでしょう。
また、仕様変更などでイベントの発生順序が変わった場合なども、修正が面倒になるでしょう。
うまい方法は無い
[編集]ゲームのフラグ処理には、あまり上手い方法はありません。
また、あまり凝った特殊フラグ処理のシステムを実装(?)すると、デバッグがとても大変になります。
なので、初心者は、あまり凝りすぎないフラグ管理システムにしましょう。
特に、特殊イベント処理のデバッグには、ゲーム全体を最初からエンディングまでプレイする「通しプレイ」によるデバッグが必要になることもありますので、あまり凝りすぎないようにしましょう。
つまり、2000年代以降のゲーム制作において、イベント処理の実装とデバッグは、じつはゲーム開発上のボトルネック( 制約(せいやく)になりうる要因 )です。
(デバッグ作業の問題ではなく)動画の画質の向上などハード面の問題なら、解決には単に、ゲーム機のハードウェアにメモリ基盤などを増やすというハード的な方法で対処できます。しかし、ゲーム内の特殊イベントのデバッグは、人間の感性をもった人員が必要なので、これはメモリをいくら増やそうがCPUを増やそうが、ハードだけではデバッグは解決しません。
まず、イベント処理にかぎらず、デバッグ作業は基本的に ITソフトウェア開発のボトルネックです。そしてゲーム開発において、特殊イベント処理のデバッグは、そのデバッグ作業のなかでも、手間のかかる部分です(通しプレイなどが必要なので)。
「ボトルネック」とサラッと言いましたが、意味を言うと、「ボトルネックとは、その原因のせいで、ほかの部分の効率やスピードまで低下するとき、 おおもとの低下の原因になっている部分をボトルネック」と言います。「ボトル」は瓶(びん)を英語のbottleのことです。「ネック」neck とは首のことです。洋服でいうネックシャツなどの、ネックのことです。
どこの業界でも設計や管理職などの分野では「ボトルネック」という言葉は使うので、 覚えておいてください。
データベース的なイベント処理の方式
[編集]ツクールなど開発ツールを使う場合
[編集]では、ツクールとかウディタとか、ああいうゲーム開発ツールでは、こういう特殊イベントのフラグ処理をどうやって実装しているのでしょうか?
ツクールなどの幾つかの開発ツールでは、「データベース」またはそれに類する項目があり、じつはこのデータベースを作者に使わせて、フラグ変数を代用させる仕組みです。
RPG開発ツールの「データベース」は、武器やモンスター名などのデータベースでもありますが、イベントフラグにも流用されます。
なお、RPGツクールやウディタなどでは、けっして、わざわざイベント専用の変数項目は用意されておらず、代わりにゲーム作者が自由に作成できる変数項目がいくつかあるので、それを組み合わせてゲーム作者が自分で作る必要があります(RPGツクールの場合、アイテム用データベースのなんらかの特殊アイテム所持数をイベントフラグとして流用するのが簡単でしょう。ウディタの場合、特に目的の定められていない「可変データベース」で目的の定まってない変数が作成できるので、それのいくつかをイベントフラグとして流用することになるでしょう)。
「戦闘中のボス敵のHPが半分以下なら、○○のイベントが起きる」などのイベントを実装したいゲーム作家さんもいるので、けっしてイベントフラグ専用の変数を、いちいちツクール開発企業(現在は角川書店、もともとはアスキー)は用意してくれません。
さて、ゲーム開発ツールは配列を流用している。ツールが配列を用いているだろうことの証拠として、ツクールやウディタでも、作成した変数にはid番号が1番(または0番)から順についており、このid番号を通じてイベントなどを制御する仕組みである。id番号が、配列の要素の番号であろう。
だから、製作ツールでよく「変数」などというものの、実際は配列の代用であることになる。
このため、配列の要素数の限界がある以上、ツールで作成できる「変数」の要素数にも限りがある可能性が高く、そのため長編ゲームなどにはゲーム制作ツールは不向きであろう。
このため、イベントも配列で管理せざるをえないし、その要素数には上記の議論のとおり限りがあるはずなので、あまり長編ゲームには制作ツールは向かないことになる。
なお、どうやら製作ツールでは、ゲーム中にイベントを挿入するさいに、データベース中の指定した項目の数値を参照して、その数値が「○○に等しいか?」「○○以上か?(以下か?)」などの条件判定をして、条件が真なら、真の場合の処理を実行するという、単純な仕組みらしいです。
また、どのマップにも固定敵とのバトルにも、イベントを挿入できる仕組みになっていますので、作者は必要に応じて、どのマップでも固定敵バトルでもイベントを起こせるのです。
Visual C++などで自作の場合
[編集]なお、もし読者のあなたが(RPGツクールやウディタみたいな)ゲーム開発ツールそのものを自作したいなら、RPGの戦闘システムやマップシステムに加え、こういったデータベースを流用したイベント管理フラグを実装すればいいわけです。
- C言語でデータベース的なものを構築する方法なら、いわゆる「構造体の配列」または「配列の構造体」というもので簡単に実装できます(wikibooksでも『C言語/構造体』で解説しているでしょう)。構造体の配列は、情報系の専門学校や大学の情報系学科などでも普通に習う、ありふれた手法です。(しかし市販のプログラミング入門書には、なかなか書かれていません)
なお、配列のほかにもデータベース的なものの構築方法はあり、テキストファイルなど外部ファイルに書き込みをして入出力する方法でも、データベースを構築できます。C言語のfopen関数で、簡単にファイルに読み書きできます。作成するテキストファイルを隠しファイルにしておけば、ユーザーが書き換えする心配も減らせますし、もし書き換えてしまってもユーザーの自己責任です。なお、Windowsでプログラミングで隠しファイルの作成などをするには、Windows APIの CreateFile関数で、ファイル属性の操作で FILE_ATTRIBUTE_HIDDEN を指定すれば、隠しファイルを作成したりできます。
さて、特に、CSV形式(シーエスブイけいしき)という書式のテキストファイルによるデータベースは、ゲーム業界にかぎらず、一般のIT業界でもよく使われる方法です。
また、テキストファイルなど外部ファイルを読み書きする方式の場合、データベースが外部出力されるのでユーザーがデータベースの中身を閲覧可能になりますので、機密性などは下がりますし、それでもデータベース内容を機密にしたい場合は暗号化などの手間が掛かります。
自分でゲーム開発ツールを造る場合は、作りたいゲームにあわせて、配列方式か外部ファイル読み書き方式か、データベースの方式を選びましょう。
どちらの方式にせよデータベースによるプログラミングは、まるで、昔の機械語による変数の実装でメモリ番号の指定による実装を、代わりにC言語の配列の番号またはテキストファイルなどを用いて擬似的に実装したかのような方法です。なので、一応は、ツクールのような方法でも、利用できる変数の個数が足りるかぎり、ほぼ、どんなイベントでも作れます。
また、欠点として、まるで擬似的な機械語みたいな実装方法なので(より正確に言うなら、配列による擬似メモリマップか。メモリマップについては『オペレーティングシステム#メモリマップ』で解説)、当然ですがC言語のような構造化プログラミングの洗練された手による支援は、ゲーム開発ツールによる上述のようなデータベース駆動型のプログラミングでは、ほぼ見込めませんので、このためソースファイルの内容が、第三者や後日の自分が見て内容の分かりりづらい、いわゆる「スパゲティコード」のようなソースファイルが作られやすくなります。
ひとつの配列を、番号ごとにゲーム内の異なる様々なイベント処理のフラグとして流用するわけですから、「どの番号がどのイベントに割り当てしているか?」などを作者は把握しておかなければならず(いちおう、番号ごとに変数名をつければ暗記しなくてすむが)、管理が面倒になります。
もし、データベースを使わずにC言語で直接にイベント処理を記述するなら、普通は異なるイベントには、異なる配列を宣言して作成するわけですので、番号の割り当ての把握は不要になります。
原理
[編集]RPGのような物語のフラグを作る場合
[編集]RPGのような物語のフラグを作る場合、原理的には、バトンリレー的に、
- 「1番目のイベントをクリアしたら、2番目のイベントの発生条件フラグが1になり、よって2番目のイベントが始まる」
- 「2番目のイベントをクリアしたら、3番目のイベントの発生条件フラグが1になり、よって3番目のイベントが始まる」
- 「3番目のイベントをクリアしたら、4番目のイベントの発生条件フラグが1になり、よって4番目のイベントが始まる」
- (以下略)
のように、連鎖的にどんどん、各イベントをクリアするたびに次のイベントが始まるようにすれば、ゲームクリアまでの物語のイベントを実装できます。
さて、原理的には、上述のような逐次的というか1個ずつ進んでいく方法でもゲーム全体の物語のイベント管理も可能ですが、物語が長くなってくると非効率になってきます。
物語の進行に関するイベントが数百個もある場合、いちいち管理するのが大変です。
このような膨大なフラグを管理する場合、どうするかというと、ストーリー全体像のフラグと、ストーリー各部のフラグとを用意するのが良いかと思います。
たとえば、そのゲームがたとえ「第1話」とか「第2話」とか「第10話」とか別れてなくても、仮に「ゲーム開始からここまでのイベントは第1話とする。」とか「ここからここまでは第2話とする。」みたいな感じで、脳内でストーリーの区切りのいいところで、ある程度おおまかに、それぞれのイベントが第何話に相当するかを分けます。
で、「第1話の内部のそれぞれのイベントフラグ」、「第2話の各部のそれぞれのイベントフラグ」、・・・というふうに、別々に作っていきます。
この「第◯◯話」のように、そのゲームのストーリー全体像を管理するフラグを用意します。その後、各話数の個別のイベントを作っていきます。
こうすると、たとえば第2話の状況なら、すでに第1話のイベントはすべて終えているハズなので、いちいち第1話の個別のイベントのオン/オフを確認する手間が省けます。
なお説明の都合上、上記では第「2」話みたいに数値で説明しましたが、実際のゲームストーリー開発では個別エピソードの順序が変更する場合もあるので、あまり数値で管理するのではなく、『○○編』(~へん)みたいに各エピソードの題名みたいなのを暫定的につけて管理するほうが、たぶんラクでしょう。
つまり、物語を『○○編』とか『△△編』や『□□編』とかに何分割か、しておきます。そのあとから、それらの各「編」の順序を、ゲームがもっとも面白くなるように並べ替えていくのです。
イベント管理のフラグを作る場合も、たとえば「『○○編』をクリアしたら、次の編である『△△』編が起きる」みたいに編単位で管理していけば効率益でしょう。
- ただし歴史シミュレーションは例外
ただし、上記のような方法でイベント管理を作れそうなのは、あくまでも物語の順序がほぼ一通りに決まっている場合です。具体的には、RPGやアドベンチャーゲームなどのジャンルです。
歴史シミュレーションゲームなどで、しかも史実どおりで無い展開も可能なゲームの場合、上記のような物語形式のフラグ管理は無理でしょう。
たとえば、日本の戦国時代シミュレーションゲームによる仮想戦記のシミュレーションで、たとえば「桶狭間の戦い」は(今川を先に信長が倒したので)起きないが、しかし「本能寺の変」が(明智光秀が生きてるので)起きる場合などの展開なども、ゲームによっては、ありえます。
こういう場合、面倒ですが、話数形式のフラグ管理が使えないので、イベント発生条件を1個ずつフラグ管理していく方法しか、ないでしょう。
イベントの終了と次イベントの引継ぎについて
[編集]あるイベントのクリアから、次のイベント開始までの発生の引継ぎをどうするかは難しそうです。もしかしたら作家によっても違うかもしれませんが、とりあえず、下記のような対策が考えられます。
方法1: 次回イベントのオープニングを終了フラグに流用
[編集]たとえば第2話のイベントを始めさせる場合を考えましょう。
さて、もし第1話を終わらせるなら、なんらかの方法で、確実に(第1話の終了直後に)第2話を直後に開始しなければなりません。
このような技法としては、いっそ、第1話の終了フラグがONになるタイミングを、第2話の開始時点にする手法もありえるでしょう。
この手法を使うと、確実に、ストーリー進行での次のエピソード開始への引渡しが可能です(というか、すでに引渡しが完了してから前のイベントを終了させる方式である)。
プレイヤー視点では表面的には、あるゲーム中のシーンが第2話の開始オープニングイベントの自動進行のように見えても、もしかしたら実はプログラム内部的には、そのシーンは第1話のエンディングイベントの自動進行として処理されているかもしれません。
方法2 :とりあえず問題解決までイベント終了させない
[編集]ところで、第1話のクリア条件を満たした場合に、もし即座に第1話を終了してしまうと、当然ですが第1話のエンディングを見られません。
たとえば、あるゲームの設定で、もしRPGで第1話の敵ボス犯罪者を倒すと、ボスが何かセリフを残して死んだり、ボスが誘拐犯なら誘拐された被害者を救出するイベントが自動進行で発生してから、第1話が終わって第2話が始まるとしましょう。
ボスを倒せば第1話のクリア条件が満たされますが、しかし、もしボスを倒した瞬間にフラグを第1話の終了に設定してしまうと、その時点でもう第1話のイベントの進行が止まるので、ボスの残すセリフが見られませんし、ボスに誘拐された被害者の救出イベントにも進行せずに、誘拐された被害者は被害を受けたままになってしまいます。
つまり、第1話のクリア条件のフラグ変数とは別に、第1話の終了条件の変数を用意する必要があります。
つまり、「クリア条件を満たしたが、まだエピソードを終了させてはいけない」みたいな場合もあるのです。
こういう場合、どう処理すればいいかと言うと、そのエピソードの作中のトラブルなどを確実に解決した段階まで、そのイベントフラグを終了されてはいけません。
上記のボスによる誘拐事件の場合、少なくとも、誘拐された被害者の救出が達成されるまでは、けっしてイベントフラグを終了に設定してはいけません。
「ストーリー進行度」
[編集]なお、一般にゲーム用語で『ストーリー進行度』や『シナリオ進行度』という表現で、そのゲーム全体のストーリーの進行の度合いを表現することが、ゲームファンにありますが、しかし実際のゲーム業界が果たして本当にそういう変数を用意してるかどうかは不明です(ゲーム会社がソースなど非公開のため)。
ただし、もし自作ゲームの完成度がすでに高くて、開発に余裕があるなら、さらに「ストーリー進行度」に対応する変数を用意しておくと、何かと今後の編集に便利かもしれません。
なぜなら、「もしストーリー進行度が○○以上なら、このイベントが発生する」などの方法を使えるようになるからです。そして、このイベント発生方法は、後述しますが、バグを抑えやすいというバグ耐性の長所があります。
ただし、実際のゲームのストーリー開発では、ストーリー内の物語の順序を開発中に、なんども(順序の)変更することがあるので、あまり開発の最初から「ストーリー進行度」に基づく設計を行わないほうが安全だと思います。
もしストーリーの順序がもう確実に決定済みで、幾つもの特殊イベントの発生順序が決まってる場合などは、データベースに「ストーリー進行度」とか「シナリオ進行度」みたいな名前のパラメータを1つ作るのも、ひょっとしたら便利かもしれません。
- ストーリー進行度システムのバグ耐性
このストーリー進行度システムの利点として、たとえばストーリーの進行に伴ってパラメーターが連動して上昇(または下降など)など連動するゲームシステムの場合に(たとえばストーリー後半になるほど、主人公のある必殺技のダメージ倍率が高くなるとか)、もしバグが混入してしまって連動システムのバグがおきた場合でも、後述の理由(要点: 連動システムの読み取りするパラメータを限定できるので、作者がフラグ管理しやすくなるのが理由)により「進行度システム」のおかげで修復しやすくなります。
ストーリー進行の具合そのものだけなら、テストプレイで(比較的に)容易に発見できます。しかし、ストーリーの進行具合に応じて、たとえば主人公の「強さ」パラメータが上昇したりとかの連動システムのあるゲームの場合に、もしバグのせいで強さが上昇・連動しない場合があっても、一見するとストーリーが進行してしまっているので、この種の連動イベントをデバッグ系テストプレイでも見落としてしまう場合があります。
このバグ発生時のとき、もし進行度システムがあると、(「強さ」などの)連動パラメ-タは定期的に進行度パラメータを読み取りにいけば済むので、作者はそのゲームのイベント進行を管理しやすくなります。(一方、もし進行度システムが無いと、個々のイベントのフラグを読み取るプログラミングをする必要があるので、もしそのゲームのイベント数が多かったりするとバグ発生率が増える。)
- アクションゲームへの応用(※参考)
アクションゲームなどのジャンプ動作の処理にも、ストーリー進行度のようなアルゴリズムが応用できます。
つまり、いわば「ジャンプ進行度」のようなパラメータを作っておいて、事前に定められた時間の経過によりジャンプ進行度が+1ずつ進むようにプログラムするのです。
たとえば、
- ジャンプ進行度1なら上昇中、
- ジャンプ進行度2でピーク、
- ジャンプ進行度3で下降中、
- ジャンプ進行度4で着地、
- (おわり)
のような仕組みです
こうすることで、バグ防止ができ、たとえばジャンプ下降中にバグで再上昇したりのようなバグを未然防止できます。
ただし、この方式にも欠点があり、たとえばジャンプ中の中割り動作を増やしたくなった場合など、パラメータの再設定が必要です。
長所と欠点を把握しながら、自分の使いやすい方式を選びましょう。
説明の都合でアクションゲームを例にしましたが、しかしRPGゲームを作りたいなら、RPGに専念したほうが良いでしょう。
- ストーリー進行度を作る場合の例
ストーリー進行度の番号は、管理が大変ですが、なるべく+1ずつ順番にイベントを番号づけしていくのを原則とする方式が、いちばん処理速度が良いでしょう。
比較のため別方式を説明しますが、ゲーム開始時はストーリー進行度が0で、物語を進めるイベントを1個クリアすることでストーリー進行度が +10 される、・・・というような仕組みも考えられます。(昔のBASICの行番号でも10間隔で行番号を書いている。)なぜ進行度の数値にこう間隔をあけるとラクかというと、もしイベントを追加して、たとえばイベントA(進行度 10番 に対応)とイベントB(進行度 20番 に対応)のあいだに後からイベントCを追加したくなったとき、たとえばイベントCの進行度を「15」とかしておけば、ラクに数値管理できます。
ただし、このような行番号に間隔があいている方式だと、処理速度が大幅に下がります。実際、BASICがそうです。全部のイベントの行番号に相当する番号を読み取らないといけないので、時間が掛かるからです。
なので、あまり長いイベントはBASICみたいな方式には、しないほうが良いでしょう。
管理の手間は増えますが、+1ずつ番号をつける方式が一番です。
なお、RPGツクールではJSONファイル形式でソースの一部が見られるのですが、それを見ると、武器や防具にはid番号をつける方式です。このようなJSONファイル読み取り形式であるので、原理的にはBASICと同様の理由で、あまり処理速度は見込めないでしょう。ツクールでもウディタでも、ゲームが長大化してくると、ジャンルがRPGでも「処理速度が落ちてくる」という話を、よく聞きます。
よってアクションゲームを開発していくなら、最終的にはC言語による開発が理想になるでしょう。アクションゲームなどを作る場合は、専用の制作ツールを使うか、またはC言語プログラミングするか、あるいは何とかRPG制作ツールで動く機能だけで限定してアクションゲームを作るかなど、工夫する必要があります。
その他
[編集]さて話題は変わりますが、ゲーム業界でのゲームエンジンでのイベント管理については、フリーゲームや同人ゲームにかぎらず、大手ゲーム企業の開発する一般の商業ゲーム(テレビなどで宣伝されるゲーム作品)でも、イベント部分の記述は、エンジン部分を担当するC言語系統のゲームエンジンおよびそのエンジン用言語とは別のスクリプト言語(たとえばpythonやLuaなど)をもちいて(イベント部分のプログラムを)記述するのが普通だとされています[2]。
なお、ゲームエンジンの多くはC言語系統のコーディング環境である。例として UnityはC#系統の環境だし、Unreal EngineはC++系統の環境である。
データベース分離の作業での方針
[編集]自作ゲームがある程度の完成度になるとデータベースを分離する作業に取り掛かることになる。
もし既にセーブ&ロードの機能が自作ゲームで実装してあれば、単に、そのセーブ&ロード機能を流用して、データベースの外部ファイルの入出力機能も作れてしまう。
この設計の場合にラクな手順としては、データベース分離の当初の段階では、けっしてデータベース編集ソフトとかを別系統では作ろうとせずに、まずは自作ゲームそのものにデータベースの入出力機能を組み込むことである。(イメージ的に言うと、ファミコンソフトの『w:ロードランナー』のステージ自作機能みたいに、自作ゲームに組み込むとラクである。)
- まず外部ファイルに出力(エクスポート)
外部データベース入出力では、まず外部ファイル出力の機能のほうから作り始めたほうが、既存のコードが流用できるのでラクであろう。
自作ゲームがある程度はプレイ可能な状態なら、すでにコード内に配列や構造体などでコードがある段階なので、あとは、そのコードの配列変数などの内容の数値を、自分で決めた書式の順番どおりに外部ファイルにCSV形式などお望みの書式(自分で書式を決めていい)で出力すればいい。(CSV形式の読み込み方法のコードの作り方は『C言語/ファイル入出力』で説明してある。)
ついつい、外部ファイル取り込み(インポート)のほうから作りがちであるが(既存のゲーム製作ツールのユーザー視点だと、こうなりがち)、しかし、外部ファイル出力(エクスポート)側から作ったほうがラクであろう。
セーブ&ロードの機能がすでにあるなら、エクスポート機能の製作では、そのセーブ機能のコードを流用すればいい。
- 外部ファイルからの読み込み(インポート)
そして次に外部ファイルを読み込めばいい。インポートでは、さきほどのエクスポートの作業を逆にして、ロード機能のコードを流用すれば、簡単にインポート機能を作れる。
さらにデータベース編集機能を作りたければ、上記のインポート&エクスポート機能に、エクスポートの際の書き換えの指示などを出せば済むだろう。
- ^ Microsoft Docs『データ型の範囲』 2021/08/17 2021年11月20日に閲覧して確認.
- ^ 『企画出身だからできる開発効率の推進〜バンダイナムコスタジオのテック職に聞く異色のクリエイターキャリア論 | インタビュー | CGWORLD.jp』 2020年1月17日に閲覧