■ 新・ゲーム開発講座




■ へっぽこプログラミング入門♪


■第35夜:コンフィグレーション

今回は、いままでテキストエンジンの中でデフォルト値として扱ってきたいくつかの項目を、コマンド上から変更できる可変パラメータに変更します。何を目的にしているかといいますと、多少のコンフィグレーション機能を持たせることで、同一の実行ファイルでも見た目に変化をつけられるようにしよう、ということです。コンフィグレーション(configuration)とは直訳すると外形/輪郭という意味ですが、ソフトウェアの世界では起動時のオプション設定機能を指すことが多いようです。

項目は以下のようなものに致しましょう。・・・内容は大したことありませんけどね(笑)

1.文字の大きさ
2.テキスト表示エリア
3.文字表示ウェイト
4.文字の色

ここで想定しているのは、起動時に最初に読み込まれるデフォルトスクリプトを、たとえば config.txt というファイルにしておいて、ゲーム本来のスクリプトにはこの config.txt から飛んでいく仕様にすることです。つまりスクリプター(ノベルのスクリプトを組む人)に初期化の一部を開放してあげる、ということですね。もちろん、コマンドそのものはスクリプトのどこからでも呼べますから、ゲームの途中でフォントサイズや色を変えることも自由にできます。

■仕様

上記 1〜3のパラメータは、今までのソースではグローバル変数として存在し、いずれも TextEngine.cpp に記述されています。4はローカル変数として存在しています。今回はそれぞれのパラメータに専用の変更コマンドを実装していきます。コマンドといっても、スクリプトからパラメータ値を読み込んでセットするだけなのでウルトラ簡単な部類です(だからまとめて取り扱うのですが ^^;)。書式としては以下のようなもので良いでしょう。


書式:#set_font_size n

フォントサイズをnに変更する。nは縦方向のドット数。

書式:#set_text_area sx,sy,ex,ey

テキスト表示エリアを (sx,sy)-(ex,ey) の矩形領域に設定する。

書式:#set_text_wait n

テキスト表示のウェイト値を nミリ秒に設定する。

書式:#set_text_color r1,g1,b1,r2,g2,b2

テキストカラーを、文字色 RGB(r1,g1,b1)、影色 RGB(r2,g2,b2) に
変更する。

では、実際にインプリメントしてみましょう。いずれも一発終了型のコマンドで、Mainloop で参照しているフラグ構造体 Mode_stat には無関係です。ソースはこちらを参照して下さい。

■フラグ

Mainloopで参照している構造体 Mode_stat のメンバに追加はありません。

■コマンド解析部

TextEngine.cppCommand_call() に、以下の要領でコマンド追加を行います。


void Command_call()
{

//省略

//コマンド名の評価 → 処理関数呼び出し
if( strcmp(com_name,"delay" )==0 ){ Com_delay(); flag=ON;} //遅延
if( strcmp(com_name,"wait" )==0 ){ Com_cursor_blink(); flag=ON;} //カーソルブリンク
if( strcmp(com_name,"w" )==0 ){ Com_cursor_blink(); flag=ON;} //カーソルブリンク
if( strcmp(com_name,"halt" )==0 ){ Com_halt(); flag=ON;} //終了
if( strcmp(com_name,"page" )==0 ){ Com_page(); flag=ON;} //改ページ
if( strcmp(com_name,"g_load" )==0 ){ Com_g_load(); flag=ON;} //BMPのLOAD
if( strcmp(com_name,"g_copy" )==0 ){ Com_g_copy(); flag=ON;} //BMPのCOPY
if( strcmp(com_name,"g_change")==0 ){ Com_g_change(); flag=ON;} //画面切替
if( strcmp(com_name,"flag_on" )==0 ){ Com_flag_on(); flag=ON;} //イベントフラグON
if( strcmp(com_name,"flag_off")==0 ){ Com_flag_off(); flag=ON;} //イベントフラグOFF
if( strcmp(com_name,"disp_flag")==0 ){ Com_disp_flag(); flag=ON;} //イベントフラグ表示
if( strcmp(com_name,"if_flag" )==0 ){ Com_if_flag(); flag=ON;} //フラグによる分岐
if( strcmp(com_name,"jump" )==0 ){ Com_jump(); flag=ON;} //ラベルジャンプ
if( strcmp(com_name,"file_change")==0 ){Com_file_change(); flag=ON;} //ファイル間ジャンプ
if( strcmp(com_name,"select3" )==0 ){ Com_select3(); flag=ON;} //3択
if( strcmp(com_name,"play_wav")==0 ){ Com_play_wav(); flag=ON;} //WAV演奏開始
if( strcmp(com_name,"stop_wav")==0 ){ Com_stop_wav(); flag=ON;} //WAV演奏停止
if( strcmp(com_name,"play_midi")==0 ){ Com_play_midi(); flag=ON;} //MIDI演奏開始
if( strcmp(com_name,"stop_midi")==0 ){ Com_stop_midi(); flag=ON;} //MIDI演奏停止
if( strcmp(com_name,"set_font_size")==0 ){Com_set_font_size(); flag=ON;} //フォントサイズ変更
if( strcmp(com_name,"set_text_area")==0 ){Com_set_text_area(); flag=ON;} //テキストエリア変更
if( strcmp(com_name,"set_text_wait")==0 ){Com_set_text_wait(); flag=ON;} //テキストウェイト変更
if( strcmp(com_name,"set_text_color")==0 ){Com_set_text_color();flag=ON;} //テキストカラー変更

//省略

}

■グローバル変数

Text_engine() 内でローカル変数扱いだったテキストカラーを、グローバル変数にしておきます。とりあえず、これで今回手を加えるパラメータはすべてグローバル変数となりました。(構造体にまとめても良さそうですけど、面倒なのでこのままでいいや・・・笑)文字色パラメータを使用しているのは Text_engine() だけですので、リスト変更もそこだけです。


unsigned char TEXT_BUF[SIZE_OF_TEXT_BUF]; //スクリプトを読み込むエリア

unsigned char *TEXT; //Text pointer(テキスト解釈位置をポイントします)

int Font_Size; //フォントサイズ

int TEXT_WAIT; //1文字表示のウェイト
RECT TEXT_AREA; //テキストを展開する画面領域
int TEXT_X; //現在の表示位置X
int TEXT_Y; //現在の表示位置Y
int TEXT_X_PITCH; //文字送りピッチX
int TEXT_Y_PITCH; //文字送りピッチY
i
nt TEXT_COLOR1; //文字色(表)
int TEXT_COLOR2; //文字色(影)

DWORD TEXT_TIMER; //タイマー

int Text_engine()
{

//------------------------------------------
// ウェイトを取りながらテキストを表示する
//------------------------------------------

//指定時間経過していない場合→Mainloopに戻る
if( HLS_timer_check(TEXT_TIMER,TEXT_WAIT)==false )return 0;

HLS_timer_start(&TEXT_TIMER); //タイマー再スタート

//■1文字を表示する

if( (unsigned char)(*TEXT)<(unsigned char)0x80 ){
//コントロール文字かどうかを判断し、該当しなければ画面に出力
if( Check_control_chr()==false ){
ChrPut3D(Back_DC,TEXT_X ,TEXT_Y ,Font_Size,TEXT_COLOR1,TEXT_COLOR2,(char *)TEXT,1);

Increment_textp_pos(1); //文字表示位置のインクリメント=1byte
}
TEXT++; //テキストポインタを1バイト進める
}else{

//2バイト文字の場合は2バイトずつ出力
ChrPut3D(Back_DC,TEXT_X ,TEXT_Y ,Font_Size,
TEXT_COLOR1,TEXT_COLOR2,(char *)TEXT,2);

Increment_textp_pos(2); //文字表示位置のインクリメント=2byte

TEXT+=2; //テキストポインタを2バイト進める

}

return 0;


}


では、各処理を具体的に実装してみましょう。コマンドが増えてきましたので、新たに Text_Com_04.cpp というファイルに記述していくことにします。

■フォントサイズの変更:実行部

フォントサイズの変更は・・・単にスクリプトの引数を読んでセットしているだけなので解説は不要ですね(笑)。一応、フォントサイズが変わると文字送りピッチも変わるので、一緒に計算させています。フォントサイズ情報は最終的には CreateFont() に渡されて処理されるので、ここでは特にリミッタは記述しません。サイズ 0 の場合は自動的にWindowsのデフォルトフォントサイズが適用され、また巨大なサイズが指定された場合はシステムで使用できる最大のフォントが適用されます(・・・と、資料には書いてあります:爆死 ^^;)


int Com_set_font_size()
{

TEXT++;
Font_Size = kaiseki_10();

TEXT_X_PITCH = Font_Size/2; //文字送りピッチ(X)
TEXT_Y_PITCH = Font_Size*130/100; //文字送りピッチ(Y)

return 0;

}

■テキスト表示エリアの変更:実行部

テキスト表示エリアを変更する #set_text_area コマンドは矩形領域を指定する為に4つの座標パラメータを持っています。表示エリアの変更は、これらのパラメータを順次解析して値の妥当性をチェックし、グローバル変数に代入するだけです。値の妥当性としては、とりあえずエリアの縦サイズはフォントサイズ以上、横サイズはフォントサイズ×4以上(まさか一度に1文字しか表示しないノベルやADVはないだろう・・・とゆー次第で適当に ^^;)に設定しています。値が大きい分には無制限ですが、まさか 10000×10000 ・・・なという指定をする人はいないだろう、と良心的に解釈しておきましょう(笑)。エリアを変更したら、文字位置カウンタを新エリアの右肩位置(=表示スタート位置)に移動します。


int Com_set_text_area()
{

int sx,sy,ex,ey,width,hight;

//パラメータ読み込み
TEXT++;
sx = kaiseki_10();
while( *TEXT==',' || *TEXT==' ' || *TEXT==0x0a )TEXT++;

sy = kaiseki_10();
while( *TEXT==',' || *TEXT==' ' || *TEXT==0x0a )TEXT++;

ex = kaiseki_10();
while( *TEXT==',' || *TEXT==' ' || *TEXT==0x0a )TEXT++;

ey = kaiseki_10();

//数値の妥当性チェック
width = ex - sx;
hight = ey - sy;
if( width < Font_Size*4 ){ msg("幅が狭いよ〜ん","Com_set_text_area()"); return 0; }
if( hight < Font_Size ){ msg("高さが狭いよ〜ん","Com_set_text_area()"); return 0; }

//ローカル変数 → グローバル変数に代入
TEXT_AREA.right = ex;
TEXT_AREA.left = sx;
TEXT_AREA.top = sy;
TEXT_AREA.bottom= ey;

//文字位置カウンタを新エリアの左肩にセットする
TEXT_X=TEXT_AREA.left;
TEXT_Y=TEXT_AREA.top;

return 0;


}


■テキスト表示ウェイトの変更:実行部

これも単純ですね。パラメータを読んで妥当性チェックをして変数に代入です。ウェイトはあまり大きな値を許容してハング状態になっても困りますので、とりあえず5秒(=5000msec)以上の値は受け付けないようにしておきましょう。現実的には1文字200〜300ミリ秒くらいが読者の限界のような気がしますけど…(笑)


int Com_set_text_wait()
{

int text_wait;

//パラメータ読み込み
TEXT++;
text_wait = kaiseki_10();

//妥当性チェック
if( text_wait < 0 ){
msg("マイナスは駄目よ〜ん","Com_set_text_wait()");
return 0;

}
if( text_wait > 5000 ){
msg("5秒以内にしてねん♪","Com_set_text_wait()");
return 0;

}

//グローバル変数に代入
TEXT_WAIT = text_wait;

return 0;


}


■テキストカラー変更:実行部

テキストカラーはRGB各色8bit=1600万色を2セット(文字色、影色)指定可能としますので、パラメータが6個もあります(そんなに色数用意してどーすんねん、という気もしますが ^^;)。とはいえ、パラメータ解釈→妥当性チェック(各チャンネル0〜255の範囲か?)→代入という基本的な流れは同じです。


int Com_set_text_color()
{

int r1,g1,b1,r2,g2,b2;
char err_mess[]="指定可能な数値は各色 0-255 です";

//パラメータ読み込み
TEXT++;
r1 = kaiseki_10();
while( *TEXT==',' || *TEXT==' ' || *TEXT==0x0a )TEXT++;

g1 = kaiseki_10();
while( *TEXT==',' || *TEXT==' ' || *TEXT==0x0a )TEXT++;

b1 = kaiseki_10();
while( *TEXT==',' || *TEXT==' ' || *TEXT==0x0a )TEXT++;

r2 = kaiseki_10();
while( *TEXT==',' || *TEXT==' ' || *TEXT==0x0a )TEXT++;

g2 = kaiseki_10();
while( *TEXT==',' || *TEXT==' ' || *TEXT==0x0a )TEXT++;

b2 = kaiseki_10();

//妥当性チェック
if( r1>255 || r1<0 ){ msg(err_mess,"Com_set_text_color()"); return 0; }
if( g1>255 || g1<0 ){ msg(err_mess,"Com_set_text_color()"); return 0; }
if( b1>255 || b1<0 ){ msg(err_mess,"Com_set_text_color()"); return 0; }
if( r2>255 || r2<0 ){ msg(err_mess,"Com_set_text_color()"); return 0; }
if( g2>255 || g2<0 ){ msg(err_mess,"Com_set_text_color()"); return 0; }
if( b2>255 || b2<0 ){ msg(err_mess,"Com_set_text_color()"); return 0; }

//グローバル変数に代入
TEXT_COLOR1 = RGB(r1,g1,b1);
TEXT_COLOR2 = RGB(r2,g2,b2);

return 0;


}

さて、いかがでしたでしょうか。今回のような「代入系」のコマンドは構造も簡単で練習素材としては最適だと思います。パターンさえ掴んでしまえばいろいろな応用が効きそうですね。↓サンプル画像を下に示しますが、処理は単純でもコンフィグレーション機能があるのと無いのとでは印象が随分違ってくることが分かると思います。そんな訳で、また次回♪(^0^)