用語の統一のため次のように定義させていただきます。
[イベント] ゲーム内で何かの動作・操作がシナリオを動作させること。あるいは、そのシステム。
[シナリオ] イベント毎に記述されたゲームの動作。キャラやマップに変化を与えたり、プレイヤーにストーリー情報を提供する。
RPGでイベント・シナリオは重要な要素ですよね。
この講座では、なるべく分かりやすく拡張性のあるイベント(シナリオ)システムを構築していきます。
イベントを記述するシナリオをスクリプトファイルにすることも考えたのですが、プログラミング言語講座みたいになるので今回は避けます。
なので、シンプルに龍神録の敵のテーブルの様な表形式にしたいと思いますが、詳しくは後で説明させてください。
後々スクリプトファイル化して読み込む形にも変更は容易です。そこについては拡張編で。
[table=border:1px;background:#FFFFFF;padding:2px 12px 2px 12px;solid #cccccc;][tr=text-align:center;]
[td=border:1px;solid #cccccc;]
●イベントの種類と発動
[/td]
[/tr][/table]
では、まず基本的な考え方を。
私の考えているゲーム中に発生するイベントは次の様なものです。
[table=border:2px solid #cccccc;][tr=text-align:left;]
[td=border:1px solid #cccccc;]
(1)マップチェンジの発生。 マップの特定座標を踏むと別のマップに移動する。
(2)マップチェンジの受付。 別のマップから移動してきたときに発生する初期イベント。
(3)町人との会話。 町人に向かって決定ボタンを押すと話しかけることが出来る。
(4)宝箱を開ける。 宝箱に向かって決定ボタンを押すと宝箱を調べることが出来る。
(5)初期スタート時イベント。 ゲームを開始した時だけ発生する初期イベント。
(6)ゲームロード時のイベント ゲームをロードした時だけ発生する初期イベント。
[/td]
[/tr][/table]
これらが私が今のところ考えているイベントです。
具体的イメージが分かり辛いとおもいますので、図を交えて説明します(協力:けろへい)。
(5)と(6)に関しては、あとで詳しく説明しますのでここでは省略します。
マップチェンジ
[table=border:1px solid #cccccc;][tr=text-align:left;]
[td=border:1px solid #cccccc;] [/td]
[td=border:1px solid #cccccc;]
マップの特定座標を踏んだときにマップチェンジのイベントが発動します。
絵を見てもらうと分かりますが、マップの出口でイベントを踏んだら、
マップチェンジが起こってフィールドの町のイベントから出現すると
いった感じです。
【予定されるプログラムの動作】
移動処理で、移動先にイベントが無いかチェックします。
マップにイベントを置くには、マップデータのイベントレイヤーを利用します。
イベントが起こってほしい場所にイベントのマップチップを敷き詰めて、
移動時にそのマップチップを踏んだらマップイベントが発動するわけです。
あとは、イベントで呼び出されたシナリオがマップチェンジを行います。
移動処理(町)→マップイベント→マップチェンジの発生シナリオ→マップロード処理→
→マップチェンジイベント受付→マップチェンジの受付シナリオ→移動処理(フィールド)
イベントを踏んだかの判定は、マップの移動可否の判定と同様の仕組みなので
実装は難しくはありません。
[/td]
[/tr][/table]
人と話す・宝箱を開ける
[table=border:1px solid #cccccc;][tr=text-align:left;]
[td=border:1px solid #cccccc;] [/td]
[td=border:1px solid #cccccc;]
絵に有るとおり、町人や宝箱の前に立って決定ボタンを押すと一歩前にある
町人や宝箱とのイベントが起きます。
そうすることで、会話したり宝箱を開けることが出来ます。
【予定されるプログラムの動作】
移動処理で決定ボタンを押すと前方に人や宝箱があればイベントが発生します。
イベントは、シナリオを呼び出して会話や宝箱の処理を行います。
移動処理→町人イベント→会話シナリオ→移動処理
移動処理→宝イベント→宝箱シナリオ→移動処理
人と宝箱は、実はほとんと同じ処理です。
(宝箱を町人キャラとして作れば良いだけですので)。
[/td]
[/tr][/table]
[table=border:1px;background:#FFFFFF;padding:2px 12px 2px 12px;solid #cccccc;][tr=text-align:center;]
[td=border:1px;solid #cccccc;]
●イベントとマップ
[/td]
[/tr][/table]
これらイベントはマップとほぼ結び付いてますので、イベントやシナリオはマップと一対一の組みとして作っていく事とします。
※ これは私の作り方で、こうしなければダメだと言う事はありません。作りやすければ自分流に変えてもかまいませんよ。
要するにマップTOWN用のイベントで結びついたシナリオTOWNがあり、マップFIELD用のシナリオFIELDがあるって感じです。 イベントは、(5)初期スタート時イベントと(6)ゲームロード時のイベントを除いて移動処理で発生しますので、移動処理にイベントを起動するための処理を組み込むつもりです。
具体的には、移動処理の状態のときに発生したイベント情報をイベント処理モジュールに送っておいて、その後イベント状態に遷移するって制御する処理になる予定です。
初期スタートやロードのイベントに関しては、必要な状況の時に説明します。
[table=border:1px;background:#FFFFFF;padding:2px 12px 2px 12px;solid #cccccc;][tr=text-align:center;]
[td=border:1px;solid #cccccc;]
●シナリオ変数
[/td]
[/tr][/table]
さて、色々説明しましたが基本的なイベント・シナリオの仕組みで、まだ説明していないものがあります。
よく、シナリオでフラグを立てるって言うけどどうやるの?って事がシナリオのシステムを組んだことがない場合に未知数だと思います。
これは、シナリオ間でシナリオ変数と言うプログラミング言語の変数に似たもので情報を受け渡ししてやるのです。
シナリオ変数はマップ間・シナリオ間でも受け渡し出来る様になっていて、C言語のグローバル変数みたいな物と思ってください。
今回の簡単RPGでは、配列として定義して番号で管理する事にします。
シナリオ変数のイメージとしての例です。
┌──┬─────────┬─────────────┐
│番号│名前 │状態 │
├──┼─────────┼─────────────┤
│[0] │王様と会ったフラグ│0なら会っていない。 │
│ │ │1なら会っている。 │
├──┼─────────┼─────────────┤
│[1] │姫と会った回数 │0なら嫌われる。 │
│ │ │10以上なら姫が会いに来る。│
└──┴─────────┴─────────────┘
こんな感じでしょうか?
シナリオ変数は、配列の形のままセーブ対象のデータとなります。
[table=border:1px;background:#FFFFFF;padding:2px 12px 2px 12px;solid #cccccc;][tr=text-align:center;]
[td=border:1px;solid #cccccc;]
●イベント(シナリオ)命令
[/td]
[/tr][/table]
最後に、今のところ予想されるイベント(シナリオ)命令とその機能の予想一覧です。
EVENT_MESだけは特別で、メッセージ出力中はProcessMessage()のループに戻ります。
それ以外は、遷移する命令を除いてイベント処理から抜けません。
まだまだ、未完成なのでこれから増えていく予定です。
┌───────┬────────┬──────────┬────────┬───────────────────────┐
│コマンド名 │パラメータ文字列│パラメータ数値1 │パラメータ数値2 │機能 │
├───────┼────────┼──────────┼────────┼───────────────────────┤
│EVENT_MES │メッセージ │一文字づつの出力有無│ │メッセージの出力。 │
├───────┼────────┼──────────┼────────┼───────────────────────┤
│EVENT_PLY_POS │NULL │x │y │自キャラの位置を変えます。 │
├───────┼────────┼──────────┼────────┼───────────────────────┤
│EVENT_PLY_EPOS│NULL │イベント番号 │ │自キャラの位置をイベント番号の位置にします。 │
├───────┼────────┼──────────┼────────┼───────────────────────┤
│EVENT_PLY_MUKI│NULL │方向 │ │自キャラの方向を変えます。 │
├───────┼────────┼──────────┼────────┼───────────────────────┤
│EVENT_MAP_CHG │マップ名 │EVENT番号 │ │マップを変更します。 │
│ │ │ │ │EVENT番号は出現ポイントのイベント番号。 │
│ │ │ │ │マップチェンジに遷移。 │
├───────┼────────┼──────────┼────────┼───────────────────────┤
│EVENT_CHR_MUKI│NULL │キャラ番号 │方向 │マップ固有キャラの方向を変えます。 │
├───────┼────────┼──────────┼────────┼───────────────────────┤
│EVENT_CHR_ANM │NULL │キャラ番号 │アニメ有無 │マップ固有キャラのアニメ有無を設定します。 │
├───────┼────────┼──────────┼────────┼───────────────────────┤
│EVENT_SHOP │NULL │SHOP番号 │ │お店に入る │
│ │ │ │ │ or │
│ │ │ │ │宿に泊まります(セーブします)。 │
│ │ │ │ │SHOPへ遷移。 │
├───────┼────────┼──────────┼────────┼───────────────────────┤
│EVENT_BOSS_BTL│NULL │敵番号 │ │ボスとのバトル。 │
│ │ │ │ │バトルに遷移。 │
├───────┼────────┼──────────┼────────┼───────────────────────┤
│EVENT_ENDING │NULL │ │ │エンディングに入ります。 │
│ │ │ │ │エンディングへ遷移。 │
├───────┼────────┼──────────┼────────┼───────────────────────┤
│EVENT_EXIT │NULL │ │ │イベントを抜けます。 │
│ │ │ │ │マップ移動へ遷移。 │
├───────┼────────┼──────────┼────────┼───────────────────────┤
│EVENT_LABEL │ラベル名 │ │ │ラベル名を定義します。 │
├───────┼────────┼──────────┼────────┼───────────────────────┤
│EVENT_GOTO │ラベル名 │ │ │ラベル名にジャンプします。 │
├───────┼────────┼──────────┼────────┼───────────────────────┤
│EVENT_IF_SKIP │文字列 │シナリオ変数番号 │IFの条件 │変数と条件一致する文字列なら │
│ │ │ │==,!=などの │次のイベント命令をスキップします。 │
│ │ │ │条件フラグ │ │
├───────┼────────┼──────────┼────────┼───────────────────────┤
│EVENT_SET_VAR │文字列 │シナリオ変数番号 │ │変数に文字列を設定します。 │
├───────┼────────┼──────────┼────────┼───────────────────────┤
│EVENT_ADD_VAR │NULL │シナリオ変数番号 │加算値 │変数の文字列を数値変換して加算します。 │
├───────┼────────┼──────────┼────────┼───────────────────────┤
│EVENT_IF_TBOX │NULL │宝箱の番号 │ │宝箱が空いていなかったら │
│ │ │ │ │次の命令をスキップします。 │
├───────┼────────┼──────────┼────────┼───────────────────────┤
│EVENT_SET_TBOX│NULL │宝箱の番号 │ │宝箱を開けたことを覚えます。 │
├───────┼────────┼──────────┼────────┼───────────────────────┤
│EVENT_GET_ITEM│NULL │アイテム番号 │ │アイテムを袋に入れます。 │
│ │ │ │ │もし袋がいっぱいなら次の命令をスキップします。│
└───────┴────────┴──────────┴────────┴───────────────────────┘
EVENT_IF_SKIPとEVENT_IF_TBOXの動作は特殊なので説明します。
イベント命令はブロック構造を持たないので、IF文で複数命令を記述する方法が無かったためアセンブラ的アプローチを取りました。
参考例を書きます。
{ EVENT_IF_SKIP,"YES",EVENT_VAR_BOSS,EVENT_COND_EQU }, //イベント変数(EVENT_VAR_BOSS)が"YES"だったら次の命令をスキップ
{ EVENT_GOTO,"BOSS_SKIP" }, //ボス・イベントをスキップする
// ここからはボスイベント。EVENT_IF_SKIPが成立するとEVENT_GOTOをスキップして下の命令を実行する。
{ EVENT_MSG,"よく来たな小僧!",1 }, //ボスのせりふ
{ EVENT_MSG,"お前など返り討ちにしてくれる!\nこい小僧!",1 }, //ボスのせりふ
{ EVENT_BOSS_BTL,0 }, //ボスとバトル開始
{ EVENT_MSG,"こっ、このわしを倒すとは!!",1 }, //ボスのせりふ
{ EVENT_MSG,"ぐぉぉぉぉぉぉぉぉぉぉ!!!",1 }, //ボスのせりふ
{ EVENT_ENDING }, //すげーあっさりエンディング
{ EVENT_LABEL,"BOSS_SKIP" }, //EVENT_GOTO命令で飛んでくるラベル
{ EVENT_EXIT }, //イベント終了
以上で今回の設計編1を終わります。
次回は、まずマップとイベントを接続する部分など色々な構造体を設計しましょう。