とりあえず SDL を使った最小限のプログラムはこんな感じになります。
#include <SDL.h> #include <stdio.h> int main(int argc,char *argv[]) { /* 初期化 */ if(SDL_Init(SDL_INIT_VIDEO)<0) { fprintf(stderr,"failed to initialize SDL.\n"); return -1; } /* 終了 */ SDL_Quit(); return 0; }
まあつまり初期化は
SDL_Init()
で終了は
SDL_Quit()
とゆーことです。
特に説明しなくても分かりそうですが、一応少し補足しておくと :
SDL_Init()
の引数に指定します。
複数のサブシステムを使うときは SDL_INIT_VIDEO|SDL_INIT_AUDIO
のようにビット和をとります。
main
は必ず
int main(int,char *[])
でなければいけません
(コマンドライン引数を参照しないときでも)。
int main(void)
だとエラーになります。
SDL がそういう仕様になっています。
まあ実害はないはずなので大人しく従っておきましょう。
で、これのコンパイルですが、
$ gcc -o test test.c
ではダメで、
$ gcc -o test test.c `sdl-config --cflags --libs`
とします (`
は左下がりではなく右下がりの引用符)。
この `sdl-config --cflags --libs`
というのは
SDL のヘッダファイルやライブラリの位置をコンパイラに教えるためのものです
(これをつけないと SDL.h などが見つかりません)。
気になる人は
$ sdl-config --cflags --libs
としてみると良いでしょう。
ヘッダやライブラリの位置が出てくると思います。
コマンド名を `
で囲んでおくとその出力結果がコマンドラインに展開されるので、それがコンパイラに伝わるという仕組みです。
ここまでで
SDL_Init()
,
SDL_Quit()
という 2 つの関数が出てきました。
この後もいろいろと出てきます。
しかしこれらを全部細かく説明しているとあまりにも長くなってしまうので、不明な点などがあれば各自
SDL のドキュメント
(和訳あり) を参照して下さい。
あと、ちょっと調べたいときは UNIX の
man
コマンドでも調べられます。
たとえば :
$ man SDL_Init
さすがに初期化と終了だけでは寂しすぎるので、もう一歩進みましょう。
#include <SDL.h> int main(int argc,char *argv[]) { /* 初期化 */ if(SDL_Init(SDL_INIT_VIDEO)<0) { fprintf(stderr,"failed to initialize SDL.\n"); return -1; } /* ループ */ while(1) { SDL_Event event; while(SDL_PollEvent(&event)) if(event.type==SDL_QUIT) goto QUIT; /* ゲームならここで何か処理する */ } /* 終了 */ QUIT: SDL_Quit(); return 0; }
ゲームだとまあ大抵ループがあるわけですが、この中に必ず入れなければならない処理があります。 それはイベント処理とゆーものです。
たとえば Windows などではウィンドウの右上の×ボタンをクリックするとプログラムが終了します。 これはどういう仕組みかというと、×ボタンがクリックされたとき、Windows からそのプログラムに「終了しろー」というメッセージが送られて、それを検知したプログラムが終了する、という流れになっています。 つまり OS 側からそのようなメッセージが送られてきていないかをプログラムの方でチェックしていなければいけないのです (これをやっていないと×ボタンをクリックしても反応がないので、プログラムが暴走しているように見えます)。
上のプログラムでは
SDL_Event event; while(SDL_PollEvent(&event)) if(event.type==SDL_QUIT) goto QUIT;
という部分でその処理を行っています。
SDL_PollEvent()
で発生したイベントを受け取って、それが
SDL_QUIT
(つまり「終了しろ」) なら終了します。
なお、
SDL_PollEvent()
は未処理のイベントがたまっている間は
1、イベントが全部消化されたら 0 を返してくるので、上のようにして
while
ループから抜け出すことができます。
OK?
さて、ここまで地味なプログラムばかりでしたが、いよいよグラフィックスに入っていきます。
とりあえず初期化 (画面モードの設定 & ウィンドウの作成)
はこんな感じです (もちろんこれは
SDL_Init()
と
SDL_Quit()
の間に入れるのですが、ここでは省略しています。
完全なコードはこのファイルの最後にあります)。
SDL_Surface *screen; screen=SDL_SetVideoMode(640,480,16,SDL_SWSURFACE); if(!screen) { fprintf(stderr,"failed to set video mode to 640x480x16.\n"); SDL_Quit(); exit(1); }
まず
SDL_Surface
というのがありますが、これはメモリ上のグラフィック領域一般 (「サーフェス」) を指します。
ディスプレイに表示されている画面も当然これに入るので、上のように
SDL_Surface *screen;
と宣言しているわけです。
ちなみに、ファイルから読み込んだ画像なども「メモリ上のグラフィック領域」になりますから、やはり
SDL_Surface
になります。
初期化は
SDL_SetVideoMode()
で行います。
SDL_Surface *SDL_SetVideoMode(int width,int height,int bpp,Uint32 flags);
width
と height
はもちろんウィンドウのサイズ。
bpp
というのは bits per pixel の略で、1 ドットあたり何ビット使うか、
つまり log2(色数) になります。
また flags
はオプションをいろいろ指定するのですが、今のところは
SDL_SWSURFACE
だけでいいでしょう
(これは画面領域をシステムメモリ上にとるという意味ですが、あまり気にする必要はありません)。
1 つ便利なオプションを挙げてみるとすれば
SDL_FULLSCREEN
でしょうか。
文字通りフルスクリーンになります。
これは SDL_SWSURFACE|SDL_FULLSCREEN
というようにビット和をとって指定します。
では、用意した画面に BMP を描画してみます。
int draw_bmp(char *filename,SDL_Surface *screen,int x,int y) { SDL_Surface *image; SDL_Rect dest; /* BMP を読み込む */ image=SDL_LoadBMP(filename); if(!image) return -1; /* 画面に描画 */ dest.x=x; dest.y=y; SDL_BlitSurface(image,NULL,screen,&dest); /* 描画した部分の表示を更新 */ SDL_UpdateRect(screen,x,y,image->w,image->h); /* 画像用に確保した領域を解放 */ SDL_FreeSurface(image); return 0; }
画像の読み込み (SDL_LoadBMP()
)
と解放 (SDL_FreeSurface()
)
は特に問題ないと思います。
SDL_UpdateRect()
も screen
のうち転送した部分を指定しているだけですね。
で、残りの
SDL_BlitSurface()
ですが、このような関数です :
int SDL_BlitSurface(SDL_Surface *src,SDL_Rect *srcrect, SDL_Surface *dst,SDL_Rect *dstrect);
これはサーフェスを転送する関数です。
src
・dst
にはそれぞれ転送元 (source)・転送先 (destination) のサーフェスを指定し、srcrect
, dstrect
には各サーフェスのどの部分を転送するかを指定します。
srcrect
, dstrect
は SDL_Rect
という型をもっており、x
, y
,
w
(width), h
(height)
というメンバー変数があります。
つまり、src
の点 (12,34) を左上頂点とする幅 10, 高さ 20 の矩形を
dst
の点 (56,78) へ転送したければ、
SDL_Rect srcrect,dstrect; srcrect.x=12; srcrect.y=34; srcrect.w=10; srcrect.h=20; dstrect.x=56; dstrect.y=78; SDL_BlitSurface(src,&srcrect,dst,&dstrect);
などとするわけです (dstrect
の w
と h
を指定する必要はありません)。
ちなみに src
全体を転送する場合は
srcrect
は NULL
で OK です。
以上をまとめると、次のようなプログラムになります。 とりあえずウィンドウが開いて画像が表示できているはずです。 画像を表示する度にファイルから読み込んでいるという無駄がありますが、そこは暇だったら各自直してみて下さい。
#include <SDL.h> int draw_bmp(char *filename,SDL_Surface *screen,int x,int y) { SDL_Surface *image; SDL_Rect dest; /* BMP を読み込む */ image=SDL_LoadBMP(filename); if(!image) return -1; /* 画面に描画 */ dest.x=x; dest.y=y; SDL_BlitSurface(image,NULL,screen,&dest); /* 描画した部分の表示を更新 */ SDL_UpdateRect(screen,x,y,image->w,image->h); /* 画像用に確保した領域を解放 */ SDL_FreeSurface(image); return 0; } int main(int argc,char *argv[]) { SDL_Surface *screen; /* 初期化 */ if(SDL_Init(SDL_INIT_VIDEO)<0) { fprintf(stderr,"failed to initialize SDL.\n"); return -1; } /* 画面作成 */ screen=SDL_SetVideoMode(640,480,16,SDL_SWSURFACE); if(!screen) { fprintf(stderr,"failed to set video mode to 640x480x16.\n"); SDL_Quit(); return -1; } /* ループ */ while(1) { /* イベント処理 */ SDL_Event event; while(SDL_PollEvent(&event)) if(event.type==SDL_QUIT) goto QUIT; /* BMP 描画 */ draw_bmp("test.bmp",screen,12,34); } /* 終了 */ QUIT: SDL_Quit(); return 0; }