
マイケル
みなさんこんにちは!
マイケルです!
マイケルです!

エレキベア
こんにちクマ〜〜

マイケル
今日は前回に引き続きC++を使ったゲーム開発を進めていくよ!
前回はC++の基礎をざっとと通ったから実際にゲーム開発に入っていきます!
前回はC++の基礎をざっとと通ったから実際にゲーム開発に入っていきます!

エレキベア
待ちくたびれたクマ〜〜〜

マイケル
ちなみに作成するゲームはこのような2Dシューティングゲーム!
ランダムに出現する敵をミサイルで撃ち倒すというシンプルなものになっています!
ランダムに出現する敵をミサイルで撃ち倒すというシンプルなものになっています!
↑作成するシューティングゲーム

エレキベア
(またカニクマか・・・)
でもちゃんとゲームになってるクマね
でもちゃんとゲームになってるクマね

マイケル
最近は座学ばかりだったので、めっちゃ楽しかったです!!

エレキベア
まあそれはいいことクマ・・・

マイケル
ちなみに今回もGitHubにソースコードをあげています!
触るも勉強するも、ご自由に使ってください!
触るも勉強するも、ご自由に使ってください!
GetHub(masarito617) – 2Dシューティングゲーム

マイケル
それでは早速やっていこう!
参考書籍

マイケル
今回参考にした書籍は下記になります!

マイケル
どちらもゲームプログラミングの基礎から丁寧に書かれているため、
読み応えがありますし、楽しいです!
読み応えがありますし、楽しいです!

マイケル
特に「ゲームプログラミングC++」の方はSDLを使って一から開発していて、
今回作ったゲームもこちらの設計をベースにさせていただいています。
記事を見て気になった方はぜひ読んでみてください!
今回作ったゲームもこちらの設計をベースにさせていただいています。
記事を見て気になった方はぜひ読んでみてください!

エレキベア
クマもこの2冊は持ってるクマ〜〜〜
SDLとは

マイケル
今回作るゲームはSDLというライブラリを使って
描画やウィンドウの作成を行なっています。
描画やウィンドウの作成を行なっています。

エレキベア
SDLって何クマ?

マイケル
SDLはマルチメディアライブラリの1つで、
描画やオーディオ、キー入力などの機能が揃っているんだ!
描画やオーディオ、キー入力などの機能が揃っているんだ!

マイケル
DirectXやOpenGLはよく聞くかもしれないけど、それと同じような類だね!
簡単に比較すると下記のようになるよ。
簡単に比較すると下記のようになるよ。
種類 | 特徴 |
DirectX | Windowsに標準搭載されているマルチメディアライブラリ。 グラフィックスだけでなく、オーディオ制御や入力制御など、ゲーム開発機能も搭載している。 |
OpenGL | 世界標準の規格としてグラフィックス機能のみを提供するAPI。 マルチプラットフォームに対応するためDirectXよりは進化が遅い。 |
SDL | マルチプラットフォームに対応したマルチメディアライブラリ。 3Dグラフィックスはサポートしていない。 |
↑マルチメディアライブラリ比較

マイケル
SDLはマルチプラットフォームに対応しているからWindowsでもMacでも使えるし、
DirectXと比べたら使い方も簡単です!
ちなみに今回のゲームはMacで開発しています。
DirectXと比べたら使い方も簡単です!
ちなみに今回のゲームはMacで開発しています。

エレキベア
C++でのゲーム開発はWindowsの印象が強かったクマが
Macでも開発できるのクマね
Macでも開発できるのクマね

マイケル
しかしSDLは3Dグラフィックスはサポートしていないため、
次回以降は SDL+OpenGL の組み合わせで開発していこうと思います!
次回以降は SDL+OpenGL の組み合わせで開発していこうと思います!

エレキベア
OpenGLも楽しみクマ〜〜〜

マイケル
それでは早速作っていこう!
2Dシューティングゲームの開発

マイケル
作ったゲームについて解説していきますが、
ソースコードについては量が多いため全ては載せていません。
要所要所で解説していくので、ソースコードはGitHubを参照するようにしてください!
ソースコードについては量が多いため全ては載せていません。
要所要所で解説していくので、ソースコードはGitHubを参照するようにしてください!
GetHub(masarito617) – 2Dシューティングゲーム

エレキベア
全部載せられても困るクマね
ゲームの仕様

マイケル
それではまず作るゲームの仕様について!
開始シーン、ゲームシーン、終了シーンの3つに分かれていて、
ランダムな動きで一定数出てくる敵を全て倒せばクリア、
敵を逃すかぶつかったらゲームオーバー
というシンプルな仕様になっています!
開始シーン、ゲームシーン、終了シーンの3つに分かれていて、
ランダムな動きで一定数出てくる敵を全て倒せばクリア、
敵を逃すかぶつかったらゲームオーバー
というシンプルな仕様になっています!
↑シーン遷移
↑敵を全て消滅させたらクリア
↑敵にぶつかるか下まで逃したらゲームオーバー

エレキベア
いい感じの規模感クマね

マイケル
ちなみに画像素材は下記を使っています!
イラレでざっと描いただけのものになります・・・
イラレでざっと描いただけのものになります・・・

ship.png

enemy.png

missile.png

bomb.png

msg_start.png

msg_clear.png

msg_over.png

bg_front.png

bg_back.png

エレキベア
クマの使用料を取りたいくらいクマ
ソースコード構成

マイケル
ソースコードの構成は下記のようになっています!
↑ソースコード構成

マイケル
Actorsフォルダには宇宙船や敵といったゲームオブジェクト、
Componentsフォルダには衝突や描画等のコンポーネント、
Commonsフォルダには計算など共通的な処理、
Scenesフォルダには各シーンのクラス
をそれぞれ格納しています。
Componentsフォルダには衝突や描画等のコンポーネント、
Commonsフォルダには計算など共通的な処理、
Scenesフォルダには各シーンのクラス
をそれぞれ格納しています。

エレキベア
フォルダごとに見ていくと分かりやすいクマね
ゲームオブジェクトとコンポーネント

マイケル
フォルダ構成で気付いた方もいるかもしれませんが、
ゲームオブジェクトとコンポーネントという、ゲームエンジンでよく触るような単語になっていたと思います。
これらの設計や実装方法について軽く解説します!
ゲームオブジェクトとコンポーネントという、ゲームエンジンでよく触るような単語になっていたと思います。
これらの設計や実装方法について軽く解説します!

エレキベア
Unityでいつもコンポーネントをアタッチしてるクマ〜〜
クラス設計

マイケル
まず単純なクラス設計としては、
継承を使用したモノシリックな設計があります。
継承を使用したモノシリックな設計があります。
↑継承を使用した設計

エレキベア
前回の基礎編でも出てきた構造クマね

マイケル
この構造は依存関係が分かりやすく共通の処理を持たせることができるけど、
規模が大きくなるほど複雑化してしまう というデメリットがあるんだ。
その点、下記のようなコンポーネントを使用した設計は必要な機能だけを持たせやすい!
規模が大きくなるほど複雑化してしまう というデメリットがあるんだ。
その点、下記のようなコンポーネントを使用した設計は必要な機能だけを持たせやすい!
↑コンポーネントを使用した設計

エレキベア
オブジェクトの中にも衝突を必要としないものがあったりするクマね

マイケル
この2つを組み合わせて使うことで、
最小限の機能だけ持たせて依存関係も分かりやすい設計にすることができます!
最小限の機能だけ持たせて依存関係も分かりやすい設計にすることができます!
↑組み合わせた設計

エレキベア
これは便利クマ〜〜〜

マイケル
この設計手法を使って実装しています!
更に詳しく知りたい方は
ゲームプログラミングC++を読んでみてください!
更に詳しく知りたい方は
ゲームプログラミングC++を読んでみてください!
依存性の注入(DI)

マイケル
実装について補足ですが、
下記がアクタとコンポーネントの基底クラスになります!
下記がアクタとコンポーネントの基底クラスになります!

マイケル
GameクラスでActorのリストを管理、
ActorクラスでComponentクラスのリストを管理していますが、
それぞれオブジェクト自身で追加、削除するクラスを受け取って処理を呼び出していることが分かります。
ActorクラスでComponentクラスのリストを管理していますが、
それぞれオブジェクト自身で追加、削除するクラスを受け取って処理を呼び出していることが分かります。

エレキベア
受け取らなくてもシングルトン化して使えばいいのではないクマ?

マイケル
Gameクラスはシングルトン化すれば使えるけど、
Actorクラスのように複数インスタンス必要になった場合に対応できなくなるから、このような設計になっているよ!
これは 依存性の注入(DI) と呼ばれます!
Actorクラスのように複数インスタンス必要になった場合に対応できなくなるから、このような設計になっているよ!
これは 依存性の注入(DI) と呼ばれます!

エレキベア
聞いたことはあったクマがこんな設計のことだったクマね

マイケル
また、DIには モジュール化してテストしやすくなる といったメリットもあります。
Unityにも Zenject というDIのためのツールがあるので、興味のある方は調べてみてください!
Unityにも Zenject というDIのためのツールがあるので、興味のある方は調べてみてください!
ゲームループ

マイケル
それではゲームの流れがどうなっているのか見ていこうと思います!
まずmain.cppから処理が始まります。
まずmain.cppから処理が始まります。

マイケル
これはもう同じみのゲームループですね!
初期化→ループ→終了の順番でGameクラスの処理を呼び出しています。
初期化→ループ→終了の順番でGameクラスの処理を呼び出しています。

エレキベア
ゲームプログラムはとりあえずここからクマね

マイケル
Gameクラスにつひてだいぶ省略していますが、簡単に説明すると
初期化処理ではSDL関連やシーンの初期化、
ゲームループではシーンの更新と切替
を行なっています。
初期化処理ではSDL関連やシーンの初期化、
ゲームループではシーンの更新と切替
を行なっています。

マイケル
そしてシーンの更新では
入力検知、各アクタの更新、画面出力処理
を呼び出しています。
入力検知、各アクタの更新、画面出力処理
を呼び出しています。

エレキベア
ざっくりした流れは分かってきたクマね

マイケル
次はそれぞれ細かく見ていこう!
初期化処理

マイケル
まずは初期化処理!
InitSDL関数では、SDL関連の初期化やウィンドウの作成を行なっています。
InitSDL関数では、SDL関連の初期化やウィンドウの作成を行なっています。

マイケル
そしてシーンの初期化では背景を作成しています!
なお、読み込んだテクスチャはキャッシュして使いまわすよう LoadTexture関数 内でキャッシュしています。
なお、読み込んだテクスチャはキャッシュして使いまわすよう LoadTexture関数 内でキャッシュしています。

マイケル
ここで、背景のスクロールはScrollSpriteComponent
というコンポーネントを作成して実現しています。
というコンポーネントを作成して実現しています。

マイケル
このように複数の画像を異なる速度でスクロールさせる手法を、
視差スクロールといいます。
手軽に遠近感が出せるのでおすすめです!!
視差スクロールといいます。
手軽に遠近感が出せるのでおすすめです!!

エレキベア
綺麗クマ〜〜〜

マイケル
作成したSDL関連の変数については、
シャットダウン時に破棄するようにしています。
シャットダウン時に破棄するようにしています。
更新処理

マイケル
次は更新処理!
シーン遷移を簡単なシーケンス処理で更新しています!
シーン遷移を簡単なシーケンス処理で更新しています!

マイケル
シーンは3種類用意し、下記のように基底クラスを
継承して実装しています。
継承して実装しています。
↑シーン構成
↑Scene基底クラス

マイケル
その通り!
例えばシーン開始処理は下記のように、
設定してあるシーンの開始処理を呼び出すだけでよくなるよ!
例えばシーン開始処理は下記のように、
設定してあるシーンの開始処理を呼び出すだけでよくなるよ!

エレキベア
これはなかなか簡潔クマね

マイケル
そしてシーン更新処理は下記の通り!
各シーンの更新処理の他、
・フレームごとの経過時間(deltaTime)の取得
・各アクタの更新処理呼び出し
・削除状態になったアクタの破棄
各シーンの更新処理の他、
・フレームごとの経過時間(deltaTime)の取得
・各アクタの更新処理呼び出し
・削除状態になったアクタの破棄
をそれぞれ行なっています!

エレキベア
deltaTimeは何のために取得するクマ?

マイケル
これはUnity何かでも使ってると思うけど、
時間に関わる処理を行う時にdeltaTimeを掛け合わせることで
性能差が違っても処理の時間差が出ないようにできるんだ!
例えば移動処理だとフレームごとにそのまま距離を足してしまうと性能がいいマシンだと高速で移動してしまう動きになってしまうよ。
時間に関わる処理を行う時にdeltaTimeを掛け合わせることで
性能差が違っても処理の時間差が出ないようにできるんだ!
例えば移動処理だとフレームごとにそのまま距離を足してしまうと性能がいいマシンだと高速で移動してしまう動きになってしまうよ。

エレキベア
1フレームにどれだけ動かすかじゃなくて、
1秒にどれだけ動かすか、という風に考えるクマね
1秒にどれだけ動かすか、という風に考えるクマね

マイケル
各アクタ内ではdeltaTimeを用いて
下記のように移動処理等をおこなっています!
下記のように移動処理等をおこなっています!

エレキベア
なるほどクマ〜〜〜
入力検知

マイケル
入力検知は下記の通り!
SDL_PollEventではウィンドウを閉じた時のイベント、
SDL_GetKeyboardStateでは入力キー
をそれぞれ取得できます!
SDL_PollEventではウィンドウを閉じた時のイベント、
SDL_GetKeyboardStateでは入力キー
をそれぞれ取得できます!

マイケル
また、シーンごとの入力検知の他、
アクタ内にも入力処理を持たせて呼び出すようにしています!
下記は宇宙船の移動処理の例です!
アクタ内にも入力処理を持たせて呼び出すようにしています!
下記は宇宙船の移動処理の例です!

エレキベア
この辺は分かりやすいクマね
出力処理

マイケル
そして最後に出力処理!
SpriteComponentを持つアクタの数だけDraw関数を呼び出して描画します!
SpriteComponentを持つアクタの数だけDraw関数を呼び出して描画します!

マイケル
ダブルバッファというのは、表示用と生成用で2枚の画面を持たせて、描画の際に入れ替える方法のことです。
Draw関数内では下記のように大きさと位置を指定して描画しています!
Draw関数内では下記のように大きさと位置を指定して描画しています!

エレキベア
これで一通りの流れが分かったクマ〜〜〜
衝突判定

マイケル
オブジェクト同士が衝突したかどうかの判定については、下記のように行なっています!
ColliderComponent に半径を持たせており、オブジェクト同士が衝突しているかどうかを返す関数を用意しています。
ColliderComponent に半径を持たせており、オブジェクト同士が衝突しているかどうかを返す関数を用意しています。

マイケル
判定方法は円の衝突を用いていて、オブジェクト同士の距離が半径の合計より小さければ衝突としています。
ベクトルの長さについては、本来平方根を使用して求めますが、
計算負荷短縮のため二乗した状態で比較を行なっています。
ベクトルの長さについては、本来平方根を使用して求めますが、
計算負荷短縮のため二乗した状態で比較を行なっています。
↑平方根は求めずに返す

マイケル
以上の判定処理は、アクタの更新処理内で呼び出しています。

エレキベア
爆発エフェクトって何クマ?

マイケル
爆発エフェクトはアクタクラスとして個別に作ったんだ!
下記のようにスケールを変更して簡単なアニメーションを付けているよ!
下記のようにスケールを変更して簡単なアニメーションを付けているよ!

エレキベア
楽しそうで何よりクマ・・・
エネミーのランダム生成

マイケル
長くなりましたが最後にエネミーのランダム生成について!
今回は適当な数ループして、ランダム値で動きや位置を変更 するようにしています。
今回は適当な数ループして、ランダム値で動きや位置を変更 するようにしています。

マイケル
動きの種類は、単純に落ちてくる動きの他、
ゆらゆらしながら落ちる動きも追加しています。
こちらの動きは sinf関数を利用 して再現しています。
ゆらゆらしながら落ちる動きも追加しています。
こちらの動きは sinf関数を利用 して再現しています。

エレキベア
sin値はグラフで書くと波のような形クマからね

マイケル
そして乱数の生成については、今回は下記のようにして取得しました!
これはC++11で追加された乱数ライブラリみたいですね。
これはC++11で追加された乱数ライブラリみたいですね。

エレキベア
手軽に乱数も作れるクマね

マイケル
精度のいい乱数生成方法は他にもあると思うので、
気になる方は調べてみてください!
気になる方は調べてみてください!
おわりに

マイケル
というわけで今回はC++での2Dゲーム開発でした!
どうだったかな??
どうだったかな??

エレキベア
C++って聞くと難しそうだったクマが
やってみたら意外と簡単に作れたクマね
やってみたら意外と簡単に作れたクマね

マイケル
それとやっぱり一から作ってる感があって楽しいね!
次回からは3Dゲーム開発に挑戦していこう!
次回からは3Dゲーム開発に挑戦していこう!

エレキベア
OpenGL楽しみクマ〜〜〜

マイケル
それでは今日はこの辺で!
アデュー!!
アデュー!!

エレキベア
クマ〜〜〜〜
【C++】第二回 C++を使ったゲーム開発 〜SDLを使った2Dゲーム開発編〜 〜完〜
※続きはこちら!