レトロ風テニスゲームの作成
SDKを使ったプログラムの動作確認ができたら、実際に動く簡単なゲームを作成して、SDL.NETの手軽さに触れていただきたいと思います。
そこで今回は、「PONG」というレトロ風テニスゲームをまねて筆者が作った、PONという壁打ちテニスゲームを通じてSDL.NETの各機能を解説します。このプログラム自体、土日の休みに片手間で作っていますから、バグは残っているかもしれませんが、SDL.NETのお手軽さを実感していただく例題としてはもってこいだと思います。
実装に先立って、どのようなゲームを作っていくかを説明します。まずは画面のイメージをご覧ください。

PONは、壁打ちテニスを模したゲームで、次のようなルールを持ちます。
- プレイヤーはキーボードの左右矢印キーでパドルを動かし、ボールを跳ね返します。
- ボールは壁面や側壁に当たると反射します。壁面では当たった時に、ボールの反射角と反射速度が変化します。(側壁では全反射し、速度も変わりません)
- 跳ね返したときのボールの速度によって、スコアが加算されていきます。(ボールの速度が速いほど高得点)
- ボールがミスエリアに入ってしまった時点でゲームオーバーとなり、この時点でのスコアを競います。
ただし、プログラムのすべてを解説するのは無理なので、ここでは特にSDL.NETの扱いに関する部分にスポットを当てて解説していきます。なお、すべてのプログラムに関しては、記事添付のサンプルプロジェクト内に含まれていますので、そちらを参照ください。
プログラムの解説
先ほどの簡単なプログラムに比べて、複数のクラスが混在する複雑なプロジェクトになっていますから、ゲームの流れに沿って、一つずつ重要な部分を解説していきます。
Ponクラス
最初に見ていくのはPonクラスです。このクラスはゲーム全体を管理するマネージャクラスで、画面の初期化処理やイベントの初期化処理のほか、ゲーム画面の更新処理なども請け負っています。
//== ゲーム管理クラスのコンストラクタ
public Pon()
{
//-- 画面周りの初期化
w = 640;
h = 480;
Video.WindowIcon(new Icon("img" + Path.DirectorySeparatorChar + "pon.ico"));
Video.WindowCaption = "PON";
screen = Video.SetVideoMode(Width, Height);
//-- イベントの初期化
Events.TargetFps = 30;
Events.Tick += new EventHandler(this.Tick);
Events.Quit += new EventHandler(this.Quit);
Events.KeyboardDown += new EventHandler(this.KeyboardDown);
//-- シーンの初期化
EnterScene(SCENE.TITLE);
//-- ビジュアル部品の初期化
player = new Paddle();
wall = new Wall();
ball = new Ball();
score = new Score(scene.small_font, this.Width);
}
//== フレーム更新ごとに呼ばれるメソッド
private void Tick(object sender, TickEventArgs e)
{
scene.KeyProcess(null); // キーボードイベントの処理
scene.Draw(screen); // シーンごとの描画処理
screen.Update(); // 画面の更新
}
//== キーボードイベントに対応する処理
void KeyboardDown(object sender, SdlDotNet.Input.KeyboardEventArgs e)
{
//-- シーンに関係なく処理できるキーを処理する
switch (e.Key)
{
case Key.Escape: // ESCキーが押されたらゲームを終了する
Events.QuitApplication();
break;
case Key.Space:
EnterScene(SCENE.TITLE);
break;
default:
scene.KeyProcess(e); // シーンごとのキー処理
break;
}
}
さっそくコンストラクタから見ていきましょう。先ほど、EventsオブジェクトのRunメソッドを呼ぶことでアプリケーションを実行すると書きましたが、その際に「画面の書き込み処理やキーボードの処理はどうするのか?」と疑問に思った方も居ると思います。実はSDL.NETでは、Runメソッド実行後はEventsオブジェクトのTickというイベントが一定時間ごとに発生する約束になっているのです(イベントの初期化部分を参照)。従って、画面描画などのゲームのメインループに当たる処理は、Tickイベントに割り当てたメソッドの中に記述していけばよいということになります。
そして、このTickメソッドを呼び出す間隔は、FPSという数値で制御されています。これは、Frame per secondという、ゲームではよく用いられる指標の一つで、1秒あたり何フレームでゲームが進行するかを表す指標を言います。一定のFPSを実現できないと、なめらかな画面処理を行うことができず、結果としてゲームの進行速度にバラつきが出たり、ある時はなめらかにアニメーションするのに、あるときはカクカク動くと言う不思議なゲームになってしまいます。ゲームプログラミングの経験がある方ならお分かりだと思いますが、通常、ゲームのメインループが処理される回数はCPUやグラフィックボードの処理速度やモニタの性能に依存するため、一定のFPSを出そうとすると、自前で秒数やフレーム数をカウントし、対処する必要があります。しかしSDL.NETでは、EventsのTargetFPSプロパティを操作するだけで、一定のFPSを実現してくれるのです。
今回はこの値を30に設定していますから、1秒間におよそ30回、Tickイベントが呼び出されることになります(およそと書いたのは、どうしても1~2フレーム程度の誤差は出てしまうからです)。もしTargetFPSを設定しなければ、デフォルト値の60FPSが使用されます。
さて、肝心のTickイベントに対応した処理ですが、今回はゲームのメインループ処理は別クラスで行っていますので、Tickメソッドでやることは各シーンでのキーイベントの処理とそれぞれのシーンに応じた画面描画処理だけです。最後にUpdateメソッドを呼び出し、強制的に画面の更新をさせています。
続いてキーボードイベントの処理に目を向けてみます。ここではKeyboardDownイベントを実装しています。このイベントに対応するメソッドには、押されたキーに対応した処理を書いていきます。しかしゲームでよく利用される、「右キーを押しっぱなしにして右方向にキャラクターを移動する」ような処理には、このイベントは向いていません。なぜなら、いったんキーが離されないと再度KeyboardDownイベントが発生しないからです。このことへの対処法については後編で解説します。従ってPonクラスのKeyboardDownイベントには、[ESC]キーでプログラムを終了したり、スペースキーでタイトル画面に戻ったりといった、連続発生させる必要のないキーボード処理だけを書いています。
以上がPonクラスで使われているSDL.NETの各機能です。これだけですと一見貧弱に見えますが、十分にゲームをマネジメントするだけの機能を実装できています。
まとめ
今回は前編として、SDL.NETの導入から、Coreライブラリを中心とした部分について、シンプルなゲームプログラムを通じて解説しました。次回は後編として、今回解説しきれなかった画面表示の部分やキーボード入力処理、サウンド関連の事柄について解説を行いたいと思います。
参考資料
記事を書くにあたって、以下の資料を参考にしました。
- SDL.NET付属ドキュメント
- SDL.NET 公式サイト
- MSDN Online
また、効果音素材はザ・マッチメイカァズさんからお借りました。
