Android携帯ゲームを作って世界に配信してみよう(2) ―― ゲームの基本構造とタップやフリックの処理を学ぶ
それでは,処理スレッド内の生成から破棄までの具体的な流れを説明します.処理スレッドの実態は,Runnableインターフェースを継承したGameViewクラスのrunメソッドに記述されています(リスト3).
リスト3 処理スレッド内の流れ(GameView.javaより抜粋)

スレッド内で処理を繰り返し行うためのwhileループは,スレッドへの中断要求があるまで繰り返されます(リスト3の(a)).繰り返し条件のthread.isInterruptedメソッド(ThreadクラスのisInterruptedメソッド.以下,Thread#isInterruptedと表記する)は,このスレッドに対して,ほかのスレッドから中断要求があった場合(割り込まれた場合)にtrueを返します.つまり,画面が破棄されてGameView.javaのsurfaceDestroyedメソッド内からThread#interruptメソッドによりスレッドの中断が要求されると,Thread#isInterruptedがtrueを返し,ループの繰り返し条件がfalseとなって,処理ループを抜け出します.
また,フレームの処理を開始する前に,フレームの処理開始時の時間(ms)を記録します(リスト3の(b)).1フレームの処理がすべて終わった後に,フレームの実処理時間(現在の時間-開始時間)を算出し,処理が早すぎた場合は想定されたフレーム・レートになるようにスレッドをスリープして(Thread#sleepメソッドを使用して)待機処理を行います(リスト3の(c)).今回のサンプル・コードの場合は,処理が早すぎても問題は起こりませんが,処理に想定外の時間がかかった場合はコマ落ちすることになります.
Thread#sleepメソッドにてスレッドがスリープ中に,中断要求された場合は,「InterruptedException」という例外が発生します(リスト3の(d)).この例外をキャッチして,強制的にループを抜け出します.
なお,お作法として,ループを抜け出してスレッドを完了する前に,本スレッドへの参照をnullでクリアして,ガーベッジ・コレクションされるようにします(リスト3の(e)).
●描画処理とダブル・バッファリング
第1回の記事では,描画処理が必要なタイミングでView#onDrawメソッドが呼び出され,onDrawメソッド内に描画処理を記述しました.今回利用するSurfaceViewでは,任意のタイミングで描画処理を行うことができます.また,あらかじめダブル・バッファリングするためのしくみが用意されています.
ここでダブル・バッファリング(ダブル・バッファ)について簡単に説明します.一般的に,ゲームで描画処理を行う際は,まず前回描画したキャラクタや背景を,背景色で塗りつぶして消去してから,少しずつ描画位置を変えたキャラクタや背景を新しく描画します.ただし,単純に「消して」,「新しく描画する」という処理を行うと,画面がちらついてしまいます.なぜなら,塗りつぶして消去してから,その上にキャラクタや背景を描画するまでの間の「何も表示していない画面」が一瞬見えてしまうからです.
この現象を防止するには,オフスクリーン・バッファ(裏画面)という,ユーザに見えない二つめの画面を用意します.画面の消去処理やキャラクタ,背景などの描画処理はオフスクリーン・バッファに行い,描画処理がすべて完了すると,オフスクリーン・バッファをオンスクリーン・バッファ(表画面)というユーザが見える画面に切り替えます.今までのオンスクリーン・バッファはオフスクリーン・バッファになり,次のタイミングでは,こちらのバッファに描画を行います.
描画する際には,まず#lockCanvasメソッドにて,描画対象のキャンバスを取得します(リスト3の(f)).取得したキャンバスはオフスクリーン・バッファになります.#lockCanvasメソッドは名前の通り,Canvasを取得するだけではなく,スレッド間で競合しないようにほかのスレッドからのCanvasリソースの取得要求をロックします.取得したスレッドが手動でアンロックして解放するまでは,他のリソースが#lockCanvasメソッドを呼び出しても,ロックされて待機することになります.複数のスレッド間で利用する場合は,複数のロックが競合してデッドロックにならないように注意してください.今回は,ゲーム・ループ専用のスレッドのみですべての処理を行いますので,デッドロックを心配する必要はありません.
正しくCanvasが取得(!= null)できたら(リスト3の(g)),必要な描画処理を行ってください.描画処理が完了したら#unlockCanvasAndPostメソッドを呼び出してCanvasのロックを解放し,現在のオンスクリーン・バッファをオフスクリーン(裏画面)に,先ほど描画したオフスクリーン・バッファをオンスクリーン(表画面)にします(リスト3の(h)).