[Siv3D] 自作ゲームに「クリスマスモード」を追加してみた🎅

# 前提

↓の記事の続きです

https://qiita.com/camino37/items/9ed3487a009bcb546607

# githubリポジトリ

↓これです

https://github.com/ryota37/Siv3DFirstGame

## 前の記事読むのめんどいよ！という方に

- 人生初のゲーム開発に挑戦
    - 目的: ソフトウェア開発の経験を積む & C++の勉強
- Siv3Dを少し触ったことがあった → メンターからSiv3Dで自作ゲーム作ってみてはと提案を受ける
- 生成AIの助けを借りて最低限動くものを作った
    - 名前は "Meat Catcher"。空から降ってくる肉を恐竜がキャッチするという胡乱な世界観のゲーム

# 今回の話

- アドベントカレンダーだし、折角なのでクリスマス要素のある記事を
- 過去に作った自作ゲームに無理やりクリスマス要素を入れてみた
- ついでに色々改修した

## まず、百聞は一見にしかず

![demo_v0_2_0.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2726845/8f6b8a3a-53e3-4718-ccd9-8cdc9792693d.gif)

↑ゲームプレイ画面のキャプチャ動画です。
- 通常モードとクリスマスモードでの動作を見せています
- クリスマスモードでは、タイトル・背景・プレイヤー・空から降ってくるものがクリスマス仕様になっていることが確認できます
- 画質が荒いのは、画面キャプチャしたmp4をgifにしているためです
    - (メディア周りの仕様に疎く、ffmpegで雑に変換してるだけなので、画質を維持する方法もあるかもしれません)

## クリスマス要素

- 前回記事からの目玉となるアップデート要素が、「クリスマスモード」
    - タイトル画面でxボタン押すとクリスマスモードに切り替わる
    - 隠しコマンド感を入れたかったため、あえてボタンで切り替えるような実装にはしていない
    - コナミコマンドのように複雑なキー操作を要求する仕様にしようかとも思ったが実装が面倒なので今回は断念した

### 実装の仕方

- `isXmas`
    - クリスマスモードと通常モードを管理するにあたって、無難に `isXmas`というbool値を定義した
    - `isXmas`のデフォルト値は`false`に設定しておいて、Xキーを押すたびに `isXmas = !isXmas;` で値が反転するようにした
        - ちなみにこれはCopilotさんの提案。当初はisXmasのtrue/false で条件分岐するような実装を想定していたが、明らかにこっちの方がスッキリしている。学びを得た

- `Player`, `FallingObject`クラス
    - これらのクラスには描画を担当する`draw()`というメンバ関数があるのだが、これを以下のように変更した
        - 引数として `texture` と `xmasTexture`の2種類のテクスチャ(絵文字)を取るようにして、isXmasの値に応じて `currentTexture` に設定するテクスチャを切り替えるというような実装
        - これはchatGPTの提案だが、引数が2つあるのは実装として美しくないような気もする。どちらかというと、関数実行時に通常モードのテクスチャを入れるかクリスマスモードのテクスチャを入れるかで挙動を分離したい
        - ただ、その変更を入れた場合の影響範囲の想定や、各種設計原則的にどうなのかという評価がまだできていないので、一旦この実装のままとする


```diff

-	void draw(const Texture& texture) {
-		texture.scaled(0.75).drawAt(posX, posY);
+	void draw(const Texture& texture, const Texture& xmasTexture, bool isXmas) {
+		const Texture& currentTexture = isXmas ? xmasTexture : texture;
+		currentTexture.scaled(0.75).drawAt(posX, posY);

```


### 背景画像を入れるにあたって

- クリスマスモードの背景画像は、フリー素材サイトの「[フリー素材ぱくたそ](https://www.pakutaso.com)」さんからDLしたものを使用しています
    - クレジット表記は[不要](https://www.pakutaso.com/faq-sns-1.html)とのことですが、素晴らしい画像素材を無償で提供してくださっている感謝を込めてご紹介させていただきます。(ゲーム中でも Licenses ボタンを押して開くページに表記しています)
- zipに画像ファイルも格納して、ゲームから画像ファイルを参照して背景を表示しようと当初は考えていました。ですが、これは二次配布に相当しそうなので画像データをゲーム中に埋め込むことにしました
    - Siv3Dファイルに外部ファイルを埋め込む方法は、[公式ドキュメント](https://zenn.dev/reputeless/books/siv3d-documentation/viewer/tutorial-resource)に記載があります

(余談)

- 自分は上記のドキュメントにちゃんと目を通しておらず、30.1の手順だけ実行して「読み込めないな〜」と唸っていました
- 真下にある30.2の手順も実行すればすぐに解決する問題だったのですが、視野の狭い私は「詰んだ！」と思い込んでしまいました
- 勇気を出してSiv3dのdiscordにて質問をしたところ、書き込みから数分以内に「30.2をやれば解決するで」とコメントをいただき、無事解決を見ました
    - 詰まった内容はあまりにもしょうもなくて申し訳ないやら恥ずかしいやらなのですが、疑問に思ったことがある際にすぐに質問できて、しかもすぐに回答してもらえるコミュニティが存在することに驚きと感動を覚えました。改めて、この場でSiv3Dコミュニティの皆様に感謝を申し上げます


## その他のアップデート内容

### タイトル画面周り

- タイトル画面にゲームタイトルと、"Press Enter to Start!!"という文言を設置した
- ゲームプレイ中にSpaceキーを押すとタイトル画面に戻るようにした
    - これは、タイトル画面からしかクリスマスモードに切り替えられるようにしていないため。タイトル画面に戻る手段がないと、一度プレイを始めたらゲームを終了しないとモードを切り替えられない

### 操作周り

- 画面内に極力ボタンを配置しないようにして、代わりにキー入力でゲーム中の操作がほぼ完結するようにした
    - 前回記事で消化したバージョンではプレイ画面に「Start」「Retry」というボタンを表示しており、しかもプレイヤー(恐竜)とボタンの表示が重なってしまうというかなり酷い仕様だった

### 肉が再出現してしまう問題の修正

- 前回のバージョンでは、恐竜が肉を食べてから肉が画面外に出るまでの間、一瞬だけ肉が表示されてしまうという変な挙動をしていた
    - これは「恐竜と肉が重なっている時のみ肉を消して、肉の落下はそのまま」という実装にしていたため発生していた事象だと思われる
- 対策
    - `FallingObject`クラスに `vanish()`というメンバ関数を追加
        - `vanish()` は、落下する物体が消えている**ように見せる**関数
        - 実際は消えているわけではなく、テクスチャをサイズを極小(1px)にした上で座標(0,0)に描画するということをしている
        - 実際にテクスチャが消えているわけではなく、なんというか苦肉の策みたいな実装です(もっと上手い方法はないものか)
    - `isSuccess` の値に応じて実行するifブロックを分け、`isSuccess` が `true` の時は `vanish()`を、`false`の時は `draw()` を呼ぶようにした
        - `isSuccess` は、名前の通り肉のキャッチに成功した時`true` になる、初期値 `false` のbool値

※コードの詳細は上述のgithubリポジトリをご覧ください

# 今後の展望

今回は「アドベントカレンダーだしクリスマスっぽい要素を入れてみるか〜」という軽いノリでクリスマスモードを突貫実装したわけですが、まぁ当然の如くコードがとっ散らかっております。
折角なので、設計原則の勉強がてらこのグチャグチャなコードをリファクタリングしてみたいと思います。そもそも、githubのリポジトリを見れば分かるとおりMain.cppに全ての処理を記述するというかなりお粗末な構成になっているので、せめてファイルを分離して可読性を上げるくらいのことはしたいと思います。(闇雲に分割すればいいという話では無いでしょうけど……。良い感じの分け方を模索したいところです)

次に、音声メディアの埋め込みに挑戦してみたいと思います。今回の更新で背景画像を埋め込むことにより、メディアの埋め込み方は分かりました。今度は、BGMや効果音などの音声メディアをゲームに埋め込んでみたいと思います。ゲームと言えば「ピコピコ」と揶揄されていたように、ビデオゲームと音は切っても切り離せないもの。没入感や楽しさを上げるうえで重要な要素だと思うので、これも早いところノウハウを身につけたいところです。

さらに、このゲームをさらに発展させる方向の実装も考えています。制限時間やスコアを導入したり、落下物の種類を増やしたり、などなど。アイデアはある程度あるので、これらもいずれ実装してみたいと思います。

# 終わりに

- アドベントカレンダーでクリスマス当日の枠を抑えられたため、折角なのでクリスマスっぽい(？)記事を書いてみました
- この記事のためだけに、既存の自作ゲーム"Meat catcher"に突貫でクリスマス要素をぶち込みました。(愚か……！)
- 今後も(気が向けば)Meat catcher をアップデートしていこうと思います。突貫で書いて散らかったコードを整理したり、音を鳴らしたり、新しい機能を実装したり。etc...

TO BE CONTINUED ……？

(おまけ)
繰り返しになりますが、githubリポジトリは[こちら](https://github.com/ryota37/Siv3DFirstGame)になります。ゲームもこちらからダウンロードできるので、ぜひ遊んでみてください。(※今のところWindows版しかないです。すみません)

(追記)
記事を書き終えた後、「折角だからクリスマスモードの時は成功時のメッセージを変えればいいじゃん！」と言うことに気がつき、急遽メッセージを変えてみました。(記事冒頭の動画はこの変更の前に撮影したものなので、旧バージョンのメッセージが表示されています)
気になる方は、是非ダウンロードしてみて、クリスマスモードの時のメッセージを確認してみてください。