TadaoYamaokaの開発日記

個人開発しているスマホアプリや将棋AIの開発ネタを中心に書いていきます。

【バイブコーディング】レトロ戦略ゲームを作る その3(マップエディタ)

前回、レトロ戦略ゲームの画像素材を生成AIで制作したものに置き換えたことで、地形やユニットの識別ができるようになった。

今回は、マップを編集する機能を実装する。

マップエディタ

実装方針

マップをどのように実装するのがよいかCodexに相談したところ、対戦画面にはフィールドと操作パネルがあるので、UIはそのままで、シーンだけを別に管理するのが良いとのことだった。

質問:

マップエディタを追加したいです。
どのように実装するのが良いですか?

回答:

おすすめは、対局用 BattleScene に編集機能を足す のではなく、EditorScene を別で作る構成です。
今のコードベースだと、データはすでに ScenarioData (line 240) と JSON 変換があるので、エディタはこれを直接編集するのが一番素直です。

...(略)
切り替え操作

バトルシーンとエディットシーンの切り替え方法は、ボタン追加を提案されたが、ゲーム操作と混じると分かりにくくなるので、右クリックのコンテキストメニューでも良いか聞いたところ、良いと思うとのことだったので、その方針にする。

質問:

シーンの切り替えの操作はどうしますか?

回答:

おすすめは、常時見えている専用ボタン ではなく、文脈に応じた遷移 にすることです。

基本はこうです。

Battle -> Editor
Commands に Edit Map を置く
押すと「現在マップを編集するか」「初期マップを編集するか」を選べる

...(略)


質問:

右クリックしたときのコンテキストメニューを開いてそこからシーンを切り替えるのはどうでしょうか?

回答:

ありです。むしろこのゲームにはかなり合っています。

左クリック主体のゲームなので、右クリックを「場面切替や補助コマンドの入口」にするのは自然です。
常設ボタンを増やすより UI を汚しにくいのも利点です。

...(略)

実装

実装の詳細を検討させた後、初版の実装範囲が提示されたので、初版実装を依頼した。

実装結果

操作パネルに多少ボタンの重なりがあったので、修正を依頼したところ、基本的な操作ができる状態になった。

マップサイズ

マップサイズが固定で、変更できなかったため、マップサイズ変更の方法を相談した。

サイズを数値入力して、アンカーを設定して、拡張するのが良いとのことだったので、その方針を採用した。

マップサイズ変更の操作はどこから行うのが良いか相談したところ、Toolsにボタン追加を提案されたが、マップ編集と混じるのは良くないので、ヘッダーのマップサイズをクリックするのはどうか聞いたところ良いとのことだったので採用した。

ヘッダーのマップサイズをクリックするとポップオーバーで編集できるようになった。


表示領域外へのスクロール

マップサイズが表示領域外の場合、マウスの中央ボタンをパンすることでスクロールできるようになっている。
しかし、表示領域外に続いていてスクロールできることが視覚的にわかりにくい。

ChatGPTにスクリーンショットを与えてどのような方法があるか聞いたところ、「edge fade」と呼ばれる手法が良く使われるとのことだった。
そこで、Codexに「edge fade」の実装を依頼した。

表示領域の枠の黒のマージン部分に、フェードアウトする形でマップの続きが描かれるようになった。
これで、スクロール可能であることが視覚的にわかるようになった。


まとめ

Codexと相談しながらマップエディタを実装した。
マップサイズ変更やスクロール処理も相談しながら実装することで、わかりやすいUIが実装できた。
マップの保存や読み込みもできるようにしたい。

【バイブコーディング】レトロ戦略ゲームを作る その2(画像素材)

前回、レトロ戦略ゲームの基本部分を生成した。

前回は、サイズなどの形式だけ合った素材をコードインタープリタで生成したので、何のユニットかわからなかった。
今回は、画像素材を生成AIで生成し、既存の素材を置き換えることを試した。

ドット絵

画像素材は、16x16のドット絵という仕様になっている。
画像生成AIの出力サイズは、1024x1024などの固定サイズになっているため、直接16x16サイズで生成はできない。
そこで、生成された画像を切り抜いて、画像編集ソフトで縮小することを試した。

しかし、16x16で縮小した後、ゲーム画面では64x64に拡大して表示しているので、クオリティが低くなった。
16x16で、何のユニットかわかるようにするのはうまくいかず、ドット絵は職人芸だと思った。

ゲーム画面では64x64に拡大しているので、はじめから64x64にした方がよいので、画像素材の仕様を64x64に変更した。

シートで出力

ChatGPTでゲーム素材を生成する良い方法を聞いたところ、一つ一つの画像を生成するよりも、シートで出力すると効率が良いとのとこだったので、シートでまとめて出力するようにした。
ChatGPTにそのためのプロンプトを出力してもらった。

Generate a sprite sheet with 16 strategy game units.
pixel art
top-down
64x64 grid

### Units
- Every file below is required and must be `64x64`, grayscale, and transparent around the sprite.

| File | What to draw |
|---|---|
| `infantry.png` | Foot soldier with firearm. Must clearly read as a person, not a vehicle or icon. |
| `engineer.png` | Support infantry. Can include tool / pack / specialized silhouette distinct from regular infantry. |
| `tank_a.png` | Light tank silhouette. Smallest and simplest tank class. |
| `tank_b.png` | Medium tank silhouette. Heavier than `tank_a`. |
| `tank_z.png` | Heavy tank silhouette. Largest land tank class among regular tanks. |
| `turret.png` | Stationary or emplacement-like gun platform. Must not read as a mobile tank. |
| `sp_artillery_a.png` | Self-propelled artillery, lighter class. Long barrel and rear support silhouette recommended. |
| `sp_artillery_b.png` | Heavier self-propelled artillery. Larger body and heavier gun than `sp_artillery_a`. |
| `anti_air_missile.png` | Anti-air missile vehicle. Missile pod / launcher silhouette should be clear. |
| `anti_air_tank.png` | Anti-air gun vehicle. Turret / gun cluster should read as anti-air role. |
| `rocket_launcher.png` | Ground rocket artillery. Multi-tube launcher silhouette recommended. |
| `armored_car.png` | Fast wheeled armored vehicle. Must read as lighter and faster than tanks. |
| `supply_truck.png` | Logistics vehicle. Cargo bed / container / supply silhouette should be visible. |
| `fighter_a.png` | Fighter aircraft, lighter class. Fast and compact air silhouette. |
| `fighter_b.png` | Fighter aircraft, heavier class. Distinct from `fighter_a` in wing/body proportions. |
| `bomber.png` | Bomber aircraft. Heavier body and payload-oriented silhouette. |

## Style Guide

- 2D pixel art
- 64x64 pixels
- top-down strategy game style
- similar readability to Game Boy Wars / Advance Wars
- slightly cartoon but military readable
- strong silhouettes
- minimal shading (2~4 color ramps)
- clean outline
- no perspective tilt
- no text
- no UI frame
- neutral faction color

### Unit Style

- grayscale only
- transparent background
- clear silhouette
- centered in 64x64
- pixel-art scale readable

このプロンプトで出力された画像は以下の通り。
そのまま使えそうなクオリティで出力された。
色は、ゲーム側でサイドカラーに塗るのでグレースケールになっている。

同様の方法で、残りのユニットと地形の画像素材を生成した。
地形素材は、パースが付いたり、3Dになったりしたので、出力を見て禁止事項を加えて生成し直す必要があった。

加工

生成された画像のサイズは、64x64にはなっていないため、切り抜きと、縮小は画像編集ソフトで手作業で行った。
この作業も自動化できるとよいが、現状コードインタープリタでは難しい。

ゲームの素材置き換え

手作業で加工した画像素材を置き換えた結果、以下のようなゲーム画面になった。

ちゃんとユニットが識別できるようになっており、クオリティも十分である。

占領の色表現

建物の画像が、偶然屋根の部分が赤色になっていたため、これをそのまま活かして、占領した際の色として使うことにした。
青側が占領した場合は、赤系を青系に変えて、未占領はグレーにするようにCodexに変更を依頼した。

占領状態が色で表現されるようになった。


まとめ

画像生成AIで、ゲームの画像素材の制作をおこなった。
ChatGPTの提案でユニットを個別ではなくスプライトシートとしてまとめて生成する方法を採用したことで実用的な画像素材を作成できた。
手作業で切り抜き・サイズ調整は必要だったが、十分使えるクオリティのものが生成できた。

次は、マップエディタを実装したい。

【バイブコーディング】レトロ戦略ゲームを作る

ゲームボーイウォーズというターン制戦略ゲームがある。

1991年に発売されたゲームボーイ向けのゲームだが、当時結構遊んでいた。
続編で、ゲームボーイウォーズターボや、ゲームボーイウォーズ2といったシリーズが発売されている。

あらかじめ用意されているマップでCPUと対戦する形だが、マップをすべてクリアすると飽きるのでもっとマップが欲しいと思っていた。
いつか自作して、好きなマップで遊べるようにしようと思っていたが、実際作るのは大変すぎるのであきらめていた。

ここ数か月、GPT-5.3-CodexやGPT-5.4を使っていて、驚異的なコーディング能力を体験したことで、以前に作りたかったソフトもノーコードで作れる気がしているので、試してみることにした。

方針

  • コードは書かない
  • ゲームルールはDeepResearchしたものを入力する
  • ゲームルール以外は生成AIでオリジナルなものを作る(画像やUIを真似ると著作権上問題があるため)
  • Windowsアプリとする

ゲームルール

GeminiのDeepResearchで、調べたゲームルールをマークダウン形式でリポジトリに配置して、Codexが読めるようにした。
内容の正しさは確認していない。

実装方針

まずは、どのような方針で実装するかをCodexに出力させた。

  • ゲーム基盤(ルールを実装)
  • ゲームエンジン(UI状態、シーン管理など)
  • CPU思考
  • コンソールアプリ

から始める作ることを推奨された。

GUIから作らず、コンソールから作るのは実際の開発でも行うので理にかなっている。

生成

準備として、Visual Studioで、C++/WinUI3のブランクソリューションを作成して、空のプロジェクトを用意した。

そこから、以下のプロンプトで生成した。

design/deep-research-report.md にあるゲームボーイウォーズの仕様を元に、クローンゲームを作りたい。
商標を避けるためゲーム名は「Hex Front」とする。

まずは、Core部分と簡単なAIを実装して、コンソールでテストプレイできるまでを実装してください。

### プロジェクト構成

1. HexFront.Core

種類: C++ Static Library
役割: ゲームルール本体

2. HexFront.AI

種類: C++ Static Library
役割: CPU思考と探索
依存: HexFront.Core

3. HexFront.Console

種類: C++ コンソールアプリ
役割: コンソールでの最低限のゲームプレイ
依存: HexFront.Engine, HexFront.AI, HexFront.Core

今後、GUIで本格的なゲームを実装することを考慮して、CoreとAIはコンソールに依存しないようにすること。
C++20のモダンな実装とすること。

コンソールアプリ

一度目の生成で、コンソールで遊べる状態まで出力された。

合法手生成、CPUまで実装されている。

Hex Front console prototype

Commands
  show           show board and summary
  units          list current side units
  actions <id>   list actions for one unit
  builds         list production actions
  all            list all legal actions
  do <index>     execute from last listed actions
  undo           undo last action
  redo           redo last undone action
  history        list executed actions
  replay [index] show replay list or one replay frame
  surrender      concede immediately
  end            end the current turn
  ai             let AI play the current side
  help           show this help
  quit           exit

Day 1  Turn: Red  Funds(R/B): 14000 / 14000

Board
       0   1   2   3   4   5   6   7   8
  0  ... .mt ... ... ... ... ... ... ...
  1    ... R05 .fo ... .cy ... .fo B12 ...
  2  ... R01 .fo ... .rv ... B08 Bcy B10
  3    .rd Rhq R03 .rd .br .rd B09 Bhq .rd
  4  R04 Rcy R02 ... .rv ... Bfa B07 ...
  5    ... R06 .fo ... .cy ... .fo B11 ...
  6  ... ... ... ... ... ... ... .mt ...

Units for Red
#1 Infantry hp=100 str=10 ammo=9 fuel=99 pos=(1,2) ready
#2 Engineer hp=100 str=10 ammo=3 fuel=70 pos=(2,4) ready
#3 Tank B hp=100 str=10 ammo=4 fuel=32 pos=(2,3) ready
#4 Supply hp=100 str=10 ammo=0 fuel=60 pos=(0,4) ready
#5 Transport Heli hp=100 str=10 ammo=8 fuel=60 pos=(1,1) ready
#6 SP Art B hp=100 str=10 ammo=4 fuel=30 pos=(1,5) ready

GUI版の準備

GUI版は、画像素材が必要になるため、いきなり生成を依頼しても画像素材まで作ることは期待できない。
そこで、事前準備として提供が必要なものをリストアップさせて、どのような形式で配置すればよいか出力させた。

地形とユニットの画像素材の形式と一覧が提示されたので、デザイナーに発注するための詳細な指示書を出力させた。
デザイナーに発注するわけではなく、生成AIで出力するためである。
具体的な色の指定やサイズ、ファイル名などの納品形式もすべて記載されている。

画像生成AIで一つずつ画像を生成するのは大変なので、いったんちゃんとした素材を作る前に、とりあえず形式が合った素材さえあれば良いので、いったんその指示書を元にコードインタープリタで素材一式を作成した。
すべてのファイルが指定したファイル名で作成されて、メタ情報が記載されたxmlと合わせたzipファイルが生成できた。

zipを展開して、指示されたアセットフォルダに格納した。

GUI版生成

準備ができたので、GUI版を生成した。

design/deep-research-report.md にあるゲームボーイウォーズの仕様を元に、クローンゲームを作りたい。
商標を避けるためゲーム名は「Hex Front」とする。

ゲームの基礎部分と、テスト用のコンソール版はすでに実装している。

GUI版をHexFrontフォルダにあるC++/WinRT(WinUI3)のプロジェクトに実装してください。

### GUI技術
- SwapChainPanelで描画領域を作る
- 文字の描画にはDirectWriteを使用
- ウィンドウのクライアントサイズを96DPI基準で、1024×768とする
- DPIによらず見た目上同じサイズになるようにする
- DPIに依存しない実装例:
  - DPIスケーリングは SwapChainPanel + DXGI + D2D の座標系を分離して実装する。
  1. レイアウト座標はDIP、SwapChainバッファは物理ピクセルで扱う。
  2. スケール値は SwapChainPanel.CompositionScaleX/Y を正とし、これを唯一の基準にする。
  3. SwapChainのWidth/Heightは round(ActualWidth * CompositionScaleX), round(ActualHeight * CompositionScaleY) で計算する。
  4. ID2D1DeviceContext::SetDpi と D2DターゲットBitmapのDPIは 96 * CompositionScaleX/Y を使う。
  5. IDXGISwapChain2::SetMatrixTransform は inverse scale(1/CompositionScaleX, 1/CompositionScaleY)を設定する。
  6. SizeChanged / CompositionScaleChanged / XamlRoot.Changed の各イベントでサイズ依存リソースを再計算する。
  7. 描画ロジック(線のX/Y計算)はDIPのみを使い、ピクセル値を混在させない。

### 画面一覧
- 初期実装では対局画面を表示して最低限プレイできること
- 今後、タイトル、マップ選択、シナリオ・マップエディットなどが追加される

### 操作仕様
- マウス主体

### ゲーム素材
- Terrain: `HexFront/Assets/Game/Terrain/`
- Units: `HexFront/Assets/Game/Units/`
- UI: `HexFront/Assets/Game/UI/`
- Metadata: `HexFront/Assets/Game/Metadata/`
  - Asset catalog: [assets.json](/d:/src/HexFront/HexFront/Assets/Game/Metadata/assets.json)
  - `assets.json` is the source of truth for filenames, kinds, sizes, origins, and `tintable` flags.
- 1マス16x16で納品されているため、ゲームでは64x64に整数倍拡大して表示する(フィルタは nearest neighbor)

### 基本部分実装済み
- HexFront.Core: ゲームルール
- HexFront.Engine: ゲームエンジン(コンソール/GUI 非依存)
- HexFront.AI: CPU思考と探索
- HexFront.Console: コンソール版(テスト用)

### 仕様
- design/deep-research-report.md: ゲームボーイウォーズ仕様
- design/additional-report.md: ゲームボーイウォーズ仕様(追加)
- design/24x24damage-matrix.md: ダメージ表
- design/gui-art-brief.md: ゲーム素材作成指示書

生成結果

Visual StudioのDeveloper PowerShellからCodexを起動しているので、ビルドも自動で行ってコンパイルエラーも解消してくれる。

生成されたコードを実行したところ、ルール通りに基本的な操作ができるゲームになっていた。


まとめ

生成AI(GPT-5.4)を使い、昔遊んだ『ゲームボーイウォーズ』風のゲームをコードを書かずに作れるか実験した。
DeepResearchで調べたルールを入力するだけで、コードを一切かかず、GUI版で実際に動作するゲームが完成した。
これに、マップエディタを追加すれば、以前作りたかったものが完成しそうである。

ただし、ゲームとして完成度を高めるには、画像素材のクオリティを上げて、UI操作を改善する必要がある。
音楽と効果音に関しては、生成AIで作るには課題がありそうである。

商用レベルまでクオリティを上げるのは難しいと思うが、そこそこの遊べる状態まで作って、ストアで公開するつもりである。

【Androidアプリ】Audio Pitch Tuner

先日、Microsoft Storeでリリースした、Pitch Tunerを、Androidに移植して、Google Playで公開した。

play.google.com

「Pitch Tuner」は使われていたので、「Audio Pitch Tuner」とした。

バイブコーディング

C++/WinUI3からKotlin/Jetpack Composeへの移植を、GPT-5.4で行った。
さすがに、UIは表示が崩れた機能面は1回で移植できた。
UIも数回の指示で修正でき、コードは1行も書いていない。

これくらいの規模のアプリだと、.NET MAUIとか、React Nativeとかクロスプラットフォームフレームワークいらずである。

アプリ開発の生産性は爆上がりなので、ストアにアプリが溢れかえりそうである。

Microsoft Storeでコマンドラインツールを公開する

以前にVectorで公開していた「WAVファイル変換・抽出ツールwavext」を、Microsoft Storeで公開した。

apps.microsoft.com

wavext

mp3やflacなどの圧縮音声ファイルや、動画から、音声をWAVとして抽出するツールである。
サンプリングレート変換やステレオ/モノラル変換、量子化ビット数変換も行える。

以前はDirectShowを使用していたが、DirectShowはMedia Foundationに置き換えられているので、Media Foundationで実装し直した。
GPT-5.4を使って実装した。

コマンドラインツールの公開方法

Microsoft Storeは、コマンドラインツールも配布できるようになっている。

Microsoft Storeからインストールすると、環境変数PATHを通すことなく、コマンドプロンプトやPowerShellからコマンドを実行できるようになる。

Visual Studioで、ソリューションにMSIXパッケージプロジェクトを追加して、コンソールアプリケーションのプロジェクトを参照設定して、Package.appxmanifestに、

      <Extensions>
        <uap3:Extension
          Category="windows.appExecutionAlias"
          Executable="wavext\wavext.exe"
          EntryPoint="Windows.FullTrustApplication">
          <uap3:AppExecutionAlias>
            <desktop:ExecutionAlias Alias="wavext.exe" />
          </uap3:AppExecutionAlias>
        </uap3:Extension>
      </Extensions>

を追加すればよい。

まとめ

Microsoft Storeは、環境変数PATHとか気にする必要がないのでコマンドラインツールの配布にも便利である。
MSIXの設定が面倒そうだと思っていたが、やってみれば全然難しくなかった。
今後はこの方法でコマンドラインツールを公開していこうと思う。

【Windowsアプリ】Vocal Pitch Monitor for Windows

先日改善したチューナーアプリのピッチ解析をベースに、AndroidとiOSでリリースしているVocal Pitch MonitorのWindows版を作成して、Microsoft Storeでリリースした。

apps.microsoft.com

Windows版では、スマホ版の機能に加えて、録音時間を最大10分まで拡張し、mp3などの圧縮音声ファイルの読み込みにも対応した。

先日、Kontakt5とマイク収録データから学習したモデルによりピッチ解析も向上している。

今後、音源分離モデルボーカル抽出した音声を使って、ボーカル音声を増やしてさらに精度を上げるつもりである。

Microsoft Storeの利用者数はGoogle PlayやApp Storeと比べると圧倒的に少なく、大きな利用は期待していない。
週末にGPT-5.3-Codexを用いてさくっと移植できたので、せっかくなので公開することにした。

GPT-5.3-Codexがなければ、Windows版を開発することはなかったと思う。

Python から VST2 インストゥルメント(VSTi)を読み込んで音を鳴らす その3

前回、Kontakt5で生成した音色×音階のデータを使うと精度が下がったことを記載した。

その後、データクリーニングを行ったところ、精度が下がらないことがわかった。

自動データクリーニング

ACFベースのピッチ解析とSwiftF0のどちらも正解のMIDIと異なるデータを除外した。
SwiftF0がオクターブエラーを起こすことあるため、両方で誤るという条件にしている。

また、マイクで収録した音声についても、同じようにデータクリーニングを行った。
しかし、ベースの4弦でオクターブエラーが起きやすくなったため、原因を調べたところ、ACFのピッチ解析とSwiftF0のどちらもオクターブエラーを起こしていた。
オクターブエラーのデータを残すため、SwiftF0の出力ピッチを1/2、1/4した値が、MIDIの周波数に近い場合は、そのデータを残すことにした。
これにより、ベースの4弦でもオクターブエラーを起こさず正しくピッチ推定できるようになった。

マイク収録音の追加

エレキベース、エレキギター、アコースティックギターで、検証を行い、特定の弦でオクターブエラー起きる場合は、録音データを増やすことで、すべての弦でオクターブエラーを起こさずピッチ推定できるようになった。
今後も、データを増やすだけで精度が担保できるようになる。

チューナーアプリに組み込み

Kontakt5のデータとマイク収録データを合わせて、決定木を学習し、チューナーアプリに組み込んだ。

Microsoft Storeで更新版を公開した。

apps.microsoft.com

様々な楽器で安定してピッチ解析できるチューナーアプリになったと思う。

ピッチの誤差

正確なピッチに対して誤差がどれくらいあるか調べた。

正弦波を入力して、誤差(cent)を測定した結果は以下の通り。

C2以下の低音域で、最大4centの誤差がある。
C2以上では、1cent以内に収まっている。

チューナーの表示は、前のフレームと指数移動平均を取って表示しているため、低音域の誤差はある程度相殺されているはずである。

低音域の誤差を減らすには、窓長を長くする必要があるが、時間分解能は下がるため、バランスを取って決めることになる。
現状は、約340ms の窓長としている。

まとめ

前回Kontakt5生成データで精度低下が見られたが、ACFとSwiftF0の両方で誤判定したデータを除外する自動クリーニングにより精度を維持できることを確認した。
さらに、オクターブエラー対策(SwiftF0出力の1/2・1/4補正)と録音データの拡充により、各弦で安定したピッチ推定を実現した。
改善した決定木モデルをチューナーアプリへ組み込み、Microsoft Storeで更新版を公開した。