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

今日は「見やすさ・わかりやすさ」を追求する日

今日は実装面とグラフィック面、両方を進めた日になりました。

実装は主にUI操作性の向上です。
「今何が起きているのか」がプレイヤーに伝わるかどうか。
それを意識しながら、下部パネルの動作・カーソル追従・ユニットの視認性を整えていきました。

後半は素材作業に移り、ユニットのマップ画像を本格的にドット絵化する作業を進めました。
気づいたら25種中14種まで完了していました。


■ 下部パネル・グリッドカーソルの動作仕様を確定

「最後に表示した情報が張り付く」問題を解消した

これまで下部の情報パネルは、状況が変わっても最後に表示した情報がそのまま残る仕様でした。
戦闘中に敵ユニットの情報が表示されたままになったり、
敵ターンが終わっても敵の情報が残り続けたりと、情報の鮮度が保てていませんでした。

今回、以下の仕様として確定・実装しました。

状態下部パネルグリッドカーソル
カーソル移動中地形・ユニット情報を表示プレイヤー操作
戦闘中(プレイヤー攻撃)情報をクリア現位置固定
敵ターン・移動中行動中の敵ユニット情報を表示敵ユニットに追従
敵ターン・攻撃中情報をクリア敵ユニット位置固定
戦闘終了後カーソル位置の情報に戻す元の挙動に戻す

実装上はシンプルで、戦闘開始時にClearInfoAll()を呼び、
終了後にOnCursorMove()でカーソル位置の情報を再取得するだけです。
プレイヤーターン側はCursorManager.cs、敵ターン側はTurnManager.csEnemyRoutine()内でそれぞれ対応しています。


■ 敵ターン中のカーソル・カメラ追従

「敵がどこに移動したか見えない」問題を解消した

これまで敵ターン中、グリッドカーソルはプレイヤーが最後に置いた位置に固定されたままでした。
敵ユニットにカメラが追従しても、カーソルだけが別の場所に残るため、
ズームインしているときに「敵がどこに消えたのか」わからなくなることがありました。

今回の対応は2点です。

① 行動開始時にカーソルを敵ユニット位置へ移動

FocusCameraOn()でカメラを追従させた直後に、
CursorManager.ForceMoveCursorTo()でカーソルも同じ座標へ飛ばします。
これでカーソルと情報パネルが常に行動中の敵ユニットを指すようになりました。

② 移動完了時にカメラを移動先へ再フォーカス

移動アニメーション中のリアルタイム追従は対応コストが高いため、
移動完了(while (e.State == UnitState.Moving) yield return null;の直後)に
もう一度FocusCameraOn()を呼ぶ方式にしました。

移動アニメーション中は見えなくても、完了時点で正しい場所にカメラが飛ぶことで
プレイヤーの混乱を防ぐことができます。ズームインしているときの体験が大幅に改善しました。


■ ユニット選択時の視認性向上

「自分が何を選んでいるか」が一目でわかるようになった

移動範囲ハイライトが選択ユニット自身のマスに重なるため、
「どのユニットを選んでいるか」が視覚的にわかりにくい問題がありました。

2点の改善を行いました。

① 選択ユニット自身のマスをハイライト除外

reachableからShowMoveRange()に渡す際、
moveHighlightというコピーを作成し、そこからユニット自身の座標をRemove()して渡します。
reachable自体は経路探索・攻撃範囲計算に使うため、元のSetは変更しません。
味方選択時・敵ホバー時の両方に適用しています。

② 点滅フラッシュ演出

選択中・ホバー中のユニットが元の色と指定色の間でグラデーション点滅するようになりました。

UnitControllerStartFlash()StopFlash()を追加し、
コルーチンでMathf.Sin()を使ったなめらかな色補間を実装しています。

private IEnumerator FlashRoutine()
{
    Color origin = (State == UnitState.ActionWait) ? actionWaitColor :
                   (State == UnitState.Done) ? doneColorMultiplier : baseColor;
    while (true)
    {
        float t = (Mathf.Sin(Time.time * flashSpeed * Mathf.PI) + 1f) * 0.5f;
        if (spriteRenderer != null)
            spriteRenderer.color = Color.Lerp(origin, flashColor, t);
        yield return null;
    }
}

flashColorflashSpeedはInspectorで調整可能です。
flashSpeedの数値が小さいとゆっくり、大きいとチカチカした点滅になります。

既存のUpdateUnitAppearance()spriteRenderer.colorを上書きする設計だったため、
_isFlashingフラグを追加してフラッシュ中は色の上書きをスキップする処理も加えています。

実装後の画面です。選択・ホバー対象のユニットが光っているので一目でわかります。


■ ユニットマップ画像の本素材化(14/25種完了)

AIで用意した仮素材をドット絵風に全面改修

これまでのユニットマップ画像はAI画像生成で用意した仮素材でした。
描き込みの細かさや粒度にばらつきがあり、ゲーム画面上での統一感に欠けていたので、
全25種を本格的にドット絵スタイルへ改修する作業を開始しました。

加工フロー

  1. AI生成元絵を2×2に拡大
  2. 濃い色バイアスを強めにして4ドット中の最多色を採用(ドット感を強調)
  3. Clip Studio Paintで手修正(削れすぎたドットの復帰・はみ出し削除・白飛び修正)
  4. 縦方向に引き延ばしてデフォルメ(マップ上の視認性向上)

戦車・砲身系は直線部分がピクセル変換で白飛びしやすく、手修正が多めになりましたが、
ティガーⅠとティガーⅡはフォルムの差をしっかりつけることができました。
「並べた時に一目で区別できる」ことを優先したデフォルメを意識しています。

本日は25種中14種が完了。残り11種は駆逐戦車・自走砲・野砲系です。
少しズングリしたフォルムになってドット感強めに調整しました。


■ シーン完成状況

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

執筆後記
今日は「実装のための実装」ではなく、「プレイヤーが感じる体験」を起点にした改善が多かったです。
下部パネルの情報張り付き、カーソルが迷子になる問題、選択ユニットの見づらさ。
どれも「動いてはいるけど、なんかおかしい」という感覚から始まった改善です。

特にズームイン時に敵の移動先が見えなくなる問題は、自分がテストプレイして地味にストレスを感じていた箇所でした。移動完了時にカメラを飛ばすだけで体験がここまで変わるというのは、改めて「カメラ演出の重要性」を実感しました。

素材作業はひたすら地道です。戦車の砲身が白飛びするたびに手で引き直す作業を繰り返しています。
でもティガーⅠとⅡが並んだ時に「あ、ちゃんと違う車両に見える」となった瞬間は素直に嬉しかったです。
残り11種、明日には完走したいところです。