1. Hello, world
Hello, world と言ってもウィンドウを表示するだけのサンプルですが. ウィンドウの扱いを除けば, ほとんど SDL1.2 と変わりません. まずはコード*1.
#include <iostream> #include <SDL.h> int main(int, char**) { // 引数は省略できない. // SDL を初期化する. if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER) == -1) { std::cerr << "SDL_Init failed: " << SDL_GetError() << std::endl; return 0; } // ウィンドウを作成する. SDL_Window* window = SDL_CreateWindow("Hello, World!", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 640, 480, SDL_WINDOW_SHOWN); // イベントループ SDL_Event event; for (bool quit = false; !quit;) { if (SDL_PollEvent(&event)) { switch (event.type) { case SDL_QUIT: quit = true; break; case SDL_WINDOWEVENT: quit = event.window.event == SDL_WINDOWEVENT_CLOSE; break; default: break; } continue; } // ここに各フレームでの処理 (描画など) を書くことになる. SDL_Delay(10); } SDL_Quit(); return 0; }
まず SDL を用いるプログラムでは main() 関数のシグネチャーは (int, char**) 固定です. これは SDL がプラットフォームごとのスタートアップ関数の差を吸収する機能に関わる制限で, SDL1.2 と同じ仕様です. たとえば Windows での GUI プログラムは WinMain() 関数がスタートアップとなりますが, SDL を使う場合にはリンクする SDLmain.lib の中に WinMain() 関数が実装されていて, ユーザーが定義した main() 関数を呼び出すようになっています. その際のシグネチャーが引数ありのものなので, ユーザーが定義する main() 関数も引数付きでないといけないわけです.
デバッグする際に一つ注意しなければならないこととして, SDL.h の中で次のようなマクロ定義がされています.
#define main SDL_main
つまり, 上のようにユーザーが定義した main() 関数は, 実際には SDL_main() という名前の関数になります.
SDL の機能は SDL_Init() 関数を呼び出してから使えるようになります. 引数には初期化したいサブシステムの種類を OR で指定します. これも SDL1.2 と同じですが, SDL1.3 ではサブシステムが増えています. 定義されている種類は SDL_INIT_TIMER, SDL_INIT_AUDIO, SDL_INIT_TIMER, SDL_INIT_JOYSTICK, SDL_INIT_HAPTIC, SDL_INIT_NONPARACHUTE, SDL_INIT_EVENTTHREAD で, これらをすべて有効にした SDL_INIT_EVERYTHING も定義されています. ちなみに現時点で SDL_INIT_EVENTTHREAD は未実装のようです.
ウィンドウの作成は SDL_CreateWindow() で行います. ウィンドウ周りは SDL1.3 で大幅に変更された部分です.
- SDL_Window* SDL_CreateWindow(char const* title, int x, int y, int w, int h, Uint32 flags);
- ウィンドウを作成します. title はキャプションを表し, x, y は SDL_WINDOWPOS_UNDEFINED または SDL_WINDOWPOS_CENTERED を指定します. w, h はウィンドウの解像度です. flags には SDL_WINDOW_FULLSCREEN, SDL_WINDOW_OPENGL, SDL_WINDOW_SHOWN, SDL_WINDOW_BORDERLESS, SDL_WINDOW_RESIZABLE, SDL_WINDOW_MAXIMIZED, SDL_WINDOW_MINIMIZED, SDL_WINDOW_INPUT_GRABBED を OR で指定します (どれも使用しない場合は 0 を渡す). 戻り値は名前の通り, 作成されたウィンドウ構造体へのポインターです *2. 0 の場合はエラーを表します.
SDL1.3 は複数ウィンドウ対応です. SDL1.2 と比べて, ウィンドウの作成や管理, イベントなどがかなり充実しています.
SDL_PollEvent() 関数を呼び出すと, その時点でイベントキューに要素があればそれを取り出しコピーして 1 を返し, 要素が溜まっていなければ 0 を返します. 上のコードでは, 毎フレームイベントキューをすべて消化しています.
イベントループは, 上のコードでは SDL1.2 とあまり変わっていませんが, ウィンドウの×ボタンが押された場合に SDL_QUIT イベントではなく SDL_WINDOWEVENT_CLOSE イベントが発生するようになっています. これは複数ウィンドウを使用する場合に, ×ボタンを押すことがプログラムの終了を表さないケースがあるからでしょう. SDL_WINDOWEVENT イベントを無視すると, ×ボタンから終了できなくなります.
SDL_Delay() は SDL1.2 と同じです. 指定した時間 (ミリ秒) だけ CPU を手放します. Windows でいう Sleep() と同じです (実際 Windows では Sleep() を呼び出します). このいわゆるスリープ処理は, 環境によっては 1 ミリ秒から有効となりますが, 一般的には 10 ミリ秒より短い時間だけスリープすることはできないと思った方がよいようです.
うん, この細かさじゃ書き続けられないな. 多分.