トップページに戻る / GCCページに戻る / SDL目次


16 戦闘画面 ウインドウの配置

ウインドウを配置し、大体の構成を作りました。「たたかう」などのコマンドウインドウと、主人公グループのHPなどを表示するステータスウインドウ、一行メッセージを表示するウインドウの3種類を用意しました。魔法とかアイテムを選択するウインドウが欲しいですが、それはもう少し後にしましょう。追加するのは簡単です。あとは、テキストを貼り付ければ戦闘画面になりますね。

敵の配置がまだ適当ですが、そのうちにまじめに計算するようにします。

#include <SDL/SDL.h>
#include <SDL/SDL_image.h>
#include <SDL/SDL_ttf.h>

/* ウインドウ設定 */
#define WINDOW_WIDTH 640
#define WINDOW_HEIGHT 480
#define BPP 32

/* メッセージウインドウ設定 */
#define MSG_WIN_BORDER 5
#define MSG_WIN_PADDING 3
#define MSG_WIN_COLOR(__x) SDL_MapRGB(__x, 0x00, 0x00, 0xAA)
#define MSG_WIN_BORDER_COLOR(__x) SDL_MapRGB(__x, 0x00, 0x00, 0x66)
#define MSG_WIN_OPACITY 160

#define BATTLE_COMMAND_WIN_X 0
#define BATTLE_COMMAND_WIN_Y 300
#define BATTLE_COMMAND_WIN_W 180
#define BATTLE_COMMAND_WIN_H (WINDOW_HEIGHT - BATTLE_COMMAND_WIN_Y)

#define BATTLE_STATUS_WIN_X (BATTLE_COMMAND_WIN_W + 1)
#define BATTLE_STATUS_WIN_Y (BATTLE_COMMAND_WIN_Y)
#define BATTLE_STATUS_WIN_W (WINDOW_WIDTH - BATTLE_STATUS_WIN_X)
#define BATTLE_STATUS_WIN_H (WINDOW_HEIGHT - BATTLE_STATUS_WIN_Y)

#define BATTLE_TOP_WIN_X 0
#define BATTLE_TOP_WIN_Y 0
#define BATTLE_TOP_WIN_W WINDOW_WIDTH
#define BATTLE_TOP_WIN_H 40

/* フォント設定 */
#define FONT_NAME "ipag-mona.ttf"
#define FONT_SIZE 24

/* 共通フォント */
TTF_Font *font;

/* 色 */
const SDL_Color color_black = {0x00, 0x00, 0x00};

/* レイヤー構造体 */
typedef struct{
	SDL_Surface *surface;	/* サーフェス */
	int use;		/* 使用有無 */
	int visible;		/* 表示有無 */
	SDL_Rect src_rect;	/* コピー元矩形 */
	SDL_Rect dst_rect;	/* コピー先矩形 */
} type_layer;

typedef struct{
	SDL_Surface *image;
	SDL_Rect src_rect;
	SDL_Rect dst_rect;
} type_enemy_image;

/* レイヤー番号列挙体 */
enum enm_layer{
	lyrBackground = 0,	/* 背景 */
	lyrEnemy,		/* 敵 */
	lyrStatuswin_1,	/* ステータスウインドウ 1 */
	lyrCommandwin_1,	/* コマンドウインドウ 1 */
	lyrTopwin,		/* 上段ウインドウ */
/*	lyrText,*/		/* メッセージ */
	lyrEnd			/* 「ダミー」 */
};

/* グローバル変数 */
type_enemy_image g_enemy_image;
type_layer g_game_layers[lyrEnd];	/* レイヤー */
int g_iDisplayUpdateFlag;			/* 画面アップデートフラグ */

/*****************************************************************
 * ここから関数                                                  *
 *****************************************************************/

/*****************************************************************
 * 更新フラグ関係                                                *
 *****************************************************************/

/* 更新フラグセット */
int set_update_display_flag(){
	g_iDisplayUpdateFlag = 1;
	return 0;
}

/* 更新フラグリセット */
int reset_update_display_flag(){
	g_iDisplayUpdateFlag = 0;
	return 0;
}

/* 更新フラグ取得 */
int get_update_display_flag(){
	return g_iDisplayUpdateFlag;
}


/*****************************************************************
 * レイヤー関係                                                  *
 *****************************************************************/

/* レイヤーのsrc_rectのサイズをサーフェスサイズに指定する */
int update_layer_rect_size(type_layer *layers, int index){
	(layers + index)->src_rect.w = (layers + index)->surface->w;
	(layers + index)->src_rect.h = (layers + index)->surface->h;
	return 0;
}

/* レイヤー配置位置を設定する */
int update_layer_rect_pos(type_layer *layers, int index, int x, int y){
	(layers + index)->dst_rect.x = x;
	(layers + index)->dst_rect.y = y;
	return 0;
}

/* SDL_Rectを0で初期化 */
int init_rect(SDL_Rect* rect){
	/* memset0でもよかったな */
	rect->x = 0;
	rect->y = 0;
	rect->w = 0;
	rect->h = 0;
	return 0;
}

/* レイヤーのsrc_rectとdst_rectを初期化 */
int init_layer_rect(type_layer* layer, int index){
	init_rect(&((layer + index)->src_rect));
	init_rect(&((layer + index)->dst_rect));
	return 0;
}

/* レイヤーの可視状態を設定する */
int set_layer_visible(type_layer *layers, int index, int flag){
	(layers + index)->visible = flag;
	return 0;
}

/* サーフェス破棄 */
int drop_surface(SDL_Surface *surface){
	SDL_FreeSurface(surface);
	return 0;
}

/* レイヤーの破棄 */
int drop_layer(type_layer *layer, int index){
	drop_surface((layer + index)->surface);
	(layer + index)->use = 0;
	set_layer_visible(layer, index, 0);
	return 0;
}

/* 全レイヤーの破棄 */
int drop_layers(){
	int iLoop;
	for(iLoop = 0; iLoop < lyrEnd; iLoop ++){
		if(g_game_layers[iLoop].use == 1){
			drop_layer(g_game_layers, iLoop);
		}
	}
	return 0;
}


/*****************************************************************
 * タイマー関数                                                  *
 *****************************************************************/

/* アニメーション */
Uint32 enemy_animation(Uint32 interval, void* param){
	type_enemy_image *enimage = (type_enemy_image*)param;
	enimage->src_rect.x = enimage->src_rect.w - enimage->src_rect.x;
	set_update_display_flag();
	return interval;
}

/* レイヤーの描画 */
Uint32 update_display(Uint32 interval, void* param){
	SDL_Surface *video_surface = SDL_GetVideoSurface();
	int iLoop;
	
	/* アップデートフラグのチェック */
	if(get_update_display_flag() == 0){
		return interval;
	}

	for(iLoop = 0; iLoop < lyrEnd; iLoop ++){
		if(g_game_layers[iLoop].visible == 1){
			switch(iLoop){
			case lyrEnemy:
				SDL_BlitSurface(
					g_enemy_image.image,
					&g_enemy_image.src_rect,
					video_surface,
					&g_enemy_image.dst_rect
				);
				break;
			default:
				/* サーフェスの複写 */
				SDL_BlitSurface(
					g_game_layers[iLoop].surface,
					&g_game_layers[iLoop].src_rect,
					video_surface,
					&g_game_layers[iLoop].dst_rect
				);
				break;
			}
		}
	}

	/* サーフェスフリップ */
	SDL_Flip(video_surface);

	/* アップデートフラグの初期化 */
	reset_update_display_flag();

	return interval;
}


/*****************************************************************
 * レイヤー作成                                                  *
 *****************************************************************/

/* メッセージウインドウ作成 */
int create_msg_window(type_layer *layer, int index, int x, int y, int w, int h){
	init_layer_rect(layer, index);
	SDL_Surface *tmp_surface;
	SDL_Rect tmp_rect;

	/* サーフェス作成 */
	tmp_surface = SDL_CreateRGBSurface(SDL_HWSURFACE, w, h, 32, 0, 0, 0, 0);
	if(tmp_surface == NULL){
		return -1;
	}

	/* ウインドウ作成(ボーダー色) */
	tmp_rect.x = 0;
	tmp_rect.y = 0;
	tmp_rect.w = w;
	tmp_rect.h = h;
	SDL_FillRect(tmp_surface, &tmp_rect, MSG_WIN_BORDER_COLOR(tmp_surface->format));

	/* ウインドウ作成(ウインドウ色) */
	tmp_rect.x = MSG_WIN_BORDER;
	tmp_rect.y = MSG_WIN_BORDER;
	tmp_rect.w = w - MSG_WIN_BORDER * 2;
	tmp_rect.h = h - MSG_WIN_BORDER * 2;
	SDL_FillRect(tmp_surface, &tmp_rect, MSG_WIN_COLOR(tmp_surface->format));

	/* 透過設定 */
	SDL_SetAlpha(tmp_surface, SDL_SRCALPHA, MSG_WIN_OPACITY);
	(layer + index)->surface = SDL_DisplayFormat(tmp_surface);

	SDL_FreeSurface(tmp_surface);

	update_layer_rect_size(layer, index);
	update_layer_rect_pos(layer, index, x, y);

	return 0;
}

/* レイヤーの初期化 */
int init_layers(){

	int iLoop;

	/* 矩形初期化 */
	for(iLoop = 0; iLoop < lyrEnd; iLoop ++){
		init_layer_rect(g_game_layers, iLoop);
		g_game_layers[iLoop].use = 0;
		g_game_layers[iLoop].visible = 0;
	}

	/* バックグラウンド */
	g_game_layers[lyrBackground].surface =
		IMG_Load("bg.jpg");
	update_layer_rect_size(g_game_layers, lyrBackground);
	g_game_layers[lyrBackground].use = 1;
	set_layer_visible(g_game_layers, lyrBackground, 1);

	/* 敵 */
	g_enemy_image.image = IMG_Load("penguin_nuke_anim.png");
	/* (手書き) */
	g_enemy_image.src_rect.w = 212;
	g_enemy_image.src_rect.h = 240;
	g_enemy_image.src_rect.x = 0;
	g_enemy_image.src_rect.y = 0;
	g_enemy_image.dst_rect.x = 200;
	g_enemy_image.dst_rect.y = 40;
	set_layer_visible(g_game_layers, lyrEnemy, 1);

	/* ウインドウ */
	create_msg_window(g_game_layers, lyrCommandwin_1,
		BATTLE_COMMAND_WIN_X,
		BATTLE_COMMAND_WIN_Y,
		BATTLE_COMMAND_WIN_W,
		BATTLE_COMMAND_WIN_H);
	set_layer_visible(g_game_layers, lyrCommandwin_1, 1);

	create_msg_window(g_game_layers, lyrStatuswin_1,
		BATTLE_STATUS_WIN_X,
		BATTLE_STATUS_WIN_Y,
		BATTLE_STATUS_WIN_W,
		BATTLE_STATUS_WIN_H);
	set_layer_visible(g_game_layers, lyrStatuswin_1, 1);

	create_msg_window(g_game_layers, lyrTopwin,
		BATTLE_TOP_WIN_X,
		BATTLE_TOP_WIN_Y,
		BATTLE_TOP_WIN_W,
		BATTLE_TOP_WIN_H);
	set_layer_visible(g_game_layers, lyrTopwin, 1);
	return 0;
}


/* メイン関数 */
int main(int argc, char* argv[]){
	SDL_Event event;
	SDL_TimerID timer_id, display_timer_id;
	int exit_prg = 0;

	SDL_Init(SDL_INIT_EVERYTHING);

	TTF_Init();

	SDL_SetVideoMode(WINDOW_WIDTH, WINDOW_HEIGHT, BPP, SDL_HWSURFACE);

	/* フォント読み込み */
	font = TTF_OpenFont(FONT_NAME, FONT_SIZE);

	/* 画像読み込み */
	init_layers();

	/* 描画 */
	set_update_display_flag();
	update_display(0, NULL);	/* 初期描画 */

	timer_id = SDL_AddTimer(1000, enemy_animation, &g_enemy_image);
	display_timer_id = SDL_AddTimer(200, update_display, NULL);

	/* イベントループ */
	while(exit_prg == 0){
		if(SDL_PollEvent(&event)){
			switch(event.type){
			case SDL_KEYDOWN:
				switch(event.key.keysym.sym){
				case SDLK_UP:
					break;
				case SDLK_DOWN:
					break;
				case SDLK_RIGHT:
					break;
				case SDLK_LEFT:
					break;
				case SDLK_ESCAPE:
					exit_prg = 1;
					break;
				case SDLK_SPACE:
					break;
				default:
					break;
				}

				break;
			default:
				break;
			}
		}
		SDL_Delay(1);
	}

	SDL_RemoveTimer(display_timer_id);
	SDL_RemoveTimer(timer_id);

	drop_surface(g_enemy_image.image);

	drop_layers();

	TTF_CloseFont(font);

	TTF_Quit();

	SDL_Quit();

	return 0;
}

ウインドウ用サーフェスを最初にあらかじめ作成させておくやりかたです。青字の部分でそれを作成しています。もっとウインドウがほしければ、レイヤーを追加し、サイズと表示位置を指定して作成します。メモリに問題がなければこれがシンプルで簡単な方法でしょう。メモリをケチりたかったら、その都度作成するようにします。


/

indexに戻る