構成整理
ここらで画像表示は一旦やめて全体の構成を見直します。
ゲーム全体の処理はものすごく大雑把に言うと
- 初期化処理
- メインループ
- 終了処理
に分けられ、さらに「メインループ」の中を分けると
- 1フレームごとの動作
- 描画
- 待機(fpsを一定にするため)
になります。
※fps(Frame Per Second)とは1秒間に画面を書き換える回数のことです。大概のゲームは60fpsで動作します。
これに従って関数を分割します。
「1フレームごとの動作」と「描画」を分ける主な理由はフレームスキップへの対応です。
余裕があったら先々実装します。
また、リソース管理は集中的に行うと管理が楽になります。
リソースっていうのはつまり画像とか音声とか。
# Win32でいうリソースとは使い方が違うので注意。
# この言葉の使い方が合ってるのかどうかは分かりませんがとりあえずこの講習会ではリソースと呼ばせてもらいます。
あちこちでバラバラに読み込むと、読み込むときはいいけど解放するときに困る。
ので、一箇所で集中的に読み込んでいらなくなったら解放する仕組みを作っておいたほうがいいです。
さーて、こいつらまとめて適用しちゃうとこうなるぞー
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <math.h> #include <assert.h> #include "SDL.h" #define SCREEN_WIDTH 640 #define SCREEN_HEIGHT 480 #define DEFAULT_COLOR_KEY 0x00ff00ff // カラーキーとして指定する色。 // 画像リソースの識別番号。 // "ElementCount"を最後に定義しておくと、全部で幾つの画像リソースがあるのかが分かる。 enum ImageResource { ImageResource_Fighter, // 自機の画像 ImageResource_ElementCount, }; // このサーフェイスの中身が画面に対応するものと思ってください。 // あちこちから参照するのでためらわずグローバル。 static SDL_Surface* screen; // ゲーム中で使用する画像リソース。 static SDL_Surface *(imageResources[ImageResource_ElementCount]); // 順番的にプロトタイプ宣言が必要なやつら static void closeGame(int exitCode); // // 画像リソースを読み込む。 // static void loadImageResource(ImageResource id) { // 最も素朴な方法で。 switch(id) { case ImageResource_Fighter: imageResources[id] = SDL_LoadBMP("Resources/Images/Fighter.bmp"); break; default: assert(false); break; } // 読み込めたかチェック。 if(imageResources[id] == NULL) { fprintf(stderr, "画像リソースを読み込めません。 id=%d :%s\n", id, SDL_GetError()); closeGame(1); } // カラーキーに設定された色は透過される。 SDL_SetColorKey(imageResources[id], SDL_SRCCOLORKEY | SDL_RLEACCEL, DEFAULT_COLOR_KEY); } // // 全てのリソースを解放する。 // static void releaseAllResources() { for(int i = 0; i < ImageResource_ElementCount; ++i) { if(imageResources[i] != NULL) { SDL_FreeSurface(imageResources[i]); imageResources[i] = NULL; // 間違えて使わないようにNULLを入れておく。 } } } // // グラフィックス機能の終了処理を行う。 // 今のところ中身はない。openがあるのにcloseが無いのは気持ちが悪かったので作っただけ。 // static void closeGraphics() { } // // ゲームの終了処理を行う。 // 内部でexit()を呼ぶためここでアプリケーションは終了する。 // exitCode: exit()に渡す終了コード。 // static void closeGame(int exitCode) { // 全てのリソースを解放。 releaseAllResources(); // 描画機能を閉じる。 closeGraphics(); // 標準エラー出力のフラッシュ。 // これをやらないと出力内容がファイルに書き込まれないことがある。 fflush(stderr); // SDL終了。 SDL_Quit(); // しゅうりょー exit(exitCode); } // // グラフィックス機能の初期化を行う。 // 戻り値: 成功したらtrue、失敗したらfalse。 // static bool openGraphics() { // 今のところ中身はこれだけ screen = SDL_SetVideoMode(SCREEN_WIDTH, SCREEN_HEIGHT, 0, SDL_SWSURFACE); if (screen == NULL) { fprintf(stderr, "%dx%dの画面モードに設定できませんでした。: %s\n", SCREEN_WIDTH, SCREEN_HEIGHT, SDL_GetError()); return false; } return true; } // // ゲームの初期化を行う。 // 戻り値: 成功したらtrue、失敗したらfalse。 // static bool openGame() { // 標準エラー出力をテキストファイルに設定。 // こうしておくとコマンドラインから起動しなくても出力内容を見れる。 freopen("stderr.txt", "w", stderr); // SDL_Initではグラフィックス以外にもタイマーや音声機能の初期化も行うことができる。 if(SDL_Init(SDL_INIT_VIDEO) < 0) { fprintf(stderr, "SDLの初期化に失敗しました: %s\n", SDL_GetError()); return false; } // グラフィックス機能の初期化。 if(!openGraphics()) { return false; } return true; } // // ゲームの状態を1フレーム分先へ進める。 // static void run() { // なにもしない } // // 現在の状態を描画する。 // static void draw() { // 画像を(100, 100)へ転送。 SDL_Rect destination = {100, 100, 0, 0}; SDL_BlitSurface(imageResources[ImageResource_Fighter], NULL, screen, &destination); // 画面を更新。めんどくさいので画面全体。 SDL_UpdateRect(screen, 0, 0, screen->w, screen->h); } int main(int argc, char *argv[]) { // ゲームの初期化。 if(!openGame()) { return 1; } // 自機画像の読み込み loadImageResource(ImageResource_Fighter); // メインループ int done; done = 0; while(!done) { // イベントの確認。詳細はSDLのドキュメントを見る。 SDL_Event event; while ( SDL_PollEvent(&event) ) { switch (event.type) { case SDL_MOUSEMOTION: break; case SDL_MOUSEBUTTONDOWN: break; case SDL_KEYDOWN: /* Any keypress quits the app... */ case SDL_QUIT: done = 1; break; default: break; } } // 1フレームごとの動作 run(); // 描画 draw(); } closeGame(0); return 0; }
機能の追加らしい追加は全く行っていないのですがコード量が増えまくりました。
main()から順に構成を見ていってください。
ちなみにFPS固定のための待機はまだ入れていません。
次にやっておくべきことは
でしょうか。
早めに片付けておきましょう。