【個人ゲーム開発25日目】戦術SLG制作

今日は「ゲームとしての完成度」を上げる日

今日は戦術シーンを中心に、演出・AI改善・ヘルプ機能・エディタツールと、 幅広いタスクを一気に片付けました。
数をこなした日というより、それぞれが「ゲームとしての完成度」に直結する実装ばかりで、 やりごたえのある一日でした。


■ ミッション結果演出の実装

バナーがバーンと出てくる

これまでステージ終了時は、演出も何もなくそのまま結果シーンに飛んでいました。 いわゆる「シュール」な遷移です。
さすがにこれはまずいと思い、本日ようやく手を入れました。

実装したのは4種類のバナー演出です。

  • 敵全滅 → MISSION COMPLETE
  • 敵拠点占領 → BASE CAPTURED
  • 味方全滅 → MISSION FAILED
  • 味方拠点奪取 → BASE LOST

バナーは大きいスケールからEaseOutQuadで縮みながらフェードインします。 勝利時はファンファーレSE、敗北時は重厚なSEが鳴り、 2.5秒ほど余韻を残してから結果シーンへ遷移します。

BASE LOSTの髑髏バナーはなかなか気に入っています。 プレイヤーへのプレッシャーとして十分機能してくれそうです。

実装上の工夫として、GameOverReason enumを新設して終了理由を管理しています。 CheckGameOver()からDelayedEndGame()に理由を渡すことで、 どのバナーを出すかを正確に制御できます。

CanvasはPhaseCanvasと独立したMissionClearCanvasを新規作成しました。 PhaseDisplayManagerがPhaseCanvasのCanvasGroupをalpha=0にする処理があるため、 同じCanvasに乗せると一緒に消えてしまうことが判明したためです。 Canvasを独立させるという判断は、今後も使えるパターンだと思います。

出来上がりはこんな感じ。


■ エンディングシーン強化

スタッフロールが「ちゃんと動く」ようになった

昨日仮実装したエンディングシーンですが、 スクロールが途中で止まる、一瞬テキストが見える、といった問題がありました。
本日はこれを全部潰しました。

スクロール高さの取得にはendingText.preferredHeightを使用しています。 ContentSizeFitterの更新待ちによる高さ取得失敗を回避するための措置です。
初期の一瞬チラ見えはCanvasGroup.alpha = 0で対応しました。 SetActive(false)にするとレイアウト計算が止まってしまうため、 alphaだけ0にする方式が正解でした。

スクロール完了後はFinalViewがフェードインして表示されます。 THANK YOU背景画像にはHLSLシェーダーで周辺減光効果(ビネット)を実装しました。 上下は狭く、左右は広めに黒くフェードする設定です。

FinalViewのお知らせパネルにはデモ版である旨の告知と、 ブログ・X(旧Twitter)の宣伝を入れています。 Steamストアページは近日公開予定として記載しています。


■ ヘルプウィンドウの実装

ゲームのルールを説明する場所ができた

戦術シーンのヘルプウィンドウに、ついに中身が入りました。
デモ版でヘルプが空のままというのはさすがにまずいと思っていたので、 本日対応しました。

実装したセクションは6種類です。

  • 基本ルール(勝利・敗北条件・占領)
  • ユニットタイプ(歩兵・装甲車・戦車・駆逐戦車・自走砲・野砲)
  • ZOC(Zone of Control)
  • 戦闘効果(地形・支援・包囲)
  • 地形について
  • ステージ評価(C〜SSの5段階)

日本語・英語・中国語の3言語に対応しています。
テキストはHelpContentManagerスクリプトで言語判定して流し込む方式で、 GameSessionManager.SystemData.languageを参照しています。
設定画面で言語を切り替えると、次回ヘルプを開いたタイミングで自動的に反映されます。

ヘルプウィンドウ上でマウスホイールを操作するとマップのズームも動いてしまう問題があったので、 CameraControllerIsZoomLockedフラグを追加して対応しました。


■ 敵AI改善

渋滞が解消されて動きが自然になった

敵ユニットが通路で一列に並んで渋滞する問題を修正しました。

根本原因は経路探索の方式にありました。 従来は探索した経路のステップ数最小で経路を選んでいましたが、 地形コストを考慮した移動コスト最小の経路で選ぶGetPathMinCost()を新設しました。
直線距離ベースの選択は地形を無視して山に突っ込む副作用があったため、 A*で地形コストを正確に計算する方式としています。

また経路上の最遠マスに同陣営ユニットがいる場合のフォールバック処理も追加しました。 移動可能範囲内から目標への移動コストが最小のマスを探して移動します。 これにより渋滞を回避しつつ、適切な位置へ散開するようになりました。

あわせてStationaryパターンのバグも修正しました。 攻撃範囲にプレイヤーユニットが来ると移動してしまう挙動があったため、 移動実行前にStationaryチェックを追加しています。


■ 爆発エフェクトの3段階化

大きいユニットが派手に爆発するようになった

戦闘画面の爆発エフェクトとSEを、ユニットサイズに応じて3段階に分けました。

  • サイズ1 → Small(小爆発)
  • サイズ2〜3 → Medium(中爆発)
  • サイズ4〜5 → Large(大爆発)

UnitDataには以前からサイズ情報(unitSize、1〜5)を持たせていたので、 そこを参照して分岐するだけで済みました。
先行して設計しておいた恩恵が出た形です。

爆発画像はSmall・Medium・Largeの3種を新規作成しました。 Largeは大きなキノコ雲が立ち上がる迫力のある演出になっています。


■ マズルフラッシュ位置調整エディタツールの作成

ドラッグするだけで位置が決まる

マズルフラッシュの発生位置(muzzleOffset)の調整が、 これまでは数値を手入力して実行確認を繰り返す作業でした。
ユニット数が25種あることを考えると、かなりの手間です。

そこでUnityEditorの拡張ウィンドウとしてMuzzle Offset Editorを作成しました。

  • メニューの Tools → Muzzle Offset Editor で起動
  • ProjectウィンドウでUnitDataを選択すると自動で対象ユニットが切り替わる
  • プレビューエリア上でドラッグするだけでマズルフラッシュ位置を調整できる
  • 「保存」ボタンでUnitDataに書き込み(Undo対応)

スプライトのPixels Per Unit(100・200・300と混在)を自動補正しているため、 どのユニットでも正確な座標で設定できます。
実際に使ってみると、1ユニットあたり数秒で調整が完了します。 こういうツールを先に作っておくと後がずっと楽ですね。


■ その他の修正

  • 行動終了ユニットの色をDoneColorのみ適用に変更(チームカラーが出すぎていた)
  • タイトルシーンのアニメーション中にボタンが連打できてしまうバグを修正(CanvasGroup.blocksRaycasts制御)
  • HPテキストがユニットスプライトの裏に隠れる問題を修正(Order in Layer調整)
  • Pathfinding.cs・TurnManager.csのコードを整理(コメント日本語化・セクション分け)

■ シーン完成状況

シーン状況
タイトル✅ 完成
ブリーフィング✅ 完成
戦術(メインゲームプレイ)🔧 実装中
結果✅ 完成
エンディング✅ ほぼ完成

執筆後記
今日は本当に盛りだくさんな一日でした。
特に印象に残っているのはマズルフラッシュ位置調整ツールです。 「ちょっと作るのに時間かかるけど後が楽になる」という判断で作ったのですが、 実際に使ってみたら一発で「作って正解」と確信しました。
ツールに投資する判断ができるようになってきたのは、 個人開発の経験値が積み上がってきた証拠かもしれません。
敵AIの渋滞解消も地味ですが、マップを眺めていて気持ちよくなりました。 敵がちゃんと散開して攻めてくる。それだけでゲームの緊張感が全然違います。
デモ版完成まで残りのタスクを一つずつ潰していきます。

【個人ゲーム開発24日目】戦術SLG制作

結果&ブリーフィングシーン演出完成

画面が「ゲームらしく」なってきた日

今日は演出強化に集中しました。
結果シーン・ブリーフィングシーンの両方をポリッシュして、それぞれ「完成」と言える状態まで持っていけました。
数値が勢いよくカウントアップされたり、要素が順番にスライドインしてきたり、
画面を見るたびに「これはゲームだ」という感覚が強くなっています。


■ 結果シーンの演出強化

カウントアップ演出の実装

これまで一瞬で表示されていた数値に、カウントアップ演出を追加しました。
ターン数 → 残ユニット数 → 残戦力値の順に順次カウントアップします。

工夫した点が一つあります。
ターン数やユニット数のカウント間隔は、実際の数値に比例して自動計算しています。
ターン数が5なら5回、ユニット数が3なら3回きっちりSEが鳴るイメージです。
一方、戦力値は桁が大きいので固定間隔でピロピロと鳴り続ける形にしました。
この使い分けが地味に効いています。

カウントアップにはSEも実装しました。
カウント中のピロ音と、完了時の確定音の2種類です。
SceneSEControllerのキー文字列で管理しているので、素材が揃ったらいつでも差し替えられます。

ランク画像のフェードイン演出

カウントアップが全部終わったタイミングで、ランク画像がドーンと登場します。
スケール大→等倍へ縮みながらフェードインするEaseOutQuadのアニメーションです。
テキストで「A」と出るより、ずっと達成感が出ますね。

勝敗表示のイメージ化

「MISSION COMPLETE」「MISSION FAILED」のテキスト表示を廃止して、
専用ラベル画像に切り替えました。
金属プレート風のデザインで、勝利時と敗北時で雰囲気が変わります。

完成した結果シーンはこんな感じです。

デバッグ機能の追加

戦闘結果データを得てからでないと動作確認できない、というのが地味に不便だったので、
ResultScene単体で再生したときに自動でダミーデータをセットする機能を追加しました。
#if UNITY_EDITOR で囲んでいるので、ビルド時には完全に消えます。
こういう小さな開発体験の改善が積み重なって効いてきます。


■ ブリーフィングシーンの演出強化

段階的登場演出の実装

これまで一瞬で全部表示されていたブリーフィング画面に、段階的な登場演出を追加しました。

  1. タイトル:上からスライドイン
  2. ステージイラスト:左からスライドイン → フェードイン
  3. ストーリーテキスト:右からスライドイン → フェードイン
  4. 戦力情報枠:フェードイン → カウントアップ
  5. ボタン類:出撃ボタンは右下から、戻るボタンは右上から斜めにスライドイン

左右から交互に入ってくる動きが、ちょっとした緊張感を演出しています。

戦力カウントアップ演出

ブリーフィング画面の戦力情報でもカウントアップを実装しました。
こちらは少し凝った仕様になっています。

敵味方を同時にカウントスタートして、戦力値に比例した時間でカウントが終わるという動きです。
つまり敵が強いステージほど、敵側の数字がなかなか止まらない。
「こいつら強いぞ…」という緊張感が自然と演出できます。
数字を調整してみたところ、初級ステージと最終ステージで
全然違う雰囲気になって良い感じです。

完成したブリーフィングシーンはこんな感じです。


■ バグ修正

Stage1のベストランクが表示されない

ステージ選択画面でStage1だけランクが「-」になるバグがありました。
原因はSystemSaveData.GetRank()内の判定ロジックです。

// 問題のあったコード
if (record.stageNum == 0) return "-";

FirstOrDefaultでレコードが見つからない場合、structのデフォルト値が返りstageNumが0になります。
ところがStage1のstageNumも0始まりで0なので、記録があっても「未記録」と判定されていました。
Stage2以降(stageNum 1〜)は影響なし、という実に分かりやすいインデックスのワナです。

// 修正後
if (!bestRecords.Exists(r => r.stageNum == stageNum)) return "-";

存在チェックを先に行う形に変更して解決しました。

ConfirmDialogがヒエラルキー非表示時に動作しない

編集中に邪魔なのでヒエラルキーでConfirmDialogプレハブを非表示にしておくと、
実行時にダイアログが表示されない問題がありました。
原因はプレハブのルートごと非アクティブにするとAwakeが呼ばれないため、
Instanceがnullのままになること。

解決策はシンプルで、ルートは常にアクティブにして、子オブジェクト(DialogContent)を非アクティブにする構成に変更しました。
他のパネル類が同じ構成で問題なく動いていたので、これに合わせた形です。


■ エンディングシーンの仮実装

結果・ブリーフィングが完成したので、エンディングシーンにも着手しました。
Animationによる仮実装を全面的にコルーチンスクロール方式に書き直しています。

エンドロールの内容はこんな構成を予定しています。

  • MISSION RESULTS:全ステージのベスト成績をセーブデータから自動取得
  • STAFF:制作者・BGM/SE提供元・AI活用(Claude/ChatGPT/Gemini)クレジット
  • SPECIAL THANKS
  • FIND US:ブログ・Steamページ等
  • Thank you for playing. ― FIN ―

スクロール完了後にタイトルへ戻るボタンが表示される仕様です。
デザインと内容の肉付けは後日行います。

AI活用についてクレジットに明記するのは、今の時代の誠実な作り方かなと思っています。


■ シーン完成状況

シーン状況
タイトル✅ 完成
ブリーフィング✅ 完成(本日)
戦術(メインゲームプレイ)🔧 実装中
結果✅ 完成(本日)
エンディング⚠️ 仮実装

執筆後記
今日は「演出を作る楽しさ」を久しぶりに味わえた気がします。
数値がカウントアップされる、要素が順番に登場する、
そういう小さな演出の積み重ねでゲームの「体験」が大きく変わりますね。
特にブリーフィングの戦力カウントアップは、
仕様を考えている段階から「絶対面白くなる」と確信していた部分で、
実際に動かしてみてその通りになったので満足しています。
残るは戦術シーンの仕上げとエンディングの肉付け。
デモ版完成まで、もうひと踏ん張りです。

【個人ゲーム開発23日目】戦術SLG制作

ブログ整備と、多方面バグ修正祭り

今日はまたいつものペース(1日10時間以上)で開発再開です。
昨日は私用で半日作業だったので、その分、今日は盛りだくさんな一日になりました。


■ 本日の開発状況

1. ブログ・Web環境の整備

ゲーム本体の作業に入る前に、ブログ周りの整備を進めました。

サイドバープロフィール追加

開発ブログにプロフィール小窓を設置しました。
アイコン画像・名前・bio・Xフォローボタンをまとめたウィジェットです。
WordPressのカスタムHTMLウィジェットとCSSで実装しています。

実装中にちょっとしたトラブルがありました。
Xへのリンクを含むHTMLを保存しようとすると「保存された」とは出るのに
実際には反映されない、という現象が発生。
WAFかセキュリティプラグインを疑って調査しましたが、
最終的な原因は「X(エックス)」のUnicode文字でした。
サーバーのフィルターが不審なバイト列として検知していたようです。
テキストを「X でフォローする」に変えたら一発で解決しました(笑)

仕上がりはこんな感じです。

ステージ1のプレイ映像(3分36秒)を限定公開でアップしました。
ブログへの埋め込み用です。
デモ版リリースのタイミングで公開に切り替える予定です。


2. 結果シーンの改修

ステージ選択モード対応

ステージ選択でクリアした場合、「次のステージへ」ボタンを非表示にする対応を行いました。
ステージ選択はハイスコア更新専用の機能なので、そのまま次ステージへ進めてしまうのは仕様上おかしいためです。
GameSessionManager.CurrentModeの判定を1箇所追加するだけで済みました。

ランク画像の実装

評価ランク(C/B/A/S/SS)をテキストではなく画像で表示するように変更しました。
ミリタリー×スチームパンク調のランクバッジを新たに作成しています。
錆びた金属・歯車・月桂樹をモチーフにしたデザインで、ゲームの雰囲気に合わせました。

表示項目の追加

残ユニット数(終了時 / 開始時)と戦力(終了時 / 開始時)の表示を追加しました。
リザルト画面で何がわかるか、が増えると達成感も出てきますね。

多言語対応

「次のステージへ」「作戦終了」のボタンラベルが日本語固定になっていたので、
英語・中国語にも対応しました。
LocalizeText.SetDynamicText()を使う方式で統一しています。


3. 戦闘演算のバグ修正

地形効果の適用順序が誤っていた

地形防御ボーナスの計算順序にバグがありました。

誤った順序:(基本DEF + 支援DEF)× 地形倍率
正しい順序:(基本DEF × 地形倍率)+ 支援DEF

地形効果はユニット固有の能力値に対して乗算するものですが、
誤った実装では支援効果も含めた値に地形倍率が掛かっていました。
結果として地形効果の高い場所で支援ありで守ると防御力が過大になる問題が発生していました。

この修正はゲームロジック側(UnitController.cs)と戦闘演出側(BattleManager.cs)の
両方に適用しています。演出の数値表示も実際の計算と一致するように直しています。


4. 戦力計算式の統一

式がコード各所にバラバラに記述されていた

戦力計算式が複数のスクリプトに分散して記述されており、しかも式が統一されていませんでした。

  • BattleFieldEvaluatorHP × (ATK + DEF)
  • GameSessionManagermaxHP × ATK × DEF(← 誤り)
  • TurnManagerCurrentHP × ATK × DEF(← 誤り)

UnitController.GetBattlePower()という共通メソッドが既に存在していたので、
全箇所をこのメソッド経由に統一しました。
これでSITREPウィンドウの損耗率が開始直後から95%になっていた謎が解けました(笑)

どうも以前AIにコード生成を依頼した際に別の式が混入したようで、
発見が遅れましたが今回の整理で綺麗になりました。


5. ステージ評価基準の拡張

残ユニット数・損耗率をランク評価基準に追加

これまでSSランクの基準はターン数のみでしたが、
残ユニット数と損耗率も評価基準として追加しました。

StageDataの評価基準構造体に以下のフィールドを追加しています。

  • targetRemainingUnitsSS:SSランク基準残ユニット数
  • targetLossRateSS:SSランク基準損耗率

ランク算出は3項目の平均で決まる方式です。
ターン・損耗率・残ユニット数それぞれが評価され、総合的にランクが出ます。


6. 敵AI改善

渋滞で動けない敵ユニットの問題

複数の敵ユニットが同じ経路で移動しようとして、先行ユニットが詰まっていると
後続が移動できずその場に留まる問題がありました。

根本原因は「経路を1本探してその上を辿る」という実装で、
経路上が塞がれた場合の代替ルート探索がなかったことです。

FindApproachPath()にフォールバック処理を追加しました。
最短経路上を一歩も進めなかった場合、移動可能マスの中から
経路ゴールに最も近いマスを選んで移動する、という動作になっています。


■ 進捗メモと今後の課題

【現在の進捗状況】

  • ブログ整備: プロフィールウィジェット・Xリンク設置 ✅
  • プレイ動画: YouTube限定公開アップ済み ✅
  • 結果シーン: ランク画像・多言語対応・各種修正 ✅
  • 戦闘演算: 地形効果適用順序バグ修正 ✅
  • 戦力計算: GetBattlePower()に一本化 ✅
  • 敵AI: 渋滞時フォールバック追加 ✅
  • 次回予定: 動作確認、地形チップ作成継続、ブリーフィングシーンデザイン着手

執筆後記
今日は「バグ修正の日」でした。
地形効果の適用順序、戦力計算式の混在、SITREP損耗率95%問題と
立て続けに問題が出てきましたが、どれも原因が明確で気持ちよく直せました。
中でも戦力計算式の混入はAIコード生成の落とし穴で、
「動いてるように見えるけど計算が違う」という一番厄介なパターンです。
こういうバグを踏むたびに、ちゃんとコードを読む習慣の大切さを痛感します。
デモ版完成まであともう少し、引き続き頑張ります。

【個人ゲーム開発22日目】戦術SLG制作

スタジオロゴ完成と、評価システムの設計見直し

今回はゲーム開発とは少し毛色の違い作業が多い1日でした。
午前中から夕方まで私用があり作業時間は限られていましたが、
やれることをやれるだけ、という感じで進めています。


■ 本日の開発状況

1. スタジオロゴ完成・起動演出を実装

スタジオのロゴを2種類作成し、ゲーム起動時に表示する演出を実装しました。

AtelierKSG
ヘルメットをかぶった黒猫が「KUSOGE」と書かれた箱から絵筆を振り回しているデザインです。
クソゲーを自称しながら作るスタイル、これが私の流儀です(笑)

スタジオ白猫斎(Studio Hakubyousai)
招き猫×筆×歯車を組み合わせたモノクロのロゴです。
「アート×エンジニアリング」をコンセプトにしています。

ゲーム起動時はこの2つのロゴが黒背景にフェードイン・フェードアウトで
順番に表示されてからタイトル画面へ遷移します。
実装自体はシンプルなコルーチンで、GameInitializer.cs に組み込んでいます。

以下はそのロゴ画像です。
開発元:スタジオ白猫斉、パブリッシャー:くそげ工房という感じで作りましたが、どちらも自分一人なので雰囲気だけ(笑)


2. Web・BOOTH環境の整備

デモ版のリリースに向けて配布環境の整備を進めました。
地味な作業ですが、リリース直前に慌てないよう早めに手を打っています。

atelier-ksg.com のSSL化

開発ブログのSSL証明書を設定し、https化が完了しました。
エックスサーバーの無料独自SSLを使用しています。
途中、WordPressのプラグイン障害で管理画面に入れなくなるトラブルが発生しましたが、
ファイルマネージャーから直接プラグインを無効化して復旧しています。
「保護されていない通信」が消えた時の安堵感はひとしおでした(笑)

BOOTHショップ準備

デモ版の無料配布先となるBOOTHショップを準備中です。
ショップ名は「白猫斎のくそげ工房」、URLは atelier-ksg.booth.pm になります。
商品ページの作成はデモ版完成後に行う予定です。

※尚、ショップURLは現時点でまだ非公開なのでアクセスできません。


3. リザルト評価システムの設計見直し

今日一番の技術的な作業です。

リザルト画面で損耗率が100%になったり0%になったりと
数値が不安定になる問題を抱えていたので、根本から設計を見直しました。

問題の原因

原因は2つありました。

1つ目は開始時データの取得タイミングです。
これまでセッション初期化時(ステージロード時)に開始時戦力を集計していましたが、
この時点ではまだユニットがシーンに配置されていないため、
値が0のまま計算に使われるケースがありました。

2つ目は「ランク」という評価結果をデータとして保持していたことです。
実数値とランクを別々に持つと両者がズレることがあり、
「たまたま正しく出る」「なぜか0%になる」という不安定な挙動の原因になっていました。

改善した設計

変更点内容
開始時データ取得タイミング1ターン目開始直前(TurnManager.Start())に変更
ランクの保持廃止実数値を保持→必要な時に都度計算する方式に変更
ユニット数の追加開始時・終了時の生存ユニット数を新たに記録
セーブデータの変更セーブスロット・システムデータも実数値ベースに統一

「データと表現を分離する」設計原則に立ち返った改修です。
ランクの計算式を後から変えたくなっても、実数値さえ持っていれば再計算できるので拡張性も上がっています。

実装の過程で StageResultstruct から class に変更する作業が発生し、
これに引きずられて複数のスクリプトで連鎖的にエラーが発生しました。
切り貼り修正で対応したところ余計こんがらがるという、よくある展開です(笑)
最終的にはファイルを丸ごと差し替える方針に切り替えて解決しています。


■ 進捗メモと今後の課題

【現在の進捗状況】

  • ロゴ演出: 起動時ロゴフェードイン/アウト実装 ✅
  • Web環境: atelier-ksg.com SSL化完了 ✅
  • リザルト評価: 実数値ベース設計に刷新 ✅
  • 次回予定: リザルトシーンの動作確認、操作性改善の実装

執筆後記
私用の都合で作業時間は実質半日程度でしたが、
懸案だった評価データの設計問題に手をつけられたのは収穫でした。
「たまたま正しく出る」バグが一番厄介で、
原因が静的フィールドの値が前回セッションから残り続けるせいだと
気づいた時は「なるほど」と膝を打ちました。
ゲームロジックの安定性は地味ですが、デモ版の品質を左右する重要な土台です。
引き続き頑張ります。

【個人ゲーム開発21日目】戦術SLG制作

テストプレーしながら一歩進んでは戻る日

概ね基本的な実装が完了し、ユニットや地形チップなど素材もある程度揃ったのでステージ用マップの編集や敵味方ユニットの配置など楽しいフェーズも少しづつしています。

その結果、テストプレー→気になる点やバグ発見→いろいろ対応を検討するという流れで一歩進んでは戻るみたいな感じになってます(笑)

Unityのタイルマップにて作成した地形チップをパレット登録してマップをデザインします。画面は最初のステージなので少し小さいサイズです。地形チップはまだ接続部分とか作ってないので昔のゲームのようにマスとマスがきっちり分かれる感じの見た目(笑)

それから次はステージデータの編集です。
ここではステージ登場するユニットを登録します。種類や座標など。ただ座標は数値で見てもどこらへんかわかりにくいので位置編集用のツールを作りました。なのでここではとにかく登録するだけで座標はあとから触ります。

作ったマップ(プレハブ)とステージデータをエディターツールにセットして以下の画面のようにユニットシンボルを表示させます。ドラッグ&ドロップで動かせるので直感的でいいですね。ついでに拠点座標も触れるようにしてます。

作ったステージはテストプレーして、敵が味方ユニットに到達するタイミングや有利な交戦場所など細かく調整して、評価用のターン数など検討しています。


■ 本日の開発状況

1. 敵AI移動ロジックの改善

テストプレーで一番気になったのがこれでした。

山岳など通行不可マスに隣接した位置に敵ユニットがいると、攻撃範囲に入るまでその場から全く動かなくなる問題が発生していました。

原因:
移動先の選定に直線距離(ヘックスグリッド上の最短距離)を使っていたため、障害物を考慮した経路選択が行われていませんでした。山岳を迂回するには一時的にターゲットから遠ざかる必要がありますが、「直線距離が縮まらない移動はしない」というロジックになっていたので動かなかったわけです。

対応:
経路探索(A*)を使って実際の移動コストベースで最近傍プレイヤーを選定する方式に変更しました。具体的には移動力を999に設定したGetPath()で障害物を迂回した経路を取得し、その経路を実際の移動力の範囲内で辿るという方式です。

あわせて敵AI全体の処理をコルーチン化し、1ユニットごとにフレームを譲ることでプチフリーズも解消しています。

また移動範囲外の接近探索には、ZOC(Zone of Control)判定を省略した軽量版の経路探索メソッドを新規に追加しています。移動範囲外の接近に厳密なZOC計算は不要なので、処理負荷を大幅に削減できました。

AIパターンと拠点移動の仕様も整理しました:

パターン動作
Aggressive常時移動・拠点攻略あり
TimedAggressive設定ターン経過後に行動開始
Defensive攻撃範囲に敵が入ったらトリガー発動
GroupTrigger同グループのトリガー発動後に行動
Stationaryいかなる状況でも移動しない(固定野砲など)

Stationaryは大抵、固定野砲や敵拠点上の重戦車に設定する想定です。


2. カメラズーム機能の実装

広めのマップになるとカメラスクロールだけでは全体把握がしんどいと感じたので、ズーム機能を追加しました。

仕様:

  • 5段階ズーム:50% / 75% / 100% / 150% / 200%
  • 操作:マウスホイール+右パネル下部の+−ボタン
  • ズームアウト制限:マップが画面内に収まった時点でそれ以上のズームアウトを禁止

50%まで引くとマップ全体がある程度把握できるので、戦況確認がかなり楽になりました。

実装で一番ハマったのはCanvasの扱いです。本プロジェクトはCanvasがすべてScreen Space – Cameraモードのため、カメラのOrthographicSizeを変えるとCanvasのPlaneDistanceも連動して調整しないとUIが崩れます。この対応に想定外に時間がかかりました。

また50%ズームアウト時にマップが画面に収まる場合はカメラを固定位置に設定するのですが、右パネル・下パネルを考慮した位置オフセットの計算も必要で、解像度が変わっても正しく動くようにピクセル→ワールド座標変換を使って実装しています。


3. Debugログのビルド無効化

Unity上ではデバッグ用のログを大量に仕込んでいましたが、Debug.Log()はビルドにもそのまま含まれてパフォーマンスに影響します。

ゲーム起動時に必ず実行されるInitializeシーンで、エディタ以外のビルドではDebugログを無効化する処理を追加しました。

#if !UNITY_EDITOR
    Debug.unityLogger.logEnabled = false;
#endif

シンプルですが、リリース前に忘れずやっておきたい対応です。


■ 進捗メモと今後の課題

【現在の進捗状況】

  • 敵AI移動: 経路距離ベースの移動に対応 ✅
  • カメラズーム: 5段階ズーム実装 ✅
  • 地形チップ: 平地・荒野・荒れ地・茂み・山岳(試作)対応済み
  • 次回予定: 地形チップ量産継続、ブリーフィングシーンデザイン着手

執筆後記
テストプレーを始めると「ここが気になる」「あそこも直したい」が次々出てきてキリがないですね(笑)
ただプレイフィールはゲームの第一印象を決める最重要要素だと思っているので、地味でも手触り感の改善は優先度高めで対応しています。
敵AIが障害物を迂回して動くようになっただけで、ゲームとしての説得力がぐっと上がった気がします。

【個人ゲーム開発20日目】戦術SLG制作

バグ修正・機能追加・地形チップ量産、2日間の記録

今回も2日分をまとめてお届けします。

4月2日はひたすらピクセルアート作業、4月3日はバグ修正・機能追加・マップチップの描画問題解決と、かなりボリュームのある内容になりました。


■ 4月2日の開発状況

戦闘用スプライト 全25種完了

本日の作業のほぼ全てをここに費やしました(笑)

AI生成した元ネタ画像を元に、1種あたり1〜2時間かけて手動でピクセル調整を行い、全25種のピクセル単位調整が完了しました。

ユニット構成は歩兵・バイク・装甲車・各種戦車(軽・中・重)・自走砲・野砲など25種。

作業報告書にすると数行で終わりますが、丸1日ぶっ通しの集中作業でした(笑)
ヤクトパンターを模した以下は結構お気に入りです。


■ 4月3日の開発状況

1. ZOCバグ修正(Pathfinding.cs)

移動力3のユニットが、本来敵のZOC(Zone of Control)の都合で到達できないはずの地点まで移動できてしまうバグを修正しました。

原因はZOCマスに到達した後もキューに積んでいたため、探索が続いてしまっていたことです。

ZOCマスへの到達時はキューに積まずそこで探索を止める方式に修正し、あわせてGetPath()でZOCマスが目標地点の場合に経路確定できなかったバグも同時に修正しています。

ついでに重複していたロジックをヘルパーメソッドに統合するリファクタリングも実施しました。

2. 移動キャンセル時のスプライト戻し漏れ修正(UnitController.cs)

ユニットを移動させた後にキャンセルすると、スプライトが移動用の上面図(TopView)のままになるバグを修正しました。

UndoMove()でIdleアニメーションを再開する処理が抜けていたのが原因で、1行追加するだけで解決しています。

3. 戦闘画面の描画順修正(BattleManager.cs)

大型ユニットのスプライトが表示順によって重なって見えてしまう問題を修正しました。

スポーン後に全ユニットをY座標でソートしてSiblingIndexを設定する方式に変更し、手前のユニットが常に上に描画されるようになっています。

4. ランダムタイル機能追加(TileMappingDatabase.cs / MapManager.cs)

マップチップに「ランダム表示」フラグを追加しました。

これまでマップ作成時に手動でチップを選びながら配置してランダム感を出していましたが、フラグをONにしておくだけでステージロード時に登録チップの中からランダムに1枚が選ばれて表示されるようになります。

同じ地形でも毎回微妙に見た目が変わるので、マップに自然なばらつきが出ます。手間がごそっと省けたのでかなり助かっています。

5. スプライトPivot一括変更ツール作成(SpritePivotTool.cs)

縦長チップ(立体オブジェクトが正方形からはみ出すタイプ)に対応するため、スプライトのPivotを自動計算して一括設定するエディタツールを作成しました。

  • 正方形チップ(82×82px)→ Center(0.5, 0.5)を自動設定
  • 縦長チップ(82×Npx)→ Y = 41 ÷ 実際の高さ で正方形基準のCenter相当位置を自動計算

Unityのスプライトエディターでは複数スプライトのPivot一括変更ができないため、エディタスクリプトで解決しています。

6. マップチップの描画順問題を解決

今日一番の大仕事でした。

縦長チップ(山岳など)を縦に並べると、上のマスのチップが下のマスのはみ出し部分を上書きしてしまう問題が発生していました。

調査の結果、同じスプライト同士が隣接するとUnityのバッチ処理で描画順が不定になることが原因と判明。以下の設定変更で解決しています。

  • Tilemap Renderer の Mode を Individual に変更
  • Project Settings → Graphics → Transparency Sort Mode を Custom Axis(X:0, Y:1, Z:0)に変更

この設定でY座標が低いマス(手前)ほど後から描画されるようになり、俯瞰視点として正しい描画順が実現しました。

副作用としてユニットスプライトがTilemapの下に潜り込む問題が発生しましたが、ユニットPrefabのSpriteRendererのOrder in Layerを調整することで解決しています。

この対応によって、山岳など立体感のある縦長チップを縦に並べても奥行き感が正しく表示されるようになりました。マップに厚みと説得力が出てきます。

7. 地形チップ作成・登録

茂みチップを作成しました。平地ベースに低木オブジェクトを合成する方式で、20種前後のバリエーションを用意しています。

また山岳チップの試作と動作確認も実施。上述の描画順問題が解決したことで、縦長の山岳チップも安心して量産できる目処が立ちました。

現在のTileMappingDatabase登録状況はこちらです。

地形ステータス
平地(明緑・暗緑)登録完了
荒野(明茶・暗茶)登録完了
荒れ地(明茶・暗茶)登録完了
茂み登録作業中
林・森林・深森・山岳・山脈ほか作成中

すごーく地道な作業です。
まだ異なる地形同士の接続部分について考えていないのになかなか遅々として作業が進みません。道路とかどうしよう(笑)
ただ工夫として、以下の平野の地形チップですが、濃淡で10種づつ用意しています。で、基本チップを配置したら10種の中からランダムでマップへ配置されるので、手作業によるランダム配置とかやらずに済むよう仕組みを入れました。


■ 進捗メモと今後の課題

【現在の進捗状況】

  • 戦闘用スプライト: 全25種完了 ✅
  • 地形チップ: 平地・荒野・荒れ地・茂み対応済み、残り林系・山岳系など
  • 次回予定: 林・森林・深森・山岳チップ作成、ブリーフィングシーンデザイン着手

執筆後記 マップチップの描画順問題は地味にずっと気になっていたので、解決できたのが今日一番の収穫でした。山岳チップが正しく重なって表示されるだけで、マップの見た目がぐっと本物らしくなります。地形チップの量産がまだまだ続きますが、技術的な問題が片付いたので後は手を動かすだけですね。

【個人ゲーム開発18日目】戦術SLG制作

ピクセルアートと地道な実装改善、2日間の記録

ピクセルアート作業が本格化してきました。
今回は2日分をまとめてお届けします。

コーディング的な意味での開発フェーズは一旦落ち着いて、ひたすら画像の作成と編集作業ばかりとなり、技術的には薄い内容(笑)になってます。


■ 3月31日・4月1日の開発状況

1. 戦闘画面のチームカラー色分け

戦術マップでは敵味方がカラーで区別されていましたが、戦闘演出画面ではこの色分けが反映されていませんでした。

今回、戦闘画面に配置されるユニットスプライトにもチームカラーを適用し、プレイヤー側は青系、エネミー側は赤系で統一。
戦術マップと戦闘画面で見た目に一貫性が出ました。

2. ユニット移動スプライトの不具合修正

移動中のユニット(特に敵ユニット)が、移動専用スプライトではなく待機スプライトのまま回転して表示されるバグを修正しました。

待機アニメーションのコルーチンが移動中も並走してスプライトを上書きしていたことが原因で、移動開始時にコルーチンを停止する処理を追加することで解決しています。

3. 地形情報パネルの表示改善

下部情報パネルの地形アイコン表示を改善しました。

これまでは各地形データに登録した専用アイコン画像を表示していましたが、マップチップの種類が増えるにつれてアイコンとの乖離が気になってきたため、実際にマップに配置されているチップ画像をそのまま表示する方式に変更しています。

カーソルを動かすと、そのマスに置いてある実際のチップが情報パネルにリアルタイムで表示されるようになりました。

4. ピクセルアート:2日間の成果

引き続きピクセルアートの作業を進めています。

項目ステータス
戦闘用スプライト13種 / 25種完了(+5)
マップチップ ベース6種完了
マップチップ 地形別2地形分作成完了
チップ用オブジェクトアート4枚作成

マップチップは各地形の土台となるベースチップから着手しました。
接続パターン(隣接チップとのなじみ)は後回しにして、まずは「1種類あたり最低限の見た目が成立する」状態を優先しています。

ユニットの上面図・側面図は全ユニット分のピクセルアート調整作業まで完了。
仕上がりはこんな感じです。

戦闘用のユニット画像は元ネタ(ピクセル単位の調整前)の画像は全て揃ったのですが、ピクセルアート調整作業がまだ大量に残ってます(笑)
出来た分の一部を公開。

マップチップはまだまだ時間が掛かりそうです。
画像自体は小さいのですが、マップタイルへ配置した時の異なるチップ同士の接続ビジュアル設計がなかなかしんどいです(笑)
とにかくパターンが多くなりそうなので如何に効率的な整理ができるかですね。


■ 進捗メモと今後の課題

機能面の細かい改善とピクセルアートの量産が並行して進んでいます。

【現在の進捗状況】

  • 戦闘用スプライト: 残り12種
  • 次回予定: ピクセルアート継続、ブリーフィングシーンデザイン着手

執筆後記

地形アイコンをマップチップ画像に切り替えてみたら、思いのほかゲームらしさが増した気がします。小さい改善でもプレイ感に効いてくるので、地道な作業も悪くないですね。

【個人ゲーム開発16日目】戦術SLG制作

タイトル画面完成と、ピクセルアート25種との戦い

UI周りのブラッシュアップが一段落し、ついにタイトルシーンが形になりました。

しかし、次に待ち受けていたのは「25種類のユニット素材」という物量の壁です。

■ 本日の開発状況

1. タイトルシーンの完成

セーブデータ選択およびステージ選択のウィンドウデザインを適用しました。これでゲームの「入り口」は完全に整ったことになります。

以下、セーブデータ選択ウィンドウ。

続けてステージ選択ウィンドウです。

2. 徹底的なバグ潰し

地味ながらプレイフィールに直結する修正を行いました。

  • セーブ日時の表示不具合: JsonUtilityDateTime型をシリアライズできない問題に対し、文字列保持用の変数を用意することで解決。
  • 敵AIの行動ルール厳格化: 移動後に攻撃できないはずのユニットが攻撃してくるズル(バグ)を修正。hasMovedフラグによる制御を導入しました。
  • 画面モード切り替え: 設定変更時の挙動を整理し、安定性を向上させています。

3. ユニット量産体制へ(ブリーフィングシーン着手)

本作の要となるユニットのピクセルアート制作を開始。

元ネタとなる上面図・側面図の生成は25種分完了しましたが、ここからの「ドット打ち直し(ピクセル単位の修正)」が本番です。

項目ステータス備考
元ネタ生成25種完了AIベースの素材
ドット修正済3種 / 25種残り22種
1枚あたりの工数1~2時間集中力が必要

これ1枚作るのに1時間くらい掛かる(笑)
ちまちまと作業してます。


■ 進捗メモと今後の課題

現在、ユニット1枚の修正に最大2時間を要しています。単純計算で残り22種 = 22~44時間

4月下旬のデモ版公開という目標に対し、このグラフィック作業のボリュームは無視できない数値です。

ブリーフィングシーンのデザイン実装と並行しつつ、いかに効率よく「鋼鉄の質感」をドットに落とし込めるかが鍵になりそうです。

【現在の進捗状況】

  • 目標: 4月下旬 デモ版リリース
  • 次回予定: ユニットピクセルアート修正継続、ブリーフィングシーンデザイン

執筆後記

1種修正するごとに肩が凝りますが、画面上で自作のユニットが動く姿を想像して乗り切ります。

【個人ゲーム開発15日目】戦術SLG制作

タイトル確定と多言語展開——視覚演出と内部処理の親和性

個人ゲーム開発記、2Dタクティカル『鋼鉄の交響曲 -Symphony of Steel-』の制作報告です。

本日は、本作の「顔」となるタイトルの正式決定と、それに伴う多言語リソースの制作、および演出面の実装を進めました。プライベートで用事があった為、作業量はいつもより少なくなっています。

1. 【重要】正式タイトルの確定とSteam対策

ストア展開を見据え、既存作品との重複を避けるための調査を完了し、正式名称を以下の通りに策定しました。

  • 日本語: 鋼鉄の交響曲 -Symphony of Steel-
  • 英語: Symphony of Steel -Tactical Command-
  • 中国語: 钢铁之交响 -Symphony of Steel-

サブタイトルを含めた固有の名称とすることで、ストア上での識別性を確保しています。

2. 三言語ロゴ制作と「フリンジ」問題の克服

タイトルロゴの制作は、プログラミング以上に根気と時間を要する工程となりました。

特に、透過画像の境界に不自然な白縁が残る「フリンジ」の除去に注力。Clip Studio Paintを使用し、透過境界のピクセルを許容誤差0で選択・除去するという地道な作業を3言語分行い、クリーンなロゴ画像を完成させました。

これらは自作の LocalizeImage スクリプトにより、実行環境の言語設定に合わせて自動で切り替わるよう実装済みです。

以下は3言語対応したタイトルロギイメージです。

3. タイトル画面のUI・演出強化

ユーザーが最初に目にする画面として、細部の調整を行いました。

  • UI配置の最適化: アプリケーション終了ボタンの配置とデザインを再調整。
  • DEMO版の明示: 右下に「DEMO」スタンプを配置し、現在のバージョンを視覚的に定義。
  • SE同期演出: Animation Event を活用。ロゴ出現の特定のタイミングでSEが再生されるよう、Animatorへのブリッジとなる TitleAnimationBridge.cs を新規作成しました。

4. ロード/ステージ選択機能の基礎構築

ゲームの根幹となる進行管理画面の実装の続きに着手しました。

  • 基本機能の開発は開発初期で終えており、現在は仮置きの枠・ボタンのデザインを本番用にブラッシュアップするフェーズに移行しています。

5. 本日の作業履歴まとめ

カテゴリ内容
タイトル確定3言語分の正式名称決定。Steam重複調査完了。
グラフィックタイトルロゴ作成(日・英・中)。フリンジ除去作業完了。
UI・演出終了ボタン調整、DEMOスタンプ追加。Animation EventによるSE再生。
スクリプトLocalizeImage.cs 改修(フォールバック・サイズオーバーライド対応)。
新規実装ロード/ステージ選択ウィンドウの基本ロジック構築。

6. 所感と次への課題

画像リソースの制作は想像以上に工数を消費しますが、多言語対応の基盤を初期段階で固めることは、将来的なグローバル展開において不可欠な投資であると判断しています。

残タスク:

  • ウィンドウ枠・ボタンの本番用画像作成
  • ステージ選択画面のビジュアル強化
  • 敵AI思考アルゴリズムの動作確認・調整

引き続き、理想のタクティカルSLGを目指して構築を継続します。

【個人ゲーム開発14日目】戦術SLG制作

UIデザイン一挙完成!タイトル画面刷新とサウンド演出の強化

個人ゲーム開発記、2Dタクティカル『Steel Symphony』の制作報告です。
早いものでもう新規プロジェクト立ち上げから二週間経過しました。

本日は、これまで「仮」の状態だったUIデザインを一気に本番仕様へブラッシュアップし、ゲームの没入感を高めるサウンド周りの実装を行いました。4月末のデモ版リリースに向け、視覚・聴覚ともに大きな前進です。

1. 【UI刷新】情報設計とデザインのブラッシュアップ

プレイ中の快適さを左右する各種ウィンドウのデザインが、ついに完成しました。

ユニット詳細ウィンドウの表示強化

ユニットリストから呼び出す詳細画面を大幅に強化しました。3言語対応の兵器区分表示(GetWeaponClassName)をメソッド化し、どの言語でもレイアウトが崩れず、かつ必要な情報が瞬時に目に入るデザインを実現しています。


戦術画面:システムウィンドウの強化

設定変更やタイトル戻りなどを行うシステムウィンドウも、ゲームの世界観に合わせたデザインへ。操作ロック(GameFlowController)との連動も完璧で、迷いのない操作が可能です。


戦況ウィンドウ(Sitrep)の実装

現在の勢力図や戦力値を一目で把握できる「戦況ウィンドウ」を実装。右側からのスライドイン演出とともに、戦場の緊張感を伝える重要なインターフェースが加わりました。


2. タイトル画面のブラッシュアップ

ゲームの「顔」であるタイトル画面を作り込みました。フェーズバナーの画像分離やフォントの選定(WDXL Lubrifont等)を経て、デモ版として恥ずかしくないクオリティまで引き上げています。


3. サウンド・演出基盤の構築(AudioMixer導入)

音の演出はゲームの命。今回は AudioMixer を導入し、BGMとSEの独立制御を可能にしました。

  • BGMフェード制御: 無音からSEを経て、BGMが滑らかに立ち上がる演出を実装。
  • 移動SEの実装: 4種類の移動音を使い分け、ユニットが止まれば音も即停止するキレのある制御を実現。
  • 専用マネージャー新設: SceneSoundManagerMainTacticalSEManager により、シーンごとの音響管理を効率化しました。

4. バグ改修:ZOC判定のロジックミス解決

以前から保留していた「ZOC・移動範囲のズレ」を徹底調査。原因は、六角形(ヘックス)グリッド特有のGetNeighbors()における奇偶判定ロジックの誤りでした。ここを修正したことで、タクティカルゲームの肝となる移動ルールが正常化しました。


5. 本日の作業履歴まとめ

カテゴリ内容
デザイン完了ユニットリスト、詳細、システム、サイドパネル、戦況ウィンドウ、タイトル画面。
新規実装AudioMixer導入、3言語対応兵器区分表示、移動SE制御、フェーズ別SE。
不具合修正ZOC・移動範囲・包囲判定の計算ミス解決
環境整備日本語・中国語のフォント選定確定、Noto Sansによる補完設定。

6. 所感と次への課題

UIの作り込みと機能実装を並行して大量に消化した1日でした。特にZOCの計算バグが取れたのは大きな収穫です。

4月末のデモ版リリースは、今のペースなら十分に現実的なスケジュールだと確信しています。

残タスク:

  • ボタンのホバー・クリックSEの実装
  • 敵AIの思考中アニメーション検討
  • 本番用画像への差し替え作業

引き続き、鋼鉄の交響曲(Steel Symphony)を組み上げていきます。