0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

ゲームプログラミングC++ Asteroid

Posted at

はじめに

『ゲームプログラミングC++』という本の三章では『Asteroid』というゲームを制作しますが、第一章で作る『Pong』と比べて遥かに複雑で、私自身大変苦労しました。

そこで、学んだことやプログラム全体像を確かな形で整理したいと思い、本記事を書くことにします。
私自身プログラミング初学者であるため、分かりにくいところもあるとは思いますが、温かい目で見て頂ければ幸いです。

本書のソースコード

この記事ではコードを詳細に載せませんので、以下の公式レポジトリを参考にしてください。

構造

今回のモデルは第二章で示されたように「階層構造」と「コンポーネント」を組み合わせた形です。

  • Actorクラスがそのオブジェクト(キャラクター、弾など)の状態、数値(座標、回転角など)を管理します
  • Componentクラスが、動作や処理(描画、移動、入力受付など)の機能を提供します

Actorに複数のComponentを「部品」として組み込むことで、ゲームオブジェクトの振る舞いを決定し、Gameクラスが全てのActorと、描画対象となるSpriteComponentを管理し、ゲームループを回します。

実際のモデル(クラス階層)

Main
 └─ Game
     ├─ Actor (アクター : オブジェクトの基底クラス)
     │   ├─ Asteroid(小惑星)
     │   ├─ Ship(プレイヤーの船)
     │   └─ Laser(レーザー弾)
     ├─ Component(コンポーネント : 機能部品の基底クラス)
     │   ├─ CircleComponent(円形の衝突判定)
     │   ├─ SpriteComponent(スプライト描画)
     │   └─ MoveComponent(移動処理)
     │       └─ InputComponent(プレイヤー入力処理)
     └─ Library(補助機能)
         ├─ Math(数学関連)
         └─ Random(乱数生成)

Library(補助機能)

まずは土台となる補助的な機能です。(.libファイルがあるわけではないのですが、何と呼べばいいのかわからないので今回はライブラリとして扱います。)

Math

ベクトルや行列の計算、円周率の定義など、ゲームを制作する上で必要となる数学的機能を提供します。今後何度も使うと思うので、SDLと同じようにプロパティから追加するようにすれば楽かもしれないですね。

Random

疑似乱数を生成する機能です。floatVectorなど複数の型に合わせた範囲指定の乱数を返します。
Asteroid(小惑星)の初期座標や移動方向をランダムに設定するために使用します。

Component

Actorに追加する「機能部品」です。Component基底クラスを継承して作られます。

  • Componentは自分が所属するActorへのポインタ(mOwner)を保持します。これにより、コンポーネントはGet関数などを通して、オーナーであるActorの各種数値にアクセスできます
  • コンストラクタでActorAddComponentを呼び出すことでコンポーネントを管理します
  • Actorから更新処理を呼び出してもらうため、仮想関数UpdateProcessInputが宣言されています

CircleComponent

Actorに円形の衝突判定機能を提供します。衝突判定を円の中心点同士の距離と半径の合計の比較によって行うので、このコンポーネントにはbool型のIntersect関数を持ちます。
LaserもしくはShipAsteroidの衝突判定に使われます。

SpriteComponent

テクスチャの保持と描画を行います。
スプライトの管理自体はGameが行っています。ですからActorGetGame関数からAddSprite関数呼び出し、同様にデコンストラクタでRemoveSprite関数を呼び出します。
インスタンスの生成と同時に、Gameで生成したテクスチャをSet関数で受け取とります。
最後にGameクラスのGenerateOutputでSpriteComponentのDraw関数を呼び出しSDLの独自関数によって描画します。

MoveComponent

Actorの移動(座標と回転)を処理します。Update関数をオーバーライドし、現在の回転速度(mAngularSpeed)と前進速度(mForwardSpeed)に基づいて、オーナーであるActorの回転角(GetRotation)と座標(GetPosition)をデルタタイム(deltaTime)を使って更新します。

InputComponent

MoveComponentを継承して、プレイヤーによる操作を提供します。
仮想関数ProcessInputをオーバーライドし、キー入力を検知します。押されたキーに応じて最大回転速度(mMaxAngularSpeed)と最大前進速度(mMaxForwardSpeed)をMoveComponentの回転速度、前進速度に設定(SetAngularSpeed,SetForwardSpeed)します。
実際の座標更新は、基底クラスであるMoveComponentUpdateが行います。

Actor

ゲームオブジェクトの基底クラスです。

  • メンバ変数として、状態(Active,Paused,Dead)と座標、スケール、回転角を持ちます
  • 自身にアタッチされたコンポーネントを保持するstd::Vectorを持ちます
  • それぞれ変数のGetSet関数を持ち、状態の初期値はActiveにします
  • 状態がActiveの時、Update関数(Actorが持つコンポーネントのUpdateUpdateActorを呼ぶ)やProcessInput(Actorが持つコンポーネントのProcessInputActorUpdateを呼ぶ)をGame`でループさせます
  • UpdateActorActorInputは仮想関数になっており、派生クラス(Shipなど)が固有の処理を実装できます

Asteroid

小惑星のアクターです。

  • コンストラクタでRandomを使い、出現座標と移動方向(回転角)をランダムに設定します
  • CircleComponentMoveComponentSpriteComponentを追加します
  • Gameで自身を管理するのでAddRemoveを忘れずに行います

Ship

プレイヤーが操作する船のアクターです。

  • SpriteComponentInputComponentを追加します
  • ActorInputをオーバーライドし、スペースキーが押されたらLaserを生成します
  • UpdateActorをオーバーライドし、レーザーのクールダウンを更新します

Laser

Shipから発射される弾のアクターです。

  • CircleComponentMoveComponentSpriteComponentを追加します
  • UpdateActorをオーバーライドし、Laserの状態を管理します
  • 生存時間(mDeathTimer)が0以下になった時、もしくはAsteroidと交差した時も、その状態をDeadにします

Game

ゲーム全体の流れを管理するクラスです。

  • 初期化(Initialize)と終了(Shutdown)、ゲームループ(RunLoop)を行います
  • 全てのActorSpriteComponentの保持と管理のための配列と関数を持ちます

ゲームループでは、主に以下の動作を実行します

1.. ProcessInput(入力処理):

  • ActorProcessInputを呼び出します
  • この処理と更新を行っている最中に生成されたActorは一時的に別の配列(mPendingActors)におきます

2. UpdateGame(更新処理)

  • デルタタイムの実装と管理をします
  • ActorUpdateを呼び出し、その後に保留されたActorを配列に追加します
  • Dead状態になったActorを配列から削除します

3. GenerateOutput(描画処理)

  • SpriteComponentDrawを呼び出します

また、初期化\終了の際、LoadData`UnloadData`(オブジェクト管理)を呼びます

  • LoadDataでは各オブジェクトを必要数生成します
  • UnloadDataではオブジェクトとテクスチャを解放します

終わりに

初見ではあまり全体像を掴めなかったのですが、この記事を通して自分が持つ解像度も高まったように思います。『Pong』と比べると様々な要素が一気に出てきて、躓いてしまう方もいらっしゃると思いますが、この記事が何かの一助になると幸いです。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?