さまざまな言語に対応するゲームプラットフォーム「Simple DirectMedia Layer」数々の3Dゲームで利用されるゲームエンジンゲーム開発の世界では、他のプログラミング分野に比べて、ごく初期の段階からプラットフォームを正しく特定しておくことが特に重要です。Windows、Linux、およびOS Xをすべてサポートしたいとお考えですか?実際、ゲームプラットフォームの草分けとして、1996年にOpenGL向けに開発された革命的なゲーム「Quake」を引き合いに出す人も多いでしょう。しかし、Quake並みのゲームプレイ水準に達するためには、第一級のオーディオサポート、ネットワーク接続、ユーザー入力デバイスサポート、リアルタイム管理機能なども必要になります。 この2つの課題、つまりクロスプラットフォームサポートとゲームの面白さの両方を追及するために役立つソリューションがSimple DirectMedia Layer(SDL)です。これは、オーディオ、キーボード、マウス、ジョイスティック、OpenGL、および2Dビデオフレームバッファに対する低レベルのアクセスを可能にする、クロスプラットフォームのマルチメディアライブラリです。 SDLは、Linux、Windows、各種のMacOS、WinCE、Dreamcastなど、およそ考え得るほぼすべてのプラットフォームをサポートしています。SDLはGNU LGPL v2で配布されているため、提供されるDLLにリンクしさえすれば、商用プログラムでも自由に利用できます。SDLはMPEGプレイヤーやハードウェアエミュレータのほか、数多くの人気ゲームでも利用されています(有名なLinux用の「Civilization: Call To Power」もその1つです)。 SDLはCで書かれていますが、C++でもネイティブに動作し、他の言語にも対応しています(Ada、Eiffel、Java、Lua、ML、Perl、PHP、Pike、Python、Rubyなど)。 SDLの活躍範囲は幅広く、私の大好きなオープンソースのフライトシミュレータ「GL-117」でも、SDLがエンジンとして使われています。現在、SDLのホームページには、SDLエンジンを利用しているゲームが568本も登録されています(うち450本はWin32実行可能ファイルをビルドできます)。 図1 GL-117のプレイ画面 ![]() 簡単なサンプル:エイリアン来襲!ゲームエンジンのことを知るには、サンプルコードを見るのが一番です。今回は、Sam Lantingaが1980年代の「スペースインベーダー」を模して作成したゲームを取り上げたいと思います。その名もシンプルに「aliens(エイリアン)」です。なんと、このゲームのソースコードは全体で560行しかありません。すべてのコードを紹介したいのはやまやまですが、少々長くなりすぎるので、最も重要な部分だけに(それこそレーザービーム級の密度で!)焦点を当て、それ以外の部分は割愛します。本稿のコードリストではaliens.cでの行番号を併記しますので、Visual Studioで実際にコードを見るときの目安にしてください。 図2 aliensのゲーム画面 ![]() SDL_mixerは、SDLアプリケーション用の、プラットフォーム非依存のサウンドミキシングライブラリプラグインです。このプラグインを利用すると、ミキシングアルゴリズムを書かなくても、音楽と一緒に複数のサンプルを再生できます。たとえば、砲撃音をBGMにシームレスにミックスすることができます。また、さまざまなファイル形式のサンプルや音楽の読み込み、および再生を容易に行うことができます。 SDL_imageは、SDLアプリケーション用のプラットフォーム非依存のグラフィック読み込みプラグインです。これにより、BMP、PNM(PPM/PGM/PBM)、XPM、LBM、PCX、GIF、JPEG、PNG、TGA、およびTIFFファイル形式をアプリケーションに読み込むことができます。 以降で紹介するコードを実際に試してみたい場合は、この2つのパッケージをダウンロードしておいてください。 main()関数当然ながら、実際のアクションは次に示すmain()関数から始まります。524行目では、SDL_Init()を使用してオーディオとビデオのサブシステムを初期化します。他に使用できるオプションとしては、ジョイスティック、タイマー、CD-ROMアクセスを有効にするものや、イベントマネージャを独立したスレッドとして実行するためのものがあります。 531行目では、Mix_OpenAudio()を呼び出すことでSDL_Mixerマネージャの使用を開始します。この例では、古い低速のCPUを考慮して低品質のオーディオ(11Khz、8ビット符号なしサンプル、モノラル、512バイトバッファ)を使用しています。 次に、SDL_SetVideoMode()を呼び出すことで、デバイスに合った適切なbpsを使用して640×480ディスプレイ用にビデオを開きます。このとき、システムメモリバッファを使用し、全画面表示(ウィンドウを画面全体に表示するモード)にするよう指定しています。この他に使用できるオプションとしては、ダブルバッファリング(アニメーションを滑らかにします)、OpenGLコンテキスト、サイズ変更可能ウィンドウ、非同期再描画などがあります。 521 main(int argc, char *argv[]) 522 { 523 /* Initialize the SDL library */ 524 if ( SDL_Init(SDL_INIT_AUDIO|SDL_INIT_VIDEO) < 0 ) { 525 fprintf(stderr, "Couldn't initialize SDL: %s¥n", SDL_GetError()); 526 exit(2); 527 } 528 atexit(SDL_Quit); 529 530 /* Open the audio device */ 531 if ( Mix_OpenAudio(11025, AUDIO_U8, 1, 512) < 0 ) { 532 fprintf(stderr, 533 "Warning: Couldn't set 11025 Hz 8-bit audio¥n- Reason: %s¥n", 534 SDL_GetError()); 535 } 536 537 /* Open the display device */ 538 screen = SDL_SetVideoMode(640, 480, 0, SDL_SWSURFACE|SDL_FULLSCREEN); 539 if ( screen == NULL ) { 540 fprintf(stderr, "Couldn't set 640x480 video mode: %s¥n", 541 SDL_GetError()); 542 exit(2); 543 } 544 545 /* Initialize the random number generator */ 546 srand(time(NULL)); 547 548 /* Load the music and artwork */ 549 if ( LoadData() ) { 550 /* Run the game */ 551 RunGame(); 552 553 /* Free the music and artwork */ 554 FreeData(); 555 } 556 557 /* Quit */ 558 Mix_CloseAudio(); 559 exit(0); 560 } RunGame()関数メインのロジックループは、もちろんすべてRunGame()内にあります。この関数は約200行から成るので、全体を一度に見るには長すぎます。そこで、SDLの機能を説明しながら、いくつかの重要なセクションを取り上げることにします。コードを省略した部分は、コメント中の省略記号(...)で示します。301 void RunGame(void) 302 { 303 int i, j; 304 SDL_Event event; 305 Uint8 *keys; 306 307 /* Paint the background */ 308 numupdates = 0; 309 for ( i=0; i<screen->w; i += background->w ) { 310 SDL_Rect dst; 311 312 dst.x = i; 313 dst.y = 0; 314 dst.w = background->w; 315 dst.h = background->h; 316 SDL_BlitSurface(background, NULL, screen, &dst); 317 } 318 SDL_UpdateRect(screen, 0, 0, 0, 0); 319 320 /* Initialize the objects */ 321 // . . . 333 CreateAlien(); 334 DrawObject(&aliens[0]); 335 UpdateScreen(); 336 337 while ( player.alive ) { 338 /* Wait for the next frame */ 339 WaitFrame(); 340 341 /* Poll input queue, run keyboard loop */ 342 while ( SDL_PollEvent(&event) ) { 343 if ( event.type == SDL_QUIT ) 344 return; 345 } 346 keys = SDL_GetKeyState(NULL); 347 348 /* Erase everything from the screen */ 349 // . . . 366 /* Decrement the lifetime of the explosions */ 367 // . . . 373 /* Create new aliens */ 374 if ( (rand()%ALIEN_ODDS) == 0 ) { 375 CreateAlien(); 376 } 377 378 /* Create new shots */ 379 if ( ! reloading ) { 380 if ( keys[SDLK_SPACE] == SDL_PRESSED ) { 381 for ( i=0; i<MAX_SHOTS; ++i ) { 382 if ( ! shots[i].alive ) { 383 break; 384 } 385 } 386 if ( i != MAX_SHOTS ) { 387 shots[i].x = player.x + 388 (player.image->w-shots[i].image->w)/2; 389 shots[i].y = player.y - 390 shots[i].image->h; 391 shots[i].alive = 1; 392 Mix_PlayChannel(SHOT_WAV, 393 sounds[SHOT_WAV], 0); 394 } 395 } 396 } 一見シンプルに見えるSDL_PollEvent()は、イベントに関する情報を含んだSDL_Eventオブジェクトを返します。ここでSDL_QUITという特殊な値を使用して、ESCキーが押されているかどうかを確認します。 これは純粋にキーボード操作だけでプレイするゲームなので、SDL_GetKeyState()を使用すれば必要な情報をすべて入手できます。PCのキーボードは複数のキーを同時に押せるよう設計されているので、結果は配列として返されます。この配列ではSDLK_SPACEのようなキーボードコード値がインデックスとして使われます(SDLK_SPACEはスペースバーを表します)。 397 reloading = (keys[SDLK_SPACE] == SDL_PRESSED); 398 399 /* Move the player */ 400 //. . . 415 /* Move the aliens and the shots */ 416 // . . . 443 444 /* Detect collisions */ 445 for ( j=0; j<MAX_SHOTS; ++j ) { 446 for ( i=0; i<MAX_ALIENS; ++i ) { 447 if ( shots[j].alive && aliens[i].alive && 448 Collide(&shots[j], &aliens[i]) ) { 449 aliens[i].alive = 0; 450 explosions[i].x = aliens[i].x; 451 explosions[i].y = aliens[i].y; 452 explosions[i].alive = EXPLODE_TIME; 453 Mix_PlayChannel(EXPLODE_WAV, 454 sounds[EXPLODE_WAV], 0); 455 shots[j].alive = 0; 456 break; 457 } 458 } 459 } このゲームでは3つのチャンネルを同時に使用し、音楽、砲撃音、爆発音をそれぞれ独立したチャンネルで扱います。なぜなら、そのときどきの状況によって、最大3つのサウンドを同時に再生する必要があるからです。独立したチャンネルを用意しておかないと、あるサウンドから別のサウンドへの切り替えをひっきりなしに行うことになり、サウンドトラックがぶつ切れになってしまいます。 話が前後しますが、これらのサウンドは次のような呼び出しで読み込んでおきます。
127 sounds[MUSIC_WAV] = Mix_LoadWAV(DATAFILE("music.wav"));
474 // . . . 475 /* Draw the aliens, shots, player, and explosions */ 476 for ( i=0; i<MAX_ALIENS; ++i ) { 477 if ( aliens[i].alive ) { 478 DrawObject(&aliens[i]); 479 } 480 } 481 // . . . 494 UpdateScreen(); 495 496 /* Loop the music */ 498 if ( ! Mix_PlayingMusic() ) { 499 Mix_PlayMusic(music, 0); 500 } 506 507 /* Check for keyboard abort */ 508 if ( keys[SDLK_ESCAPE] == SDL_PRESSED ) { 509 player.alive = 0; 510 } 511 } 512 513 /* Wait for the player to finish exploding */ 514 while ( Mix_Playing(EXPLODE_WAV) ) { 515 WaitFrame(); 516 } 517 Mix_HaltChannel(-1); 518 return; 519 } 520 まとめ500行あまりのコードで実装された昔懐かしい2Dシューティングゲームに音楽とリアルタイムプレイを組み合わせただけですが、SDLの素晴らしさはわかっていただけたのではないでしょうか。もちろん、この同じコードから、WindowsやLinuxをはじめ、さまざまなプラットフォームで実行可能なアプリケーションをコンパイルできます。本稿では、SDLの表面を少し触ってみただけにすぎません。興味のある方は、ぜひ実際に使ってみてください。著者紹介Victor Volkman(Victor Volkman)
1980年代末から『C/C++ Users Journal』などのプログラミング雑誌に記事を執筆。ミシガン工科大学を卒業し、WashtenawコミュニティカレッジCIS学部の諮問委員会のメンバーを務める。『C/C++ Treasure Chest』など数々の書籍の編集者であり、Loving Healing Pressのオーナーでもある。オープンソースのツールやライブラリについての相談、お問い合わせはsysop@HAL9K.comまで。
関連記事 最新トップニュース
|
KDDI、大人向けケータイ「URBANO」を発表――発表会にジローラモ氏が登場(8月28日 16:50)
読者が迷惑メールと認識する時…(9月3日 10:00)
なぜ勝った? 世界No.1シェアをつかんだ“Windows”(9月5日 11:00)
KDDI、「光で操作ナビ」などを搭載した簡単ケータイ「W62PT」を8月30日発売(8月29日 18:00)
ニフティ、スパム Blog に対するフィルタリング技術を「BuzzPulse」等に搭載(9月4日 16:00)
私の周りは“geek out”している人ばかり(9月5日)
|