C++ゲーム開発録 - 1週間で音ゲー制作

## はじめに

この記事は [Aizu advent calendar](https://adventar.org/calendars/3023) 21日目の記事です。(大遅刻になってしまいました。ごめんなさい。)
前の日はslme_not_foundさん、次の日はmt-coffさんでした。



## 自己紹介

Shirotsuと申します。C++歴1年程度のゲームプログラマー志望の学生です。記事を公開する以上極力間違いの無いよう心がけますが、もしございましたらご指摘ください。GitHubのアカウントは[こちら](https://github.com/Nao-Shirotsu)。



## 概要

環境構築を含めて1週間で音ゲー制作にチャレンジしたことを綴る記事です。開発の記録を残すことと**C++ゲーム制作入門者向け**に私がどのように開発を進めたかということを参考にしていただくという目的を兼ねて書きつらねていきます。また、この記事では**Visual Studio, Siv3Dの使い方とC++については深く解説しません。**



## 開発環境

Windows10 Visual Studio 2017 とC++用フレームワーク [Siv3D](https://play-siv3d.hateblo.jp/) を利用して開発しました。VS2017の環境構築については[こちら](https://docs.microsoft.com/ja-jp/visualstudio/install/install-visual-studio?view=vs-2017)をどうぞ。



## Siv3D環境構築

Siv3Dリファレンスページの[ダウンロードとインストール](https://github.com/Siv3D/Reference-JP/wiki/%E3%83%80%E3%82%A6%E3%83%B3%E3%83%AD%E3%83%BC%E3%83%89%E3%81%A8%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB)のページを見て行います。
注意書きにもある通り、Siv3DはVS2015バージョンのプロジェクトファイルで扱う必要があります。

#### ハマったところ

Siv3Dのインストールも終わってさあ実行してみようとすると、こんなビルドエラーが。
![builderror.PNG](https://qiita-image-store.s3.amazonaws.com/0/200674/0824ba4c-67f0-6b82-493c-40226cd28802.png)

これを解決するには、プロジェクトのプロパティを変更する必要があります。
![debug_info.PNG](https://qiita-image-store.s3.amazonaws.com/0/200674/64d69a33-b0af-1aa2-dda5-a5d7dbf32f86.png)


> ソリューションエクスプローラー > プロジェクトを右クリック > プロパティ > C/C++ > 全般 > デバッグ情報の形式

このオプションを**C7互換**にする必要があります。
このオプションは、実行中のバイナリファイルとソースコードを結び付けてデバッグを可能にする機能関連のオプションです。詳細は[こちら](https://docs.microsoft.com/en-us/previous-versions/958x11bc(v=vs.140))。



## 開発の流れ

基本的な開発フローの話ですので、分かっている方は見出しで判断して読み飛ばしてください。

#### 1. ゲームの流れと仕様を決める

環境構築を終えたら、簡単にゲームの仕様と「ゲームに必要なシーン」を考えます。この後に**プログラムの構成を決めるため**、おおまかにどんなものが必要か記しておく作業です。

私は音ゲーを作ろうと決め、このようにしました。
![specification.PNG](https://qiita-image-store.s3.amazonaws.com/0/200674/01172ef3-0aeb-4357-a968-592ee90c8fcf.png)
![trans.PNG](https://qiita-image-store.s3.amazonaws.com/0/200674/9e23ea05-fb81-a43b-c7bd-295c60b0adcd.png)


#### 2. プログラムの構成を決める

次は大まかなクラス構成を決めます。中規模以上のプログラムになると、この構成をやらなければ後々プログラムが絡まりあってデバッグや機能追加で苦しむことになるでしょう。[こちらの記事](https://qiita.com/narumi_/items/312ce1894c24cd3e0d6b)で紹介されているようにゲームプログラムに使える様々な設計パターンがありますので、積極的に利用しましょう。
[Draw.io](https://www.draw.io/)などを使って[UMLのクラス図](https://ja.wikipedia.org/wiki/%E3%82%AF%E3%83%A9%E3%82%B9%E5%9B%B3) で決めていくのがおすすめです。

今回、私はこんな構成にしました。(※簡単のために省略したり、書いていない箇所があります。)
![diagram.png](https://qiita-image-store.s3.amazonaws.com/0/200674/87d25a4d-4a34-67f6-3b3f-668ebf84ace4.png)


核となるGame::Coreクラスが各シーンの実体を格納するスタックを持ち、スタックの一番上にあるシーンを実行します。実行したいシーンのポインタを切り換えれば、ごく軽い処理でフレームとフレームの間にシーン遷移ができます。(参考プログラム)

```cpp:main.cpp
int main(){
    Game::Core gamecore;
    while( !gamecore.IsGameEnd() ){
        gamecore.Update();
        gamecore.Draw();
    }
}
```

```cpp:GameCore.cpp
void Game::Core::Update(){
    if( sceneStack.top()->NeedsTransition() ){
		std::unique_ptr<Base>&& nextScene = sceneStack.top()->TransitionToNext(); 
		if( nextScene ){
			sceneStack.push( std::move( nextScene ) );
		}
		else{
			sceneStack.pop();
		}
	}
	sceneStack.top()->Update();
}

void Game::Core::Draw() const{
    sceneStack.top->Draw();
}
```

#### 3. プログラムを実装する

まずクラス図に沿ってヘッダファイルを書きます。その後、各ヘッダに合わせて**何もしないメンバ関数**の実装を書きます。この状態は、いわゆるモックと呼ばれるものです。

次に、核となるクラスから末端となるクラスの順に実際の動作を実装します。

プログラムを書いている時に、追加する必要のあるクラス/関数を思いつくでしょう。その都度、追加します。
モジュールの急な追加を繰り返すとプログラムが複雑になりやすいので、本来であればヘッダファイルを書き始める時に全ての構成要素が揃っていることが理想です。



## 音ゲーの実装

音ゲーの処理は、曲のBPMから1拍にかかる秒数を算出 -> n小節m拍の秒数 付近で特定のキーが叩かれたか、という処理で実装しました。実際のコードは[こちら](https://github.com/Nao-Shirotsu/Siv3D_Alhythm/tree/master/Alhythm/src)。



## プロトタイプの完成

<blockquote class="twitter-tweet" data-partner="tweetdeck"><p lang="ja" dir="ltr">リリースするベータ版にごく近いバージョンです <a href="https://t.co/sZERNsS0bv">pic.twitter.com/sZERNsS0bv</a></p>&mdash; Shirotsu@GEA読了24% (@4rotsugd) <a href="https://twitter.com/4rotsugd/status/1077605495043551233?ref_src=twsrc%5Etfw">December 25, 2018</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

なかなかいい感じできたもので、Twitterで狂喜乱舞していました(笑)。いろいろ調整&ビジュアル面を強化して近日中にリリースします。



## 蛇足な余談

本当は「1日でゲーム開発チャレンジ」と言っていたところ延びに延びて1週間かかり、結果Advent Calendar投稿に大遅刻するという事態になってしまいました。
どれだけ時間がかかるのかを把握するためにタスクを小さく分割し、逆算しておくことが大事だなあと感じました。