シーケンス遷移テンプレート。


RGKT-NDS-STDY-022(006)


ゲームプログラマになる前に覚えておきたい技術』を自分なりに解釈して
「Chapter5 かんたんなシーケンス遷移」の言う
階層構造のシーケンス遷移を実装してみました。


CAVE GAME で組んだシーケンス遷移よりももう少しだけちゃんと書き直しました。
一般的な
タイトル<−>ゲーム
のシーン遷移を行っていて、ゲームシーンは更に
メイン、コンフィグへと分岐・シーン遷移します。


初歩の初歩として必要な項目はこのくらいかと。
後はシーンに応じて階層を深く掘って行けばいいんじゃないかと思います。


解釈が間違ってるよーとか、突っ込み大歓迎です。
分かりやすく見れるよう勤めたつもりですが…
書き終わったばっかりで客観視できませんw
後、C++ での説明を独自解釈で C ソースに落とし込んでるので
これで合ってるのか(=理解出来てるのか)が不安だったり(苦笑)。


C++ じゃなくて Cコード の場合の実装参考例として。
C++ でしたら上記書物の付属CDにそのものが入ってるので、
そっちで知ることをお勧めします。


Cソース 5ファイル、ヘッダ 5ファイルの構成です。
メイン関数用と、シーン毎でファイルを分割してこの数になります。
シーン毎にファイル分けするのは1シーン毎の見易さを得るためです。
本当に少しの処理だけなら1ファイルにまとめても構わないと思いますが
関連するシーンが3シーン以上に分岐しそうならファイル分けした方が良いと感じました。個人的に。


後はシーン遷移の、各々の関連性(遷移する流れ・順番等)は結局のところ
コーディングする自分自身が管理・理解してないと
状態遷移を組んでもどう遷移するか見えなくなるので
(このソース場合の遷移は、状態をスタックするタイプじゃなく、
やってるそれはラベルジャンプ(goto)のようなものなので)
小規模以上の遷移を組むなら紙なりテキストファイルなりで
遷移の順は把握できるようにして置いた方が無難です。


続きを読むにて、ソース・ヘッダの内容を見ることができます。
main.h

// TODO: ここに一意の識別子を定義する(重複インクルード防止用)。
#ifndef _MAIN_H_
#define _MAIN_H_

// TODO: ここに、このヘッダファイルが必要とする外部ヘッダファイルを記述する。

#ifdef __cplusplus
extern "C" {
#endif

// TODO: ここに #define 定義、ならびに関数のプロトタイプ宣言を追加する。

//メインループ内、シーン定数
typedef enum{
	SCENE_TITLE = 0,
	SCENE_GAME  = 1
}SCENE_SEQ;

#ifdef __cplusplus
}
#endif

#endif


main.c

//root
//---------------------------------------------------------------------------------
//メインループ内、インクルードヘッダ群
#include <nds.h>
#include <stdio.h>
#include "main.h"
#include "title.h"
#include "game.h"
//---------------------------------------------------------------------------------
//メインループ内関数、プロトタイプ宣言
void initMain(void);
//---------------------------------------------------------------------------------
//メインループ内関数
void initMain(void)
{
	//コンソール初期化
	consoleDemoInit();
}
//---------------------------------------------------------------------------------
int main(void)
{
	//メインループ内、シーン遷移フラグ
	SCENE_SEQ scene_seq = SCENE_TITLE;
	
	//メイン初期化処理
	initMain();
	
	//メインループ
	while(1) {
		scanKeys();
		
		//シーケンス遷移
		switch(scene_seq){
		case SCENE_TITLE:
			scene_seq = updateTitle();
			break;
		case SCENE_GAME:
			scene_seq = updateGame();
			break;
		default:
			break;
		}
		
		swiWaitForVBlank();
	}
	return 0;
}
//---------------------------------------------------------------------------------


title.h

// TODO: ここに一意の識別子を定義する(重複インクルード防止用)。
#ifndef _TITLE_H_
#define _TITLE_H_

// TODO: ここに、このヘッダファイルが必要とする外部ヘッダファイルを記述する。
#include "main.h"

#ifdef __cplusplus
extern "C" {
#endif

// TODO: ここに #define 定義、ならびに関数のプロトタイプ宣言を追加する。

//タイトルシーン更新用、グローバル関数、プロトタイプ宣言
SCENE_SEQ updateTitle(void);

#ifdef __cplusplus
}
#endif

#endif

title.c

//SCENE_TITLE
//---------------------------------------------------------------------------------
//タイトルシーン用、インクルードヘッダ群
#include <nds.h>
#include <stdio.h>
#include "title.h"
#include "main.h"
//---------------------------------------------------------------------------------
//タイトルシーン内、シーン定数
typedef enum{
	SCENE_TITLE_INIT = 10,
	SCENE_TITLE_EXEC = 11,
	SCENE_TITLE_EXIT = 12
}SCENE_TITLE_SEQ;
//---------------------------------------------------------------------------------
//タイトルシーン内、シーン遷移フラグ
static SCENE_TITLE_SEQ scene_title_seq = SCENE_TITLE_INIT;
//---------------------------------------------------------------------------------
//タイトルシーン内、シーン更新インライン関数、プロトタイプ宣言
static SCENE_TITLE_SEQ initTitle(void);//コントラクタ
static SCENE_TITLE_SEQ execTitle(void);//メインループ
static SCENE_TITLE_SEQ exitTitle(void);//デストラクタ
//---------------------------------------------------------------------------------
//タイトルシーン内、シーン更新インライン関数
static SCENE_TITLE_SEQ initTitle(void)
{
	BG_PALETTE_SUB[0] = RGB15(15, 15, 15);
	consoleClear();
	iprintf("\x1b[7;3HSequence transition sample");
	iprintf("\x1b[8;9HScene TITLE");
	iprintf("\x1b[11;3HPUSH START to GAME MENU");
	return SCENE_TITLE_EXEC;
}
//---------------------------------------------------------------------------------
static SCENE_TITLE_SEQ execTitle(void)
{
	if(keysDown() & KEY_START){
		//スタートボタンが押されたらタイトルシーンを終了する。
		return SCENE_TITLE_EXIT;
	}
	//タイトルシーンが終わってなければタイトルシーンを継続する。
	return SCENE_TITLE_EXEC;
}
//---------------------------------------------------------------------------------
static SCENE_TITLE_SEQ exitTitle(void)
{
	//デストラクタ処理(次回実行時のために必要な初期化作業)
	return SCENE_TITLE_INIT;
}
//---------------------------------------------------------------------------------
//タイトルシーン更新用、グローバル関数
SCENE_SEQ updateTitle(void)
{
	//タイトルシーケンス遷移
	switch(scene_title_seq){
	case SCENE_TITLE_INIT:
		scene_title_seq = initTitle();
		break;
	case SCENE_TITLE_EXEC:
		scene_title_seq = execTitle();
		break;
	case SCENE_TITLE_EXIT:
		//タイトルシーンを終了。ゲームシーンへ遷移する。
		scene_title_seq = exitTitle();
		return SCENE_GAME;
		break;
	default:
		break;
	}
	return SCENE_TITLE;
}
//---------------------------------------------------------------------------------

game.h

// TODO: ここに一意の識別子を定義する(重複インクルード防止用)。
#ifndef _GAME_H_
#define _GAME_H_

// TODO: ここに、このヘッダファイルが必要とする外部ヘッダファイルを記述する。
#include "main.h"

#ifdef __cplusplus
extern "C" {
#endif

// TODO: ここに #define 定義、ならびに関数のプロトタイプ宣言を追加する。

//ゲームシーケンス内、シーン定数
typedef enum{
	SCENE_GAME_INIT   = 20,
	SCENE_GAME_EXEC   = 21,
	SCENE_GAME_MAIN   = 22,
	SCENE_GAME_CONFIG = 23,
	SCENE_GAME_EXIT   = 24
}SCENE_GAME_SEQ;

//ゲームシーン更新用、グローバル関数、プロトタイプ宣言
SCENE_SEQ updateGame(void);

#ifdef __cplusplus
}
#endif

#endif


game.c

//SCENE_GAME
//---------------------------------------------------------------------------------
//ゲームシーン用、インクルードヘッダ群
#include <nds.h>
#include <stdio.h>
#include "main.h"
#include "game.h"
#include "gameMain.h"
#include "gameConfig.h"
//---------------------------------------------------------------------------------
//ゲームシーン内、シーン遷移フラグ
static SCENE_GAME_SEQ scene_game_seq = SCENE_GAME_INIT;
//---------------------------------------------------------------------------------
//ゲームシーン内、シーン更新インライン関数、プロトタイプ宣言
static SCENE_GAME_SEQ initGame(void);//コントラクタ
static SCENE_GAME_SEQ execGame(void);//メインループ
static SCENE_GAME_SEQ exitGame(void);//デストラクタ
//---------------------------------------------------------------------------------
//ゲームシーン内、シーン更新インライン関数
static SCENE_GAME_SEQ initGame(void)
{
	BG_PALETTE_SUB[0] = RGB15(31, 0, 0);
	consoleClear();
	iprintf("\x1b[7;3HSequence transition sample");
	iprintf("\x1b[8;9HScene GAME");
	iprintf("\x1b[11;3HPUSH A to GAME MAIN");
	iprintf("\x1b[12;3HPUSH B to GAME CONFIG");
	return SCENE_GAME_EXEC;
}
//---------------------------------------------------------------------------------
static SCENE_GAME_SEQ execGame(void)
{
	if(keysDown() & KEY_A){
		//Aボタンが押されたらゲームメインシーンへ遷移する。
		return SCENE_GAME_MAIN;
	}else if(keysDown() & KEY_B){
		//Bボタンが押されたらゲームコンフィグシーンへ遷移する。
		return SCENE_GAME_CONFIG;
	}
	//ゲームシーンが終わってなければゲームシーンを継続する。
	return SCENE_GAME_EXEC;
}
//---------------------------------------------------------------------------------
static SCENE_GAME_SEQ exitGame(void)
{
	//デストラクタ処理(次回実行時のために必要な初期化作業)
	return SCENE_GAME_INIT;
}
//---------------------------------------------------------------------------------
//ゲームシーン更新用、グローバル関数
SCENE_SEQ updateGame(void)
{
	//ゲームシーケンス遷移
	switch(scene_game_seq){
	case SCENE_GAME_INIT:
		scene_game_seq = initGame();
		break;
	case SCENE_GAME_EXEC:
		scene_game_seq = execGame();
		break;
	case SCENE_GAME_MAIN:
		scene_game_seq = updateGameMain();
		break;
	case SCENE_GAME_CONFIG:
		scene_game_seq = updateGameConfig();
		break;
	case SCENE_GAME_EXIT:
		//ゲームシーン終了。タイトルシーンへ遷移する。
		scene_game_seq = exitGame();
		return SCENE_TITLE;
		break;
	default:
		break;
	}
	return SCENE_GAME;
}
//---------------------------------------------------------------------------------


gameMain.h

// TODO: ここに一意の識別子を定義する(重複インクルード防止用)。
#ifndef _GAME_MAIN_H_
#define _GAME_MAIN_H_

// TODO: ここに、このヘッダファイルが必要とする外部ヘッダファイルを記述する。
#include "game.h"

#ifdef __cplusplus
extern "C" {
#endif

// TODO: ここに #define 定義、ならびに関数のプロトタイプ宣言を追加する。

//ゲームメインシーン更新用、グローバル関数、プロトタイプ宣言
SCENE_GAME_SEQ updateGameMain(void);

#ifdef __cplusplus
}
#endif

#endif


gameMain.c

//SCENE_GAME_MAIN
//---------------------------------------------------------------------------------
//ゲームメインシーン用、インクルードヘッダ群
#include <nds.h>
#include <stdio.h>
#include "game.h"
#include "gameMain.h"
//---------------------------------------------------------------------------------
//ゲームメインシーケンス内、シーン定数
typedef enum{
	SCENE_GAME_MAIN_INIT = 220,
	SCENE_GAME_MAIN_EXEC = 221,
	SCENE_GAME_MAIN_EXIT = 222
}SCENE_GAME_MAIN_SEQ;
//---------------------------------------------------------------------------------
//ゲームメインシーン内、シーン遷移フラグ
static SCENE_GAME_MAIN_SEQ scene_game_main_seq = SCENE_GAME_MAIN_INIT;
//---------------------------------------------------------------------------------
//ゲームメインシーン内、シーン更新インライン関数、プロトタイプ宣言
static SCENE_GAME_MAIN_SEQ initGameMain(void);//コントラクタ
static SCENE_GAME_MAIN_SEQ execGameMain(void);//メインループ
static SCENE_GAME_MAIN_SEQ exitGameMain(void);//デストラクタ
//---------------------------------------------------------------------------------
//ゲームメインシーン内、シーン更新インライン関数
static SCENE_GAME_MAIN_SEQ initGameMain(void)
{
	BG_PALETTE_SUB[0] = RGB15(0, 31, 0);
	consoleClear();
	iprintf("\x1b[7;3HSequence transition sample");
	iprintf("\x1b[8;9HScene GAME MAIN");
	
	iprintf("\x1b[11;1HPUSH START to GAME MAIN END");
	iprintf("\x1b[12;1Hnext TITLE");
	return SCENE_GAME_MAIN_EXEC;
}
//---------------------------------------------------------------------------------
static SCENE_GAME_MAIN_SEQ execGameMain(void)
{
	if(keysDown() & KEY_START){
		//スタートボタンが押されたらゲームメインシーンを終了する。
		return SCENE_GAME_MAIN_EXIT;
	}
	//ゲームメインシーンが終わってなければゲームメインシーンを継続する。
	return SCENE_GAME_MAIN_EXEC;
}
//---------------------------------------------------------------------------------
static SCENE_GAME_MAIN_SEQ exitGameMain(void)
{
	//デストラクタ処理(次回実行時のために必要な初期化作業)
	return SCENE_GAME_MAIN_INIT;
}
//---------------------------------------------------------------------------------
//ゲームメインシーン更新用、グローバル関数
SCENE_GAME_SEQ updateGameMain(void)
{
	//ゲームメインシーケンス遷移
	switch(scene_game_main_seq){
	case SCENE_GAME_MAIN_INIT:
		scene_game_main_seq = initGameMain();
		break;
	case SCENE_GAME_MAIN_EXEC:
		scene_game_main_seq = execGameMain();
		break;
	case SCENE_GAME_MAIN_EXIT:
		//ゲームメインシーン終了。ゲームシーン終了へ遷移する。
		scene_game_main_seq = exitGameMain();
		return SCENE_GAME_EXIT;
		break;
	default:
		break;
	}
	return SCENE_GAME_MAIN;
}
//---------------------------------------------------------------------------------


gameConfig.h

// TODO: ここに一意の識別子を定義する(重複インクルード防止用)。
#ifndef _GAME_CONFIG_H_
#define _GAME_CONFIG_H_

// TODO: ここに、このヘッダファイルが必要とする外部ヘッダファイルを記述する。
#include "game.h"

#ifdef __cplusplus
extern "C" {
#endif

// TODO: ここに #define 定義、ならびに関数のプロトタイプ宣言を追加する。

//ゲームコンフィグシーン更新用、グローバル関数、プロトタイプ宣言
SCENE_GAME_SEQ updateGameConfig(void);

#ifdef __cplusplus
}
#endif

#endif


gameConfig.c

//SCENE_GAME_CONFIG
//---------------------------------------------------------------------------------
//ゲームメインシーン用、インクルードヘッダ群
#include <nds.h>
#include <stdio.h>
#include "game.h"
#include "gameConfig.h"
//---------------------------------------------------------------------------------
//ゲームコンフィグシーケンス内、シーン定数
typedef enum{
	SCENE_GAME_CONFIG_INIT = 230,
	SCENE_GAME_CONFIG_EXEC = 231,
	SCENE_GAME_CONFIG_EXIT = 232
}SCENE_GAME_CONFIG_SEQ;
//---------------------------------------------------------------------------------
//ゲームコンフィグシーン内、シーン遷移フラグ
static SCENE_GAME_CONFIG_SEQ scene_game_config_seq = SCENE_GAME_CONFIG_INIT;
//---------------------------------------------------------------------------------
//ゲームコンフィグシーン内、シーン更新インライン関数、プロトタイプ宣言
static SCENE_GAME_CONFIG_SEQ initGameConfig(void);//コントラクタ
static SCENE_GAME_CONFIG_SEQ execGameConfig(void);//メインループ
static SCENE_GAME_CONFIG_SEQ exitGameConfig(void);//デストラクタ
//---------------------------------------------------------------------------------
//ゲームコンフィグシーン内、シーン更新インライン関数
static SCENE_GAME_CONFIG_SEQ initGameConfig(void)
{
	BG_PALETTE_SUB[0] = RGB15(0, 0, 31);
	consoleClear();
	iprintf("\x1b[7;3HSequence transition sample");
	iprintf("\x1b[8;9HScene GAME CONFIG");
	
	iprintf("\x1b[11;1HPUSH START to GAME CONFIG END");
	iprintf("\x1b[12;1Hnext GAME MENU");
	return SCENE_GAME_CONFIG_EXEC;
}
//---------------------------------------------------------------------------------
static SCENE_GAME_CONFIG_SEQ execGameConfig(void)
{
	if(keysDown() & KEY_START){
		//スタートボタンが押されたらゲームコンフィグシーンを終了する。
		return SCENE_GAME_CONFIG_EXIT;
	}
	//ゲームコンフィグシーンが終わってなければゲームコンフィグシーンを継続する。
	return SCENE_GAME_CONFIG_EXEC;
}
//---------------------------------------------------------------------------------
static SCENE_GAME_CONFIG_SEQ exitGameConfig(void)
{
	//デストラクタ処理(次回実行時のために必要な初期化作業)
	return SCENE_GAME_CONFIG_INIT;
}
//---------------------------------------------------------------------------------
//ゲームコンフィグシーン更新用、グローバル関数
SCENE_GAME_SEQ updateGameConfig(void)
{
	//ゲームコンフィグシーケンス遷移
	switch(scene_game_config_seq){
	case SCENE_GAME_CONFIG_INIT:
		scene_game_config_seq = initGameConfig();
		break;
	case SCENE_GAME_CONFIG_EXEC:
		scene_game_config_seq = execGameConfig();
		break;
	case SCENE_GAME_CONFIG_EXIT:
		//ゲームコンフィグシーン終了。ゲームシーンへ遷移する。
		scene_game_config_seq = exitGameConfig();
		return SCENE_GAME_INIT;
		break;
	default:
		break;
	}
	return SCENE_GAME_CONFIG;
}
//---------------------------------------------------------------------------------