今回は、
(3)eventScn.cpp/h イベント(シナリオ)システム本体。
のプログラム本編です。
長くなりますがお付き合いください。
[eventScn.cpp]
●ヘッダのインクルードと内部関数の定義
内部関数の細かい説明は後でします。
内部関数(シナリオ命令)は、シナリオ命令毎の関数を定義します。
CmdFunc_tは、シナリオ命令毎の関数ポインタの型宣言です。
#include
#include "main.h"
#include "comSub.h"
#include "eventScn.h"
#include "winMng.h"
//----------------------------------------------------------------------
// 内部関数
//----------------------------------------------------------------------
// シナリオ関係
static void event_InitScn(); // シナリオを初期化する。
static ScnData_t* event_GetNowScnCmd(); // 今のシナリオ得る。
static void event_NextScn(); // 次のシナリオに進める。
static int event_UpdateScnPointOrFrame(); // シナリオ命令位置更新かフレームのカウント
// ウィンドウ関係
static void event_ActiveWnd(int wndType); // wndTypeで指定されたウィンドウを有効にする。
static void event_DelAllWnd(int bInit); // 全ウィンドウを閉じる。
//----------------------------------------------------------------------
// 内部関数(シナリオ命令)
//----------------------------------------------------------------------
// 関数の型の宣言
typedef int (*CmdFunc_t)(ScnData_t *scnData);
// 各シナリオ命令関数
static int scn_Exit(ScnData_t *scnData);
static int scn_Message(ScnData_t *scnData);
static int scn_PlayerEvtPos(ScnData_t *scnData);
●s_scnCmdFunc
s_scnCmdFuncは先ほど定義したCmdFunc_tを使ったシナリオ命令の関数ポインタのテーブルです。
enumで定義された順番にシナリオ命令が並んでいます。
●MES_SPEED
メッセージの速度(一文字の表示フレームです)
●s_scnWndData
ウィンドウの位置とサイズを定義したテーブルでSCN_WND_???の番号と対応しています。
構造体宣言と変数の宣言と値の初期値設定を同時に書いています。
//----------------------------------------------------------------------
// 定数
//----------------------------------------------------------------------
// イベント命令を実行する関数ポインタ。シナリオ命令のenum並びと同一にすること。
const CmdFunc_t s_scnCmdFunc[SCN_CMD_MAX] = {
NULL, //SCN_END シナリオの終端を示すコマンドで必須です。SCN_GOTOのストッパでもあります。
scn_Message, //SCN_MES メッセージの出力
NULL, //SCN_PLY_POS 自キャラの位置を変えます。
scn_PlayerEvtPos, //SCN_PLY_EPOS 自キャラの位置をイベント番号の位置にします。
NULL, //SCN_PLY_MUKI 自キャラの方向を変えます。
NULL, //SCN_MAP_CHG マップを変更します。
NULL, //SCN_CHR_MUKI マップ固有キャラの方向を変えます。
NULL, //SCN_CHR_ANM マップ固有キャラのアニメ有無を設定します。
NULL, //SCN_SHOP, お店に入るor宿に泊まります(セーブします)。
NULL, //SCN_BOSS_BTL ボスとのバトル。
NULL, //SCN_ENDING エンディングに入ります。
scn_Exit, //SCN_EXIT イベントを抜けます。
NULL, //SCN_LABEL ラベル名を定義します。
NULL, //SCN_GOTO ラベルにジャンプします。
NULL, //SCN_IF_SKIP 変数と一致する文字列なら次のイベント命令をスキップします。
NULL, //SCN_SET_VAR 変数に文字列を設定します。
NULL, //SCN_ADD_VAR 変数の文字列を数値変換して加算します。
NULL, //SCN_IF_TBOX 宝箱が空いていなかったら次の命令をスキップします。
NULL, //SCN_SET_TBOX 宝箱を開けたことを覚えます。
NULL, //SCN_GET_ITEM アイテムを袋に入れます。もし袋がいっぱいなら次の命令をスキップします。
};
// メッセージ速度
#define MES_SPEED 5
// ウィンドウのタイプ別のウィンドウデータ
#define WMD_POSX (40/2)
#define WMDU_POSY 10
#define WMDD_POSY (380)
const struct {
int px; //ウィンドウ座標X
int py; //ウィンドウ座標Y
int sizex; //ウィンドウサイズX
int sizey; //ウィンドウサイズY
} s_scnWndData[SCN_WND_MAX] = {
{WMD_POSX,WMDU_POSY,SCREEN_X-(WMD_POSX*2),90}, //SCN_WND_MSGU セリフ用ウィンドウ上段
{WMD_POSX,WMDD_POSY,SCREEN_X-(WMD_POSX*2),90}, //SCN_WND_MSGD セリフ用ウィンドウ下段
};
●EventControl_tは、イベント(シナリオ)制御のための構造体です。
EventDataTableは、今のマップのイベント情報のポインタです。マップが切り替わるとこの情報ポインタを切り替わります。
mapMoveInfoは、マップ移動に関連した情報のポインタです。
mapNameは、決め打ちで511バイトの文字まで受け付けます。
eventTypeとeventNoは、これから起こるイベントの情報です。この値を使って発生イベントを決めます。
cur_Scnは、今実行中のシナリオの先頭ポインタでeventTypeとeventNoでEventDataTableから得られます。
scnPointは、今現在の実行中シナリオの位置を示すカウンタでbNextPointがTRUEなら次の命令に進みますが、FALSEなら同じ命令を実行し続けます(メッセージなど)。
scnCmdFrameは、今のシナリオ命令を開始してからの経過フレーム数です。
winnoは、シナリオで管理するウィンドウの情報をウィンドウの種類だけ保持します。
//----------------------------------------------------------------------
// 構造体
//----------------------------------------------------------------------
// イベント(シナリオ)制御構造体
typedef struct {
// 制御に必要なデータ
struct tag_EventData *EventDataTable; // 今有効なイベント情報ポインタ
MapMoveInfo_t *mapMoveInfo; // マップ移動に関連したデータ
// 「ロードする」あるいは「ロード済み」のマップ名。
char mapName[512];
// イベント初期化時に利用される。
int eventType; // 発生イベントの種別
int eventNo; // 発生イベントの番号(EVENT_TYPE_MAP_CHANGEだけは出現先のイベント番号)
// 実行中のシナリオの情報
ScnData_t *cur_Scn; // 実行中のシナリオ・ポインタ
int scnPoint; // 今現在のシナリオ位置
int bNextPoint; // 次の命令に進むフラグ
int scnCmdFrame; // シナリオ命令毎のフレーム
// 実行中シナリオの保持情報
int winno[SCN_WND_MAX]; // シナリオで管理するウィンドウの情報
} EventControl_t;
g_scnVarは、シナリオ変数の値を保持する配列です。
g_tboxStateは、宝箱の状態を保持する配列です。
EventControlDataは、EventControl_tの実体です。
//----------------------------------------------------------------------
// 変数
//----------------------------------------------------------------------
// シナリオ変数構造体
char g_scnVar[SCNVAR_MAX][SCNVAR_LENGTH_MAX];
// 宝箱状態変数
char g_tboxState[TBOX_MAX];
// イベント制御の変数
static EventControl_t EventControlData;
イベントシステムを初期化します。
重要なのは、シナリオ・宝箱変数の初期化と各変数初期化です。
基本的にロードするマップはシナリオで管理するので、最初にロードするマップの名前をここで設定します。
//----------------------------------------------------------------------
// イベントシステムの初期化。
//----------------------------------------------------------------------
void event_Init(char *startMapName)
{
// 制御変数など初期化します。
EventControlData.EventDataTable = NULL; //今有効なイベント情報ポインタ
MACRO_ASSERT( strlen(startMapName)scnCmd];
if( scnCmdFunc!=NULL ) {
rtnCode = (scnCmdFunc)(scnData);
} else {
printfDx( "未定義命令(%d)", scnData->scnCmd );
}
}
// シナリオ命令位置更新かフレームのカウント。シナリオ命令位置更新ならTRUEが返る。
bNextCmd = event_UpdateScnPointOrFrame();
// シナリオ命令の戻り値がイベントを継続でなければ
if( rtnCode != EVENT_RTN_CONTINE ) {
// 全ウィンドウを破棄
event_DelAllWnd(FALSE);
// 直ちにイベント処理を抜ける。
break;
}
};
// 遷移状態を持ち帰る。
return rtnCode;
/*
return EVENT_RTN_SHOP; //SHOPへ遷移。
return EVENT_RTN_ENDING; //エンディングへ遷移。
return EVENT_RTN_MAPCHANGE; //マップチェンジに遷移。
return EVENT_RTN_BATTLE; //バトルに遷移。
*/
}
イベント関連の描画を行います。
今のところウィンドウしか描画されません。
//----------------------------------------------------------------------
// イベントを描画する。
//----------------------------------------------------------------------
void event_Draw(int frame)
{
// 有効なウィンドウの描画
winmng_Draw();
}
(シナリオ命令)SCN_EXITで、シナリオを抜けてマップ移動の処理に遷移します。
returnでEVENT_RTN_CONTINE以外を返すと状態遷移が起こります。
//######################################################################
// (シナリオ命令)SCN_EXIT イベントを抜けます。
//######################################################################
static int scn_Exit(ScnData_t *scnData)
{
// マップ移動へ遷移。
return EVENT_RTN_MAPMOVE;
(シナリオ命令)SCN_MESでメッセージ出力を行います。
命令パラメータは、str[メッセージ]、p1[一文字づつの出力有無]、p2[使用するウィンドウのタイプ]です。
最初の命令フレームで、p2「使用するウィンドウのタイプ」で指定されたウィンドウを有効にして、そのウィンドウにstr「メッセージ」のメッセージ出力を設定します。
メッセージ速度は、MES_SPEEDの固定で、p1「一文字づつの出力有無」がなしの場合は一気にメッセージを出力します。
winmng_UpdateMsg()でメッセージの表示位置を更新して、TRUEならメッセージの待機中に決定ボタンを押されたのでシナリオを次に進めます。それ以外の場合は同じシナリオ命令を続けることになります。
ここがシナリオの特殊な制御のキモなのですが、event_Main()でシナリオが次の命令に進まない場合はシナリオループを抜けると説明していますよね。メッセージ中はメッセージのシナリオ命令が継続している必要があり、かつ画面の更新もしなくてはいけないのでシナリオループを抜けてメインループに戻る必要があるのでこうなっています。
returnはEVENT_RTN_CONTINEでシナリオの継続です。
//######################################################################
// (シナリオ命令)SCN_MES メッセージの出力
// パラメータ:str[メッセージ]、p1[一文字づつの出力有無]、p2[使用するウィンドウのタイプ]
//######################################################################
static int scn_Message(ScnData_t *scnData)
{
// 命令の最初のフレーム?
if( EventControlData.scnCmdFrame == 0 ) {
// ウィンドウを有効にする。
event_ActiveWnd(scnData->p2);
// メッセージを設定する。待機カーソルあり。
winmng_SetMsg(scnData->str,TRUE);
}
// メッセージ速度
int msg_sp = MES_SPEED; //ウェイトあり。
if( scnData->p1 == FALSE ) {
msg_sp = 0; //ウェイトなし。
}
// 毎フレームの更新
if( winmng_UpdateMsg(msg_sp) ) {
// シナリオを次に進める。
event_NextScn();
}
// イベントを継続する。
return EVENT_RTN_CONTINE;
}
(シナリオ命令)SCN_PLY_EPOSでプレイヤーのキャラの位置を変更します。
命令パラメータは、p1[イベント番号]です。
map_GetEventPosition()関数でp1[イベント番号]で指定されたイベントの座標をpx,pyで返してもらいます。
その座標をmapMoveInfoのプレイヤーキャラの座標に代入しています。
event_NextScn()でシナリオ命令を次に進める予約をします。
returnはEVENT_RTN_CONTINEでシナリオの継続です。
//######################################################################
// (シナリオ命令)SCN_PLY_EPOS 自キャラの位置をイベント番号の位置にします。
// パラメータ:p1[イベント番号]
//######################################################################
static int scn_PlayerEvtPos(ScnData_t *scnData)
{
int px,py;
// イベント座標の取得
if( map_GetEventPosition(scnData->p1,&px,&py) ) {
// 自キャラの位置をイベント番号の位置にします。
EventControlData.mapMoveInfo->player_px = px;
EventControlData.mapMoveInfo->player_py = py;
}
// シナリオを次に進める。
event_NextScn();
// イベントを継続する。
return EVENT_RTN_CONTINE;
}
これからシナリオを動かすための初期化をします。
まず全ウィンドウを閉じて、シナリオの先頭ポインタを一度クリアします。
イベント情報ポインタが有効なら、イベントの種類ごとに違う配列からシナリオの先頭ポインタを得ます。
システムイベントはタイプ別で、地形とキャラクタはイベント番号別です。
最後にシナリオの進行に関するパラメータを初期化して終了です。
//----------------------------------------------------------------------
// シナリオを初期化する。
//----------------------------------------------------------------------
static void event_InitScn()
{
// 念のため全ウィンドウを閉じる。
event_DelAllWnd(FALSE);
// シナリオの情報を初期化する。
EventControlData.cur_Scn = NULL; // 実行中のシナリオ・ポインタ
// 今有効なイベント情報ポインタ
if( EventControlData.EventDataTable != NULL ) {
// システム・イベント?
if( EventControlData.eventType eventSystemScn[EventControlData.eventType];
} else {
// イベント種類別の処理
switch( EventControlData.eventType ) {
case EVENT_TYPE_MAP: // 地形のイベント
// 地形イベントのシナリオ
EventControlData.cur_Scn = EventControlData.EventDataTable->mapScn[EventControlData.eventNo];
break;
case EVENT_TYPE_CHAR: // キャラクタのイベント
// キャラクタ・イベントのシナリオ
EventControlData.cur_Scn = EventControlData.EventDataTable->charScn[EventControlData.eventNo];
break;
}
}
}
// 再生シナリオの位置を初期化
EventControlData.scnPoint = 0; // 今現在のシナリオ位置
EventControlData.bNextPoint=FALSE;// 次の命令に進むフラグ
EventControlData.scnCmdFrame = 0; // シナリオ命令毎のフレーム
}
今シナリオのポイント(EventControlData.scnPoint)が指しているシナリオ命令のポインタを持ち帰ります。
シナリオが無効ならNULLを返します。
//----------------------------------------------------------------------
// 今のシナリオ得る。
//----------------------------------------------------------------------
static ScnData_t* event_GetNowScnCmd()
{
// 現在のシナリオは無効?
if( EventControlData.cur_Scn==NULL ) {
// 無効なのでシナリオ命令も無効
return NULL;
} else {
// 今のシナリオを持ち帰る。
return &EventControlData.cur_Scn[EventControlData.scnPoint];
}
}
シナリオが有効なら、シナリオを次に進めるフラグを立てます。
ただし、今指しているシナリオがSCN_EXITの場合は次には進みません。
//----------------------------------------------------------------------
// 次のシナリオに進める。
//----------------------------------------------------------------------
static void event_NextScn()
{
// 現在のシナリオは有効?
if( EventControlData.cur_Scn!=NULL ) {
// シナリオ終端以外なら
if( EventControlData.cur_Scn[EventControlData.scnPoint].scnCmd != SCN_EXIT ) {
// 次のシナリオ命令に進むフラグを立てる。
EventControlData.bNextPoint=TRUE;
}
}
}
bNextPointがTRUEならシナリオ命令を次の命令に進めてシナリオ命令毎のフレームカウンタをクリアします。
FALSEなら、シナリオ命令毎のフレームカウンタをカウントします。
戻り値でbNextPointがTRUEだったかFALSEだったかを返します。→シナリオループの制御に使われます。
//----------------------------------------------------------------------
// シナリオ命令位置更新かフレームのカウント。どちらで動作したかを戻り値で返す。
//----------------------------------------------------------------------
static int event_UpdateScnPointOrFrame()
{
// 次のシナリオ命令に進むフラグ?
if( EventControlData.bNextPoint ) {
// フラグをクリア
EventControlData.bNextPoint=FALSE;// 次の命令に進むフラグ
// 次のシナリオに進める。シナリオ命令毎のフレームは初期化。
EventControlData.scnPoint++; // 今現在のシナリオ位置
EventControlData.scnCmdFrame = 0; // シナリオ命令毎のフレーム
// シナリオ命令位置更新。
return TRUE;
} else {
// シナリオ命令毎のフレームをカウントする。
EventControlData.scnCmdFrame++;
// フレームのカウント。
return FALSE;
}
}
指定されたウィンドウが無効なら新しくウィンドウを作成します。ウィンドウの大きさなどはs_scnWndDataテーブルから得ます。
すでにウィンドウが有効なら、そのウィンドウをアクティブにします。
//----------------------------------------------------------------------
// wndTypeで指定されたウィンドウを有効にする。
//----------------------------------------------------------------------
static void event_ActiveWnd(int wndType)
{
MACRO_ASSERT( wndType < SCN_WND_MAX );
// まだ無効なウィンドウなら
if( EventControlData.winno[wndType] == -1 ) {
// 新しいウィンドウを作成する。
EventControlData.winno[wndType] = winmng_NewWin(
s_scnWndData[wndType].px,
s_scnWndData[wndType].py,
s_scnWndData[wndType].sizex,
s_scnWndData[wndType].sizey,
NULL
);
} else {
// アクティブ・ウィンドウを切り替える。
winmng_SetActiveWin( EventControlData.winno[wndType] );
}
}
全てのウィンドウを破棄して、有効なウィンドウ番号を無効にします。
//----------------------------------------------------------------------
// 全ウィンドウを閉じる。
//----------------------------------------------------------------------
static void event_DelAllWnd(int bInit)
{
// 初期化以外なら
if( !bInit ) {
// 全ウィンドウを破棄
winmng_DelAllWin();
}
// ウィンドウの番号の記録を無効に。
for( int wn=0 ; wn<SCN_WND_MAX ; wn++ ) {
EventControlData.winno[wn]=-1;
}
}
次回は、町イベントの記述に付いて説明します。