幾年もの開発ののち, ついにSDL 2.0がリリースされた!
私たちはその出来に満足し, SDL 1.2を使ったゲームはすぐに移行することを望んでいる. それは面倒だと思うかもしれない. この文書には新しいライブラリへの移行の方法を簡潔に書かれている. それは考えているよりは難しくないとわかるはずだ. 同等の関数に置き換えるか, 1.2の問題を避けるための工夫を行わないようにするだけの場合もあるだろう.
SDL 2.0は, 新しい機能とSDL 1.2の経験が生かせる満足のいくものだと私たちは考えている. この文書はSDL2の全ての機能を扱うことを目指してはいない. それは多すぎるからだ. しかし, すぐに動かすために必要なものはそろっているはずだ. 既にコードを移行したならば, 新しい項目をチェックすべきだ. あなたのアプリケーションで新しい機能を使いたくなるだろう.
SDL2への互換レイヤーはない. 2.0のAPIに変更するならば, つじつまが合うように古い関数を変更や削除する必要がある. 単に1.2のプログラムに2.0のヘッダをインクルードしたならば, 恐らくコンパイルに失敗するだろう. この文書では最も大きな変更点とつまずきやすい点を説明する.
SDL_mainは存在しない! …たしかにWindowsではmain()とWinMain()の差を隠すための小さなコード片は存在する. しかし, その中には初期化のためのコードはなく, 必ずしも使わなくてもよい. それはmainを乗っ取ることなくSDLが使えることを意味し, SDLを使ったプラグインや, スクリプト言語をSDLモジュールと共に使う場合に有利である. 1.2のSDL_mainで行っていたことは, 今では全てSDL_Init()で行っている.
SDL parachuteは, もはや存在しない. 1.2でSDL_INIT_NOPARACHUTEと呼ばれていたものがデフォルトかつ唯一となった. これはメインスレッド以外がクラッシュしたとき問題を引き起こしていた. そしてアプリケーションが独自に設定したシグナル/例外ハンドラを阻害していた. さらに, 一部の環境ではクラッシュ時にフルスクリーンの後処理が行われていなかった. あなたは自分自身でクラッシュハンドラを導入するか, atexit()関数でSDL_Quit()やそれと同等の関数を呼ぶ必要がある. なお, Unix環境では, SDLは今でもSIGINTを捕らえSQL_QUITイベントに変換しているので注意すること.
1.2から最も劇的に変化したのはビデオAPIである. SDLのAPIが設計された1990年代の終わりから状況は大きく変化した. 現在のハードウェアとOSの機能を扱うため, 古い1.2のビデオAPIの大半は完全に置き換えられた.
心配することはない. 新しいAPIは強力で, 一度変化を理解すれば, 新しい仕様のAPIを1.2のゲームに組み込めるはずだ. その方法は後で説明する.
幸いなことに, OpenGLを使っているならば, すべきことは多くない. 少数の関数をSDL2の等価なものに置き換えれば十分である.
2Dグラフィックは, SDL1.2では「サーフェイス」と呼ばれるピクセルのメモリバッファが提供されていた. スクリーンそれ自体も「サーフェイス」で, 2Dレンダリングがソフトウェアで行われた. そして, サーフェイス間のコピー(転送 blit)を行う関数が提供され, 必要ならばサーフェイス間のピクセルの形式が変換されていた. それらはビデオRAMとGPUではなく, ほぼ全てシステムRAMとCPUで行われていた. SDL 2.0ではこれが変わった. ほぼ全てがハードウェアアクセラレーションで行われ, APIもそれを反映したものになった.
2Dゲームならば, 3つのレンダリングの方法の1つに変更することになる. それらの方法について述べるが, まず基本的なことを説明する.
SDL_SetVideoMode()を覚えているだろうか? それは完全に廃止された. SDL 2.0では複数のウィンドウが使える. そのためこの古い関数はもはや無意味である.
よって, このような書き方は...
SDL_WM_SetCaption("My Game Window", "game");
SDL_Surface *screen = SDL_SetVideoMode(640, 480, 0, SDL_FULLSCREEN | SDL_OPENGL);
このようになる
SDL_Window *screen = SDL_CreateWindow("My Game Window",
SDL_WINDOWPOS_UNDEFINED,
SDL_WINDOWPOS_UNDEFINED,
640, 480,
SDL_WINDOW_FULLSCREEN | SDL_WINDOW_OPENGL);
この置き換えは1.2とよく似ていることがわかるだろう. 変わったのは, (必要ならば)複数のウィンドウが使えるようになったことと, より詳細に設定できるようになったことである. SDL_WM_SetCaption()も廃止された. それぞれのウィンドウがそれぞれのタイトルを持つようになったためである. (後でSDL_SetWindowTitle()で変更することもできる.) さらに, ウィンドウの位置も設定できるようになった. (SDL_WINDOWPOS_UNDEFINEDを使えばシステムの決めた位置になる. SDL_WINDOWPOS_CENTEREDを使うのもよい選択である.)
ユーザがウィンドウを表示するディスプレイを指示することもできる: SDL2はマルチモニタも扱える. しかし, 今ここでは説明しない.
これでウィンドウを画面に表示できるようになった. ここからはその扱いについて述べる. SDL2にもSDL_Surfaceが存在する. しかし, 使えるならば, 本当に欲しいのは新しいSDL_Textureのはずだ. SDL_Surfaceでは常にシステムのRAMを使い, そして常にCPUで操作される. その方法は取りたくないだろう. SDL2には新しいレンダリングAPIがある. これはシンプルな2Dゲームでの使用を意図している. しかし, 最も注目すべきなのは, ソフトウェアレンダリングからビデオRAMとGPUに移行したことである. そして, たとえソフトウェアレンダリングの結果を画面に転送するためだけに使う場合でも利点はある: もし可能ならばOpenGLかDirect3Dが使われる. それにより高速に転送され, Steam Overlayが有効になり, 自由に拡大縮小できるようになる.
初期設定は以下のようになる.
SDL_SetVideoMode()は上で述べたようにSDL_CreateWindow()になった. しかし, 解像度はどのように設定すればよいのだろうか? 例えば, あなたのゲームが640×480でハードコードされていれば, 現在のモニタではフルスクリーンの解像度として設定できないかもしれない. そしてウィンドウモードならば, ハイエンドのモニタならばあなたのゲームは動く郵便切手のように見えてしまうだろう. SDL2ではよりよい解法がある.
もうSDL_ListModes()を呼ぶ必要はない. 同様の関数はSDL2にもある. (ループの中でSDL_GetDisplayMode()をSDL_GetNumDisplayModes()回呼ぶ.) しかし, 代わりに新しい仕様「フルスクリーンデスクトップ」を使うことができる. これはSDLに「画面全体を要求するが解像度は変えない」ことを指示するものである. 例えば640×480のゲームならば, 次のようになる:
SDL_Window *sdlWindow = SDL_CreateWindow(title,
SDL_WINDOWPOS_UNDEFINED,
SDL_WINDOWPOS_UNDEFINED,
0, 0,
SDL_WINDOW_FULLSCREEN_DESKTOP);
640×480を指定しないことに注意すること...フルスクリーンデスクトップは画面全体のサイズになり, 指定したサイズは無視される. ゲームウィンドウはクリックして新しい解像度になるのを待たずにすぐに現れる. そして, デスクトップのサイズに拡大縮小するためにGPUが使われる. それはLCDの解像度を低く偽装するよりも高速で鮮明になる傾向にある. さらに他のウィンドウの大きさが変わることはない.
次はレンダリングコンテキストが必要になる.
SDL_Renderer *renderer = SDL_CreateRenderer(sdlWindow, -1, 0);
SDL_Rendererによってウィンドウへの描画の方法は隠蔽される. Direct3D, OpenGL, OpenGL ES, またはソフトウェアでレンダリングされるが, たとえSDLがどれを選んでもユーザはコードを同じように書ける. (ユーザがレンダリングの方法を強制的に決めることもできる.) ティアリング(tearing, 描画の最中にフレームが替わることで発生するちらつき)を軽減するために, 垂直同期を取る(sync-to-vblank)ことを強制したいならば, 3番目の引数の0をSDL_RENDERER_PRESENTVSYNCに変更すること. ウィンドウの生成時にSDL_WINDOW_OPENGLフラグを指定してはならない. もしSDL_CreateRenderer()がOpenGLを使うと決めたならば, そのときウィンドウはそれに合わせて更新される.
その働きが理解できたならば,
SDL_CreateWindowAndRenderer()で一まとめにすることもできる:
SDL_Window *sdlWindow;
SDL_Renderer *sdlRenderer;
SDL_CreateWindowAndRenderer(0, 0, SDL_WINDOW_FULLSCREEN_DESKTOP, &sdlWindow, &sdlRenderer);
これらの関数が正常に実行されれば画面への描画の準備は整った.
まず画面を黒で消去する.
SDL_SetRenderDrawColor(sdlRenderer, 0, 0, 0, 255);
SDL_RenderClear(sdlRenderer);
SDL_RenderPresent(sdlRenderer);
この動作は予想できるだろう: 描画を黒にする(赤, 緑, 青が全て0, αは最大値), ウィンドウ全体をクリアする, クリアしたウィンドウを画面に表示する. 画面の更新のためにSDL_UpdateRect()やSDL_Flip()を使ったことがあるかもしれないが, レンダラーAPIではSDL_RenderPresent()が使われる.
初期化で必要なことはまだある. SDL_WINDOW_FULLSCREEN_DESKTOPを指定したならば, 描画すべき画面の実際の大きさがわからない. だが, 幸いなことにそれは知る必要がない. 1.2では, 例えば640×480を要求してもその通りになるとは限らず, 高い解像度の画面の中央に小さく画面が表示されることもあった.
2.0ではレンダリングAPIを使うとこのようになる...
SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear"); // 拡大縮小が滑らかになる
SDL_RenderSetLogicalSize(sdlRenderer, 640, 480);
...そして, このようにすべきた. この方法は論理的なレンダリングサイズを様々な目的に合わせて変えられる点で良い方法である. しかし, 最も重要なのは, レンダリングサイズにシステムを合わせようとする代わりに, レンダリングサイズをシステムに合わせるようになったことである. 1920x1200のモニタで640×480のアプリケーションを動かしても, SDLはGPUを使って拡大し画面全体に表示する. 640×480と1920×1200では縦横比が異なるが, SDLはその点も考慮しており, 可能な限り拡大した後に黒帯を追加(letterboxing)する.
ここから本当の描画が始まる.
古いゲームには, アプリケーションが全てのピクセルを描画し, 最後にその結果を画面に転送するものがある. 例えば, Doom, Duke Nukem 3Dなどがそうである.
この場合は, 1つのSDL_Textureが画面を表すようにする.
640×480のゲーム用に生成してみよう:
sdlTexture = SDL_CreateTexture(sdlRenderer,
SDL_PIXELFORMAT_ARGB8888,
SDL_TEXTUREACCESS_STREAMING,
640, 480);
これはGPU上のテクスチャーを表している. このテクスチャのピクセルの更新を完了した後に, ウィンドウにテクスチャを描画し, 描画したものと画面を入れ替える. SDL_TEXTUREACCESS_STREAMINGはSDLにテクスチャのコンテキストが周期的に変わることを知らせている.
以前はSDL_Surfaceに描画し, それからSDL_Flip()で画面に表示していただろう.
現在では, ピクセルの書き込み先として, RAMにSDL_Surfaceを生成するだけでなく, 単にmalloc()で確保したブロックを使うこともできる.
理屈の上ではバッファにRGBAピクセルを書き込むことはできる. しかし, 変換が必要ならば次の方法でもよい.
extern Uint32 *myPixels; // surface->pixels や malloc()で確保したバッファなど
フレームの最後で次のようにテクスチャーを更新する:
SDL_UpdateTexture(sdlTexture, NULL, myPixels, 640 * sizeof (Uint32));
これでピクセルがGPUメモリへと転送される. 転送する領域としてNULLの部分にはみ出した領域を指定しても, 現在のハードウェアならば問題なくフレームバッファ内に収まるようになるはずだ. 最後の引数はピッチ(ある行から次の行までのバイト数)だが, この例ではリニアなRGBAバッファなので, 単に640(幅)×4(R,G,B,A)である.
これからテクスチャを画面に表示する:
SDL_RenderClear(sdlRenderer);
SDL_RenderCopy(sdlRenderer, sdlTexture, NULL, NULL);
SDL_RenderPresent(sdlRenderer);
これだけだ. SDL_RenderClear()は現在のビデオフレームバッファ(最後にSteam Overlayが書き込んだものも含む)を消去する. SDL_RenderCopy()はテクスチャのコンテキストをビデオフレームバッファにコピーする(さらにSDL_RenderSetLogicalSize()によって中央で拡大縮小される). そして, SDL_RenderPresent()は画面に表示する.
ここでは, あなたのSDL1.2のゲームが多くのグラフィックをディスクからSDL_Surfaceに読み込んでいる場合について扱う. もしかするとSDL_HWSURFACEを指定してビデオRAM上に確保しようとしているかもしれない. 読み込むのは一度で, 必要があるたびに何度もフレームバッファに転送するが, 変更されることはない. シンプルな2Dゲームはそうなっているはずだ. サーフェイスを「スプライト」と考えていて, バッファにピクセルを書き込まないならば, この項目はあなたに合っている.
テクスチャ(GPUメモリ上のサーフェイス)を個々に生成する方法は, 大きな1つのテクスチャを生成するのと同じである:
sdlTexture = SDL_CreateTexture(sdlRenderer,
SDL_PIXELFORMAT_ARGB8888,
SDL_TEXTUREACCESS_STATIC,
myWidth, myHeight);
こうすれば期待した通りになるだろう.
SDL_TEXTUREACCESS_STATICを使うのは, 転送されるのは一度だけだからである.
しかし, もっと便利な方法がある:
sdlTexture = SDL_CreateTextureFromSurface(sdlRenderer, mySurface);
この方法の場合, 読み込みは今まで通りSDL_Surfaceに対して行うが, 最後にそれをテクスチャに変換することになる. 一度SDL_Textureに変換すれば, 元のサーフェイスは解放してよい.
1.2では, 多数のSDL_Surfaceを確保し, それをSDL_BlitSurface()で画面のサーフェイスに転送してフレームバッファを構成し, 最後にSDL_Flip()で画面を更新していた. SDL 2.0では, 多数のSDL_Textureを確保し, それをSDL_RenderCopy()でレンダラーに転送してフレームバッファを構成し, 最後にSDL_RenderPresent()で画面を更新する. これは単純である. これらのテクスチャを変更しないならば, 高速に描画できるだろう.
フレームバッファへのサーフェイスの転送と, 個々のピクセルの変更の両方を行いたい場合は, やや複雑になる. Round trips(テクスチャからのデータの読み戻し)は非常に高コストである. 一般的にはデータは常に一方通行が望ましい. 恐らくこの場合最も良いのは, 最終的に画面に転送するまで全てソフトウェア内に止めておいて, 前の2つの方法を組み合わせる方法である.
幸いなことに1.2のSDL_Surface APIはほとんど使える. よって画面のサーフェイスを, これから...
SDL_Surface *screen = SDL_SetVideoMode(640, 480, 32, 0);
...このようにする...
// 16進数で書きたくなければSDL_PixelFormatEnumToMasks()を使うこと!
SDL_Surface *screen = SDL_CreateRGBSurface(0, 640, 480, 32,
0x00FF0000,
0x0000FF00,
0x000000FF,
0xFF000000);
SDL_Texture *sdlTexture = SDL_CreateTexture(sdlRenderer,
SDL_PIXELFORMAT_ARGB8888,
SDL_TEXTUREACCESS_STREAMING,
640, 480);
...そして前の方法で転送とピクセルの変更を行い, このSDL_Surfaceのフレームバッファを構築する.
一度画面のピクセルを得る準備をすれば, 最初の方法と同じようにすればよい.
SDL_UpdateTexture(sdlTexture, NULL, screen->pixels, screen->pitch);
SDL_RenderClear(sdlRenderer);
SDL_RenderCopy(sdlRenderer, sdlTexture, NULL, NULL);
SDL_RenderPresent(sdlRenderer);
テクスチャの生成は高価で資源が限られているので注意すること: SDL_CreateTextureFromSurface()を毎フレーム呼んではならない. 初期化でサーフェイスからテクスチャへの変換を一度だけ行うこと.
レンダラーAPIにはもっと多くの機能があり, あなたのアプリケーションのコード: 拡大縮小, 線分の描画などと置き換えられるものもあるだろう.その機能を使えば, 個々のピクセルの操作を止めて全てGPU上に移すことができ, あなたのプログラムを高速で単純にできるかもしれない.
いくつかの単純な効果はピクセルを直接操作しなくてもレンダラーAPIで実現できる. 以下の内の一部は1.2のサーフェイスでも使える.
OpenGLを直接つかっているならば移行は簡単である. SDL_SetVideoMode()をSDL_CreateWindow()とSDL_GL_CreateContext()に置き換える. そして, SDL_GL_SwapBuffers()をSDL_GL_SwapWindow(window)に置き換える. これらのGLの呼び出しは全て等価である.
もしSDL_GL_SetAttribute(SDL_GL_SWAP_CONTROL, x)を呼んでいるならば, これも置き換える. それはSDL_GL_SetSwapInterval(x)になった. これで存在するGLコンテキストを変更できる.
SDL 2.0はウィンドウ/フルスクリーンを切り替えられ, OpenGLウィンドウに戻ってもGLコンテキストは失われない(すばらしい!). これはSDL_SetWindowFullscreen()で実現できる.
よい知らせはSDL 2.0ではUnicode入力ができるようになったこと, 悪い知らせはこの変更のためにあなたのプログラムを少し修正しなければならないことである.
1.2では, SDL_EnableUNICODE(1)を呼んでも, キーに関連付けられた文字を得るだけだった, 多くのアプリケーションは英語しか対応できなかった. これは英語圏の外ではうまく行かず, そしてアジア言語ではまったくうまく行かない.
国際化は困難であった.
2.0ではここが変わった. SDL_EnableUNICODE()は廃止され, SDL_KeysymはUnicodeのフィールドになった. もはやSDL_KEYDOWNイベントからは文字入力は得られない. 現在ではSDL_KEYDOWNは101個のボタンのあるジョイスティックのように扱われる. テキスト入力は別の方法で行う.
新しくSDL_TEXTINPUTイベントが追加された. これはユーザが新しいテキストを入力するたびに発生する. このテキストはキーの押下とIME(これは複雑な複数のテキストを入力する有効な方法である)の両方で発生するので注意すること. このイベントは入力された1文字または複数文字の文字列を戻す. この文字列は常にUTF-8である.
もし実際に押したキーを扱いたいならば, 今でもSDL_KEYDOWNが使える. しかし, このシステムは1.2の頃から2つ: キーコードとスキャンコードに分かれた.
スキャンコードはキーボードレイアウトに依存しない. これは, 実際にはヨーロッパのキーボードでもDvorakキーボードでも, 「ユーザはアメリカのQWERTYキーボードの"Q"の位置にあるキーを押した」として扱う. スキャンコードは常にキーの物理的な位置を扱う.
キーコードはキーボードレイアウトに依存する. これは「ユーザは"Q"と書かれたキーを押した」として扱う.
例えば, アメリカのQWERTYキーボードのCAPS LOCKキーから右2つ目のキーを押したとする. スキャンコードがSDL_SCANCODE_Sで, キーコードはSDLK_Sとなるだろう. Dvorakキーボードの同じキーならば, スキャンコードはSDL_SCANCODE_Sで, キーコードはSDLK_Oとなる.
キーコードとスキャンコードは現在では32bitになったので注意すること. SDLK_LASTはなくなった. SDLキーとあなたのアプリケーション内部で必要なものを対応付けるために, プログラムで要素数がSDLK_LAST個のテーブルを使っているならば, それはもはや正しく動作しない. 代わりにハッシュテーブルを使うべきだ. std::mapを使えば可能だろう. スキャンコードをキーコードの代わりに使うならば, SDL_NUM_SCANCODESを配列の要素数として使うことができる. その値は現在は512である.
SDLModはSDL_Keymodになり, METAキー(Windowsキー)はGUIキーと呼ばれるようになった.
SDL_GetKeyState()の名称はSDL_GetKeyboardState()に変更された. 戻り値の配列の順序(添え字)は, SDL_KeycodeからSDL_SCANCODE_*になった.
次はマウス入力である.
まず, マウスホイールはボタンとして扱われなくなった. これは間違いだった. SDL 2.0では適切に扱われる. SDL_MOUSEWHEELを参照すること. 垂直, 水平両方のホイールに対応している. そして一部の環境にあるトラックパッドの2本指スクロールもホイール入力として扱われる. マウスホイールの操作をSDL_BUTTONDOWNイベントとして受け取ることはもうない. 4, 5番目のボタンも実際のマウスのボタンとして扱われる.
もしあなたのゲームがマウスをある方向へずっと動かす必要がある,
例えばFPSでマウスカーソルが画面の外に出てしまうとプレイヤーが向きを変えられなくなるのを防ぐならば,
マウスカーソルを隠して入力グラブしていたかもしれない:
SDL_ShowCursor(0);
SDL_WM_GrabInput(SDL_GRAB_ON);
SDL2ではこれは少し変わった. このようにして...
後はSDLに任せればよい.
SDL_SetRelativeMouseMode(SDL_TRUE);
SDL_PushEvent()の成功時の戻り値は0から1になった.
イベントマスクは範囲を指定できるようになった:
これはこのようになる:
SDL_PeepEvents(&event, 1, SDL_GETEVENT, SDL_EVENTMASK(SDL_MOUSEBUTTONDOWN));
SDL_PeepEvents(&event, 1, SDL_GETEVENT, SDL_MOUSEBUTTONDOWN, SDL_MOUSEBUTTONDOWN);
幸いなことに, 1.2とは一点を除き後方互換性がある. 新しい機能を使いたいならばそれもできる. しかし, 恐らく変更なしにコンパイルし実行したいだろう.
重要な唯一の違い: オーディオコールバックはバッファが完全に初期化された状態から始まらない.
よってバッファには全て書き込まなければならない.
もし音声が足りなければ, コールバックで無音を書き込む必要がある.
そうしないと, 同じ音の繰り返しや雑音が発生する.
もし以前と同じように無条件に初期化したいならば, ただ SDL_memset(stream, 0, len)
をコールバックの先頭に挿入すればよい.
ジョイスティックイベントはSDL_JoystickIDを持つようになった. これによりSDL 2.0ではジョイスティックのゲーム中の抜き差しを扱えるようになった. 1.2で使っていたデバイス一覧の番号はデバイス一覧が変わると無効になっていた.
SDL_JoystickIDはオープンしたSDL_Joystick*から次の呼び出しで得られる:
SDL_JoystickID myID = SDL_JoystickInstanceID(myOpenedStick);
そして, ジョイスティックのイベントのwhichフィールドとmyIDを比較すれば識別できる. ジョイスティックイベントを使いたくないならば, SDL_JoystickGetAxis()とその仲間がSDL 1.2のように使える.
新しいゲームコントローラーAPIもチェックすべきだ. それはすばらしいものだ. そして, 1.2APIで行っていた様々な工夫を, 新しいコードではより明快に解決できるかもしれない. それらはSDL_gamecontroller.hにある. ゲームコントローラーAPIはSteamのBig Pictureモードに適している: ほとんどのコントローラーを自動的に設定でき, そして手動で設定したい場合にはすばらしいユーザインターフェースがある. どちらの場合でも, SteamはあなたのSDLアプリケーションに設定を提供する.
SDL_KillThread()は廃止された. それは安全でなく, 信用できるものではなかった. 最もよい書き換えは, フラグを設定しスレッドに終了を伝える方法である. スレッドはフラグを周期的に確認し, "kill"するスレッドがSDL_WaitThread()を呼んで後処理を行う.
SDL_CreateThread()にはスレッドの名前の引数が追加された. これはデバッグ時の識別に使える. 使わないのであれば, 追加の引数をNULLにすればよい.
1.2のCD APIは全て廃止された. 置き換えは存在しない. SDL_mixerを使えばOgg Vorbisや他のオーディオファイル形式の音楽を再生できる.
OS/2やMac OS 9などの古い環境は切り捨てられた. 対応の環境は次の通りである: Windows(XP以降), Linux, Mac OS X, iOS, Android. HaikuやSony PSPでは動作するが, 重視されておらず対応は完全ではない. 誰かがパッチを送れば対応環境は追加されるだろうが, この新しいバージョンへの移行が古い友に別れを告げるときだと考えている.
ずっと前からSDL 1.2の非公式なiOSとAndroid版は存在していた. 現在ではSDLはこれらの環境に正式に対応している. そして, 2.0 APIはこれらによく適合している. ほとんどのアドバイスはこの文書の他の部分に書かれているが, いくつか注意すべきことがある.
最初に, モバイル端末のみのイベントがいくつか存在する. 言い換えると, iPhone後の世界のモバイル端末のOSが行うような方法に対応している. 私たちは最初は既に存在するSDLイベントに対応付けようとした(例えば「アプリケーションが背後へ移った」をデスクトップアプリケーションがフォーカスを失ったことにする等). しかし, 実際にはそれよりも緊急の問題である: 多くの場合これらのイベントにはすぐに反応しなければならない. そしてそうしなければOSはアプリケーションを終了させてしまう.
よって, 私たちは一部のAndroidとiOS固有のSDLイベントを追加した. それらはSDLイベントフィルタを設定してOSが報告するとすぐに捕らえられるようにすべきである. なぜならば, 次のSDL_PollEvent()まで待つと遅すぎるからである.
例えば, iOSのapplicationWillResignActive()に対応したSDL_APP_WILLENTERBACKGROUNDがあるが, もしこのイベントを受け取った後に画面に描画すると, iOSはあなたのプロセスを終了させてしまう.
よって, これはすぐに捕らえなければならない:
int SDLCALL myEventFilter(void *userdata, SDL_Event * event)
{
if (event->type == SDL_APP_WILLENTERBACKGROUND) {
// ここで資源を解放する. 前面に戻るまで何も描画してはならない!
}
// その他の処理
return 1;
}
// 初期化処理...
// イベントが発生するとすぐにmyEventFilter(data, event)を呼ぶようにする
SDL_AddEventWatch(myEventFilter, data);
次に, マウス入力イベントへの変換だけでなく, 実際のタッチイベントも存在する. タッチの追跡やマルチタッチ, 複雑なジェスチャーも行える. 望むならばこれらを使うことができる. これらの関数はSDL_touch.hを参照し, SDL_events.h内のSDL_Finger*を見ること.
SDLは単純なタッチをマウスイベントに対応付けている(この場合, イベントのwhichフィールドにSDL_TOUCH_MOUSEIDが設定される)ことに注意すること. これは, もし複雑なタッチインターフェースを扱わないならば, 既存のデスクトップアプリケーションをそのままスマートフォンの画面で指を使って操作できることを意味する. したがって, モバイル専用アプリケーションはSDL_TOUCH_MOUSEIDイベントを無視したほうがよいかもしれない. しかし, それでもタッチイベントに加え「本物の」マウスイベントも考慮する必要がある―― 一部のモバイルデバイスはUSBやBluetoothマウスに対応しているからだ. 結局どうすればよいのだろう!――これはSDL2へ移行した後に, アプリケーションを改良するためによく考えるべきことだ.
そのほかにも, スクリーンキーボードを表示するSDL_StartTextInput()の様な, モバイル環境に適した関数がある. これらも活用すること.
付け加えると, AndroidとiOS固有の関数もあり, 一般的なAPIでは操作できない環境固有の機能を操作できる. それらの関数はSDL_system.hを参照すること.
SDL_RWread()とSDL_RWwrite()はエラーのとき-1ではなく0を戻すようになった.
自分自身でSDL_RWopsを実装するならば, 関数のシグニチャを変えなければならない. 大きなファイルを扱うために, 関数の型はintからSint64とsize_tになった. 多くの場合, シグニチャを変えれば以前と同じように動くが, もしこれらの制限のために処理をあきらめていたならば, よい解決策になるだろう. 呼び出しているアプリケーションは変更された戻り値にあわせる必要がある.
RWopsにもsizeメソッドが存在するようになった. それはSDL_RWsize()の呼び出しである. これは, RWopsにストリームのサイズをアプリケーションに0byteから最後までシークさせることなくサイズを報告するものである. 言い換えると, シークせずにストリームの全サイズを得られるようになった. dこれを行えないストリームならば-1を戻す.
公式の拡張ライブラリであるSDL_image, SDL_ttf, SDL_mixer, SDL_netはSDL 2.0に対応し, SDL2_image, SDL2_ttf, SDL2_mixer, SDL2_netとなった. 必要ならばmercurialリポジトリから最新版をダウンロードできる. もちろんあなたのプログラムをmakeするときは, リンクをSDL_imageではなくSDL2_imageに変更する必要がある.
これらのライブラリは以降は1.2をサポートしない. そして1.2との互換性は新しいバージョンのどの時点かで失われるだろう.
SDL_gfxも2.0.21(2010年5月)以降は2.0でコンパイルできる.
SDL 2.0には, 新しく興味深い膨大な1.2にはなかった機能がある.
ここでは1.2のプログラムを2.0で実行できるようにする説明のみを行ったが,
望むものがあるが, 現在でもできるのか, しなくてもよくなったのかをドキュメントを調べるべきである.
例えば, 全てのゲームのこのようなコードは, メッセージボックス関数に置き換えられる:
この場合はSDL_ShowSimpleMessageBox()が使えるようになった.
#if USING_SDL
fprintf(stderr, "MSGBOX: %s¥n%s¥n", title, text); // ああ, ううむ
#endif
最初に戻って新しい機能の概要をチェックしてみよう!