前回、敵のアニメーションをやりました。敵のグラフィックのみ動かすと重ねがきしてしまうので、背景部分も描画する必要があります。
そこで、次のように変更します。
#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 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, /* 敵 */ /* lyrText,*/ /* メッセージ */ lyrEnd /* 「ダミー」 */ }; /* グローバル変数 */ type_enemy_image g_enemy_image; type_layer g_game_layers[lyrEnd]; /* レイヤー */ /* ここから関数 */ /* レイヤーの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; } /* 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; } /* アニメーション */ 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; return interval; } /* レイヤーの描画 */ Uint32 update_display(Uint32 interval, void* param){ SDL_Surface *video_surface = SDL_GetVideoSurface(); int iLoop; 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); return interval; } /* レイヤーの初期化 */ 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 = 0; g_enemy_image.dst_rect.y = 0; set_layer_visible(g_game_layers, lyrEnemy, 1); 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; } /* メイン関数 */ 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(); /* 描画 */ 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; }
アニメーションを動かすタイマーと描画用のタイマーを用意します。アニメーションを動かすタイマーではRectの値の変更のみ行い、描画用タイマーで実際に描画を行うようにします。こうすればサーフェスへの二重アクセス等といった面倒な問題を回避するようにしています。いまのところ、アップデートしなければならない原因は敵アニメーションのみですが、実際にはプレイヤー操作のカーソル移動等がありますので、処理をまとめておいたほうが便利です。
描画用タイマーの間隔はアニメーションタイマーより短く取ってあるので、無駄な描画が多くなります。アップデートフラグかなにかを用意して、無駄な描画を省くようにするといいでしょう。