■ 新・ゲーム開発講座




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


■第49夜:SAVEポイントの自動生成

さてウォーミングアップから始まって、SAVEだけでもう5夜目です。
ずいぶんボリュームがあって書いているほうも大変なのですが、自前のノベルツールで 「きちんとSAVEができる」 ということはちょっとしたレベルなので、「脱・初心者」の称号を目指して頑張りましょう(^^)

■SAVEポイントの自動生成

ここでは、いちいち手動でスクリプトに #save_point#sp を書かなくても、フラグさえ立てておけばシステムが勝手にSAVEデータのパッケージングを行ってくれる仕組みを作ります。「そんなこと出来るのかーっ?!」と思う方もいるかも知れませんが、データをパッケージする関数はすでに作ってしまいましたから、あとはどんなタイミングでそれを呼び出すかという話です。ルールさえ決めてしまえばどうということはありません。

ここでは、そのルールを「改ぺージの直後」とします。具体的には改ページ処理の

Text_Com_01.cpp int Com_page() 改ページ(明示処理)
Text_Com_01.cpp int Com_page2_end() 改ページ(ページ端)


と、実質的に改ページに相当する画面切り替え処理の

Text_Com_02.cpp int sub_Com_g_change_task_1() カーテン
Text_Com_02.cpp int sub_Com_g_change_task_2() 放射
Text_Com_02.cpp int sub_Com_g_change_task_3() 伸縮X
Text_Com_02.cpp int sub_Com_g_change_task_4() 伸縮Y


の終端部分 (画面が切り替わった後) とします。ここでSAVEポイント処理の自動/手動を表すフラグを見て、自動に設定されていたらSAVEデータのパッケージを行う make_save_info() を呼びます。手動モードが設定されていたら、なにもしません。

フラグの設定はスクリプト上から自由にできるようにコマンドを用意します。書式は以下のようなもので良いでしょう。

■書式







#save_point_mode flag

flag : AUTO 自動 / MANUAL 手動

セーブポイントを自動生成するかどうかを設定する。
AUTO を設定した場合、無条件に改ページ直後をセーブ
ポイントとして扱う。
MANUAL を設定した場合、#save_point または #sp コマ
ンドで明示的に示された箇所のみセーブポイントとして扱う。


ついでに、スクリプトのDEBUG用に、現在の設定を表示するおまけコマンドもつけておきましょう。(こういう一見地味な補助コマンドが、実際に長いテキストを書く際に意外に役に立ったりするものなのです ・ω・)

■書式


#disp_save_point_mode

現在のSAVEポイントモードが AUTO か MANUAL かを
表示する



■コマンド解釈部

いつものように、コマンド名と処理関数を追加します。



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,"play_mp3")==0 ){ Com_play_mp3(); flag=ON;} //MP3演奏開始
if( strcmp(com_name,"stop_mp3")==0 ){ Com_stop_mp3(); flag=ON;} //MP3演奏停止
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;} //テキストカラー変更
if( strcmp(com_name,"set_str")==0 ){ Com_set_str(); flag=ON;} //文字列変数に値をセット
if( strcmp(com_name,"text_input")==0 ){ Com_text_input(); flag=ON;} //ダイアログを開いてテキスト入力
if( strcmp(com_name,"save_point")==0 ){ Com_save_point(); flag=ON;} //セーブポイント設定
if( strcmp(com_name,"sp")==0 ) { Com_save_point(); flag=ON;} //セーブポイント設定(短縮表記)
if( strcmp(com_name,"save_point_mode")==0 ){Com_save_point_mode(); flag=ON;}//セーブポイントモード
if( strcmp(com_name,"disp_save_point_mode")==0 ){Com_disp_save_point_mode();flag=ON;} //セーブポイントDEBUG用

// ↑
//そのうちここに他のコマンドも追加していきましょう
// ↓

//フラグを見て、切り出されたコマンド名が有効なコマンドだったかどうか確認する
if( flag == OFF ) {

char str[256];

sprintf( str,"警告:無効なコマンド [#%s] が記述されています",com_name);
MessageBox(NULL,str,"Command_call()",MB_OK);

}

}


■コマンド実行部

タスク動作をするわけでもなく一発完了処理なので、内容は単純です。SAVEポイントモードを設定する Com_save_point_mode() は、スクリプトの引数を評価してフラグの値を設定するだけです。 DEBUG用コマンドの Com_disp_save_point_mode() はメッセージボックスを開いて現在の設定を表示します。



#define _AUTO_ 0
#define _MANUAL_ 1

int _save_point_mode; //セーブポイント生成モード _AUTO_ or _MANUAL_

int Com_save_point_mode()
{

char str[256];

//パラメータ解析
TEXT++;
strcpy( str, Kaiseki_TextStr() );

if( strcmp( str,"AUTO" )==0 ){
_save_point_mode = _AUTO_;
}
else
if( strcmp( str,"MANUAL" )==0 ){
_save_point_mode = _MANUAL_;
}
else
{
msg("セーブポイントモードの指定が不正です。\n"
"暫定的に AUTO として扱います。","Com_save_point_mode()");
_save_point_mode = _AUTO_;
}
return 0;

}

int Com_disp_save_point_mode()
{

char str[256];

sprintf(str,"セーブポイントモードは無効な値です");
if( _save_point_mode == _AUTO_ ) sprintf(str,"セーブポイントモードは AUTO です");
if( _save_point_mode == _MANUAL_ ) sprintf(str,"セーブポイントモードは MANUAL です");

MessageBox(NULL,str,"Com_disp_save_point_mode()",MB_OK);

return 0;

}



■「改ページ」に対する処理の追加

フラグの設定部分ができたので、次はフラグを参照してSAVE構造体のパッケージ関数を呼ぶ部分です。改ページに相当する処理の最後の部分に

if( _save_point_mode == _AUTO_ )make_save_info();


という記述を追加します。非常に単純なのでこれ以上解説のしようがありませんね(^^;)。
記述箇所は以下のリストの通りです。ずらりと並べると結構な分量がありそうですが、ここで重要なのは
「実質改ページ処理」 の最後に漏れなくチェック部分を書いておくということなので、ソースの見かけの分量でビビらないでください(^0^)。



Text_Com_01.cpp

/------------------------------------------------------------
// 改ページ@ スクリプトコマンドとしての改ページ
//
// 書式:#page
//
// 画面を背景画像で上書きしてテキストを消し、文字表示位置を
// リセットする。
//------------------------------------------------------------

int Com_page()
{

//背景データでバックサーフェイスを書き換える
BitBlt(Back_DC,0,0,640,480,BG_DC,0,0,SRCCOPY);
strcpy(BK_bmp_fname,BG_bmp_fname) ; //ファイル名をBG面のものでキャッシュ

//文字表示位置をテキストエリアの左肩にリセットする
TEXT_X = TEXT_AREA.left;
TEXT_Y = TEXT_AREA.top;

if( _save_point_mode == _AUTO_ ) make_save_info();

return 0;



}

//------------------------------------------------------------
// 改ページA ページ端に達してしまった場合の禁則処理
//------------------------------------------------------------

int Com_page2_end()
{

//テキスト表示フラグを戻す
Mode_stat.flag_text = ON;

//改ページ用フラグを解除
Mode_stat.flag_page2 = OFF;

//背景データでバックサーフェイスを書き換える
BitBlt(Back_DC,0,0,640,480,BG_DC,0,0,SRCCOPY);
strcpy(BK_bmp_fname,BG_bmp_fname) ; //ファイル名をBG面のものでキャッシュ

//文字表示位置をテキストエリアの左肩にリセットする
TEXT_X = TEXT_AREA.left;
TEXT_Y = TEXT_AREA.top;

if( _save_point_mode == _AUTO_ )make_save_info();

return 0;

}



↓Text_Com_02.cpp

//----------------------------
// カーテン型
//----------------------------

int sub_Com_g_change_task_1()
{

int pitch = 10;

//タイマーチェック
if( HLS_timer_check( g_change_timer,g_change_wait )==false )return 0;

//タイマーショット
HLS_timer_start(&g_change_timer);

BitBlt( Back_DC,g_change_frame_counter ,0,pitch-9,480,
BG_DC, g_change_frame_counter ,0,SRCCOPY);

BitBlt( Back_DC,g_change_frame_counter -10 ,0,pitch-8,480,
BG_DC, g_change_frame_counter -10 ,0,SRCCOPY);

BitBlt( Back_DC,g_change_frame_counter -20 ,0,pitch-7,480,
BG_DC, g_change_frame_counter -20 ,0,SRCCOPY);

BitBlt( Back_DC,g_change_frame_counter -30 ,0,pitch-6,480,
BG_DC, g_change_frame_counter -30 ,0,SRCCOPY);

BitBlt( Back_DC,g_change_frame_counter -40 ,0,pitch-5 ,480,
BG_DC, g_change_frame_counter -40 ,0,SRCCOPY);

BitBlt( Back_DC,g_change_frame_counter -50 ,0,pitch-4 ,480,
BG_DC, g_change_frame_counter -50 ,0,SRCCOPY);

BitBlt( Back_DC,g_change_frame_counter -60 ,0,pitch-3 ,480,
BG_DC, g_change_frame_counter -60 ,0,SRCCOPY);

BitBlt( Back_DC,g_change_frame_counter -70 ,0,pitch-2 ,480,
BG_DC, g_change_frame_counter -70 ,0,SRCCOPY);

BitBlt( Back_DC,g_change_frame_counter -80 ,0,pitch-1 ,480,
BG_DC, g_change_frame_counter -80 ,0,SRCCOPY);

BitBlt( Back_DC,g_change_frame_counter -90 ,0,pitch ,480,
BG_DC, g_change_frame_counter -90 ,0,SRCCOPY);


if( g_change_frame_counter - 100 < 640 ){
g_change_frame_counter += pitch;
}else{

//終了条件

//テキスト表示を再開する
Mode_stat.flag_text = ON;

//画面切替えのフラグをクリア
Mode_stat.flag_g_change = OFF;

//文字表示位置を初期化しておく
TEXT_X = TEXT_AREA.left;
TEXT_Y = TEXT_AREA.top;

strcpy( BK_bmp_fname,BG_bmp_fname ); //画面キャッシュの操作(BG→BK全面コピーに相当するので)

if( _save_point_mode == _AUTO_ ) make_save_info(); //セーブポイント処理

}

return 0;

}

//----------------------------
// 放射型
//----------------------------

int sub_Com_g_change_task_2()
{


static int A[]={
34,33,32,31,30,29,28,27,26,25,24,23,22,21,20,19,18,17,16,15,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34, //00
33,32,31,30,29,28,27,26,25,24,23,22,21,20,19,18,17,16,15,14,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33, //01
32,31,30,29,28,27,26,25,24,23,22,21,20,19,18,17,16,15,14,13,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32, //02
31,30,29,28,27,26,25,24,23,22,21,20,19,18,17,16,15,14,13,12,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31, //03
30,29,28,27,26,25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30, //04
29,28,27,26,25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29, //05
28,27,26,25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10, 9, 9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28, //06
27,26,25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10, 9, 8, 8, 9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27, //07
26,25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10, 9, 8, 7, 7, 8, 9,10,11,12,13,14,15,16,16,17,18,19,20,21,22,23,24,25, //08
25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10, 9, 8, 7, 6, 6, 7, 8, 9,10,11,12,13,14,15,16,16,17,18,19,20,21,22,23,24, //09
24,23,22,21,20,19,18,17,16,15,14,13,12,11,10, 9, 8, 7, 6, 5, 5, 6, 7, 8, 9,10,11,12,13,14,15,16,16,17,18,19,20,21,22,23, //10
23,22,21,20,19,18,17,16,15,14,13,12,11,10, 9, 8, 7, 6, 5, 4, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15,16,16,17,18,19,20,21,22, //11
22,21,20,19,18,17,16,15,14,13,12,11,10, 9, 8, 7, 6, 5, 4, 3, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15,16,16,17,18,19,20,21, //12
21,20,19,18,17,16,15,14,13,12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15,16,16,17,18,19,20, //13
20,19,18,17,16,15,14,13,12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15,16,16,17,18,19, //14
19,18,17,16,15,14,13,12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15,16,16,17,18, //15
19,18,17,16,15,14,13,12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15,16,16,17,18, //16
20,19,18,17,16,15,14,13,12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15,16,16,17,18,19, //17
21,20,19,18,17,16,15,14,13,12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15,16,16,17,18,19,20, //18
22,21,20,19,18,17,16,15,14,13,12,11,10, 9, 8, 7, 6, 5, 4, 3, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15,16,16,17,18,19,20,21, //19
23,22,21,20,19,18,17,16,15,14,13,12,11,10, 9, 8, 7, 6, 5, 4, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15,16,16,17,18,19,20,21,22, //20
24,23,22,21,20,19,18,17,16,15,14,13,12,11,10, 9, 8, 7, 6, 5, 5, 6, 7, 8, 9,10,11,12,13,14,15,16,16,17,18,19,20,21,22,23, //21
25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10, 9, 8, 7, 6, 6, 7, 8, 9,10,11,12,13,14,15,16,16,17,18,19,20,21,22,23,24, //22
26,25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10, 9, 8, 7, 7, 8, 9,10,11,12,13,14,15,16,16,17,18,19,20,21,22,23,24,25, //23
27,26,25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10, 9, 8, 8, 9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27, //24
28,27,26,25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10, 9, 9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28, //25
29,28,27,26,25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29, //26
30,29,28,27,26,25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30, //27
31,30,29,28,27,26,25,24,23,22,21,20,19,18,17,16,15,14,13,12,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31, //28
32,31,30,29,28,27,26,25,24,23,22,21,20,19,18,17,16,15,14,13,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32, //29
33,32,31,30,29,28,27,26,25,24,23,22,21,20,19,18,17,16,15,14,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33, //30
34,33,32,31,30,29,28,27,26,25,24,23,22,21,20,19,18,17,16,15,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34, //31

};
int x,y;

//タイマーチェック
if( HLS_timer_check( g_change_timer,g_change_wait )==false )return 0;

//タイマーショット
HLS_timer_start(&g_change_timer);

for( y=0;y<32;y++ ){
for( x=0;x<40;x++ ) {
if( A[y*40+x] == g_change_frame_counter ){
StretchBlt( Back_DC,x*16+7,y*16+7, 2, 2,BG_DC,x*16+4,y*16+4,2,2,SRCCOPY);
}
if( A[y*40+x] == g_change_frame_counter-2 ){
StretchBlt( Back_DC,x*16+6,y*16+6, 4, 4,BG_DC,x*16+6,y*16+6,4,4,SRCCOPY);

}
if( A[y*40+x] == g_change_frame_counter-4 ){
StretchBlt( Back_DC,x*16+4,y*16+4, 6, 6,BG_DC,x*16+4,y*16+4,6,6,SRCCOPY);
}
if( A[y*40+x] == g_change_frame_counter-6 ){
StretchBlt( Back_DC,x*16+3,y*16+3, 8, 8,BG_DC,x*16+3,y*16+3,8,8,SRCCOPY);
}
if( A[y*40+x] == g_change_frame_counter-8 ){
StretchBlt( Back_DC,x*16+2,y*16+2,12,12,BG_DC,x*16+2,y*16+2,12,12,SRCCOPY);
}
if( A[y*40+x] == g_change_frame_counter-10 ){
StretchBlt( Back_DC,x*16 ,y*16 ,16,16,BG_DC,x*16,y*16,16,16,SRCCOPY);
}
}
}

if( g_change_frame_counter < 45 ){
g_change_frame_counter++;
}else{

//終了条件

//テキスト表示を再開する
Mode_stat.flag_text = ON;

//画面切替えのフラグをクリア
Mode_stat.flag_g_change = OFF;

//文字表示位置を初期化しておく
TEXT_X = TEXT_AREA.left;
TEXT_Y = TEXT_AREA.top;

strcpy( BK_bmp_fname,BG_bmp_fname ); //画面キャッシュの操作(BG→BK全面コピーに相当するので)

if( _save_point_mode == _AUTO_ ) make_save_info(); //セーブポイント処理

}

return 0;

}

//----------------------------
// 伸縮型(X)
//----------------------------

int sub_Com_g_change_task_3()
{

int pitch = 20;

//タイマーチェック
if( HLS_timer_check( g_change_timer,g_change_wait )==false )return 0;

//タイマーショット
HLS_timer_start(&g_change_timer);

StretchBlt( Back_DC, 640/2-g_change_frame_counter,0,g_change_frame_counter*2,480,BG_DC,0,0,640,480,SRCCOPY);

if( g_change_frame_counter < 640/2 ){
g_change_frame_counter += pitch;
}else{

//終了条件

//テキスト表示を再開する
Mode_stat.flag_text = ON;

//画面切替えのフラグをクリア
Mode_stat.flag_g_change = OFF;

//文字表示位置を初期化しておく
TEXT_X = TEXT_AREA.left;
TEXT_Y = TEXT_AREA.top;

strcpy( BK_bmp_fname,BG_bmp_fname ); //画面キャッシュの操作(BG→BK全面コピーに相当するので)

if( _save_point_mode == _AUTO_ ) make_save_info(); //セーブポイント処理

}

return 0;

}

//----------------------------
// 伸縮型(Y)
//----------------------------

int sub_Com_g_change_task_4()
{

int pitch = 20;

//タイマーチェック
if( HLS_timer_check( g_change_timer,g_change_wait )==false )return 0;

//タイマーショット
HLS_timer_start(&g_change_timer);

StretchBlt(Back_DC, 0,480/2-g_change_frame_counter,640,g_change_frame_counter*2,BG_DC,0,0,640,480,SRCCOPY);

if( g_change_frame_counter < 480/2 ) {
g_change_frame_counter += pitch;

}else{

//終了条件

//テキスト表示を再開する
Mode_stat.flag_text = ON;

//画面切替えのフラグをクリア
Mode_stat.flag_g_change = OFF;

//文字表示位置を初期化しておく
TEXT_X = TEXT_AREA.left;
TEXT_Y = TEXT_AREA.top;

strcpy( BK_bmp_fname,BG_bmp_fname ); //画面キャッシュの操作(BG→BK全面コピーに相当するので)

if( _save_point_mode == _AUTO_ ) make_save_info(); //セーブポイント処理

}

return 0;


}



…さあ、これでようやくシステムメニュー上からのSAVE処理を行う準備ができました。次回はいよいよ目に見える部分です(^^;)いや〜長いっ!!