01高fpsでキャラが横歩き(ムーンウォーク)する

「移動量が閾値を超えたら向きを変える」処理で、比較基準の lastPosition を毎フレーム上書きすると高fpsで壊れます。1フレームの移動が閾値に届かず、差分が永遠に溜まらないため。vsyncを切った環境(200fps超)だけキャラが横歩きしました。
直し方: lastPosition は閾値を超えたときだけ更新する=閾値未満の微小移動を蓄積する。
各作品の開発で実際に踏んだバグの記録と、作品を問わないUnityの落とし穴。出典とタグで絞れます。

「移動量が閾値を超えたら向きを変える」処理で、比較基準の lastPosition を毎フレーム上書きすると高fpsで壊れます。1フレームの移動が閾値に届かず、差分が永遠に溜まらないため。vsyncを切った環境(200fps超)だけキャラが横歩きしました。
直し方: lastPosition は閾値を超えたときだけ更新する=閾値未満の微小移動を蓄積する。

Time.timeScale = 0 でポーズすると Time.deltaTime が 0 になり、deltaTime で動かしている演出は全部止まります。ポーズメニューのアニメーションまで固まって気づきました。
直し方: ポーズ中も動かしたいUI・演出は Time.unscaledDeltaTime で駆動する。

被弾で白フラッシュ→元の色に戻す処理は、連続ヒットで色が戻らなくなることがあります。原因は再入。2発目のコルーチンが「元の色」として白を掴み、最後にそれを復元してしまうため。敵が白いまま固まって気づきました。
直し方: フラグ1枚で二重起動を止める(再入ガード)。フラッシュ中の外部からの色変更にも世代カウンタで備える。

攻撃した本人が死ぬと、飛んでいる弾や時限爆弾の演出が一緒に消える。演出コルーチンを術者のMonoBehaviour上で回していると、Destroyと同時にコルーチンも止まるためです。
直し方: 常駐の空オブジェクト(ランナー)に演出コルーチンだけ回させる。本人が倒れても爆弾はちゃんと爆発する。

「次に進むセル」を取る関数が、取得と同時にスタックを更新していると壊れます。移動がキャンセルされた時(先客がいた等)に内部状態だけ進んでしまい、次の取得が2マス先や斜めのセルを返す。キャラが壁を斜めにすり抜けて歩いていました。
直し方: 取得は参照(Peek)だけにして、実際に動いた瞬間に更新(Push/Pop)を確定する。

テクスチャのFilter Modeは既定がBilinear(バイリニア補間)で、拡大時にピクセル同士がにじみます。16x16のドット絵を拡大表示すると輪郭が溶けたようにぼやけて、ドット絵の「カクッとした」味が消えます。
直し方: ピクセルアートは Filter Mode: Point (no filter) に。Compression も None 推奨。

[SerializeField] のフィールドは、最初にシリアライズされた時の値がシーンやプレハブに保存され、以後はそちらが使われます。コードの初期値を後から直しても実効値は古いまま。連射間隔を直したつもりが、シーンに焼き付いた0.01秒のままで長押し掘削が毎フレーム連射になっていました。
直し方: 値を変えたらコードと Inspector(シーン/プレハブの保存値)の両方を更新する。

波ダッシュ(U+301C)と全角チルダ(U+FF5E)は、見た目がほぼ同じ別の文字です。ビットマップフォントは片方しか収録していないことがあり、IMEやOSによってどちらが入力されるかも変わります。乙姫のセリフに□が出て気づきました。
直し方: 日本語セリフの波線は U+FF5E(全角チルダ)に統一する。

エディタは AssetDatabase、ビルドは Resources と読む経路が違うため、Art側とResources側で PPU(Pixels Per Unit)がズレていてもエディタでは気づけません。瑠璃のドット絵が Art=550 / Resources=16 のままビルドされ、配置した瞬間に34倍で表示されました。
直し方: 同じ画像を2箇所に置くならインポート設定(PPU・Filter Mode)を完全一致させる。ビルド前チェッカーで全数検査すると確実。

シーンや ScriptableObject は enum を番号(int)で保存します。途中に値を挿入すると以降が1つズレて、保存済みの「3」が別の名前を指すようになり、回復スキルが範囲攻撃に化けます。既存の値の番号は動かさず、新しい値は末尾に足します。
直し方: 新しい値は必ず末尾に追加する。途中に入れたくなったら明示的な数値割り当てを使う。

寿命を lifetime = 999f にして「実質無限」のつもりでいると、判定を通る以上いつか発症します。タイトル画面を16分39秒放置すると、背景の光線がキラキラ粒子として再生成されて高速回転し始めました。
直し方: 無限にしたいものは寿命判定・リスポーン対象から明示的に除外する。

Time.deltaTime 駆動のシェイクを timeScale=0 の凍結に持ち越すと、経過時間が進まないのにランダムオフセットだけ毎フレーム適用され続け、永久に揺れます。クエイク直後に敗北すると、敗北会話の間ずっと画面が揺れていました。
直し方: dt=0 の間はオフセットを戻して静止し、解凍後に残り時間だけ揺らす。

「攻撃範囲に敵がいたら移動せず待つ」AIは、待機判定の範囲が攻撃範囲より少しでも広いと詰みます。移動補間中はキャラ同士が同じセルに重なれるのに、待機は距離0を含み、攻撃は隣接4方向のみ。殴れない相手をお互いに待ち続ける永久フリーズになりました。
直し方: 待機範囲は攻撃範囲と完全一致させる。範囲を広げるときは必ず両方セットで。

uGUIはクリックしたボタンがEventSystemの「選択」に残り続けます。あとで押したEnterやパッド決定ボタンのUnity標準Submitが、そのボタンを裏で再発火。閉じたオーバーレイの下の見えないボタンでも起きます。強化画面で進化が1段余計に購入されました。
直し方: 独自のキー/パッドナビを組むなら Navigation を無効化し、毎フレーム SetSelectedGameObject(null) で標準ナビを遮断する。

設定値が未配線で実は無視されている状態のまま数値調整を重ねると、その値は動作実績ゼロの地雷になります。未配線のまま「決めた」ボスの移動間隔0.08秒/マスが、配線バグを修復した瞬間に初めて有効化され、毎秒15マスで走るテレポートボスが誕生しました。
直し方: 配線バグを直したら、その経路に乗る全パラメータを「初めて有効化される値」として再検証する。

範囲スキルの「撃つか判定」を距離で書き、実効果を扇形で当てると、扇の外の敵に反応して発動し、正面の壁へ撃つ「無言の空撃ち」(MPとクールダウンだけ消費)が生まれます。扇形波スキルの初版は「何も起きない」と報告されました。
直し方: 照準・発動判定・効果適用は同じジオメトリ関数を共有させる。以後追加した砲撃スキルは同じ流儀で空撃ちゼロ。

同じ挙動が専用系と汎用系の2系統で実装されていると、専用系だけ止めても汎用系が生き残ります。繁殖しないはずの特殊種が、巣システムだけ止めて汎用の確率繁殖フラグを立て忘れたせいで、15秒ごとに自己クローンを産んで指数的に増殖。同じ構造の穴を別種族で3回踏みました。
直し方: 機能を無効化するときは、その機能に至る全経路を列挙して全部塞ぐ。

グリッド移動+補間描画のゲームでは「キャラのいるセル」が2つあります(スプライトの描画位置と、移動完了時に更新される論理セル)。クリック判定を論理セルにしたら移動中の海獣が選べなくなり、AIの進入判定が描画座標だったら「進入はブロックするのに攻撃が届かない」空転が起きました。両方向で踏んでいます。
直し方: 入力(クリック選択)は描画位置=見えているもの、AI(攻撃・進入・待機)は論理セル、と規約で使い分ける。

画面全体に泡やキラキラを撒くとき、純ランダム配置は必ず塊と空白ができます。画面をパーティクル数ぶんのセルに分割し、各セル内でランダムにオフセットする「グリッドジッター法」なら、1パスで均一密度と自然なばらつきが両立します。
直し方: cols=√(個数×幅/高さ)でセルを割り、i番目の粒をセル内のランダム位置へ。タイトル画面と勝利演出は全部この方式です。

攻撃の踏み込みやシェイクなどの位置演出で絶対座標を書くと、移動の補間処理と毎フレーム座標を取り合ってテレポートや位置ズレが起きます。「今回のオフセット−前回足した分」だけをtransformに加算する差分方式なら、移動と複数の演出が自然に合成されます。
直し方: 毎フレーム position += offset - applied、終了時に -= applied。歩きながら揺れても跳ばず、必ず元の位置に戻る。

staticフィールドはシーン再読込を生き残りますが、Time.timeは0に戻ります。「Time.time < 保存時刻+間隔」型のキャッシュ判定は、再読込直後に未来の時刻と比較することになり、古い値を返し続けます。リトライ後、海獣が最大5分間寝返ったままになりました。
直し方: Time.time >= 保存時刻 の巻き戻り検出を必ず併記する(またはシーン再読込でキャッシュをリセットする)。

TilemapはTileアセットのspriteを差し替えても、tilemap.RefreshTile(cell)を呼ぶまで再描画されません。内部数値は正しいのに見た目だけ古い、という気づきにくいバグになります。土に撒いた魔分の色が、散布3経路まるごと画面に出ていませんでした。
直し方: タイルのデータを変えたら、変更したセルを RefreshTile で再描画する。

キーボードとパッドのカーソル移動は長押しリピート(1回1マス、間隔0.08秒)です。カメラを引いた全体表示ではマスが小さくなるぶん、同じ間隔でも画面上の移動が遅く感じます。画面上の速度が一定になるよう引き具合に反比例で速くしたところ(最大拡大 orthographicSize 8 → 全体表示 約18 で2.25倍速)、今度は速すぎて狙ったマスで止まれませんでした。GIFの左が反比例の初版、右が平方根スケール+上限1.25倍(全体表示で0.064秒間隔)です。
直し方: 体感の補正は平方根スケール+上限で控えめに。速くするのはリピート間隔だけで、1回の移動は1マスのままにする(複数マス飛ばすと通り道が4連結でなくなり、長押し掘削が途切れます)。

掘削の条件は「隣の4マスに掘削済みのマスがあること」です。斜め入力(横と縦を同時に倒す)でカーソルを斜めに1マス飛ばすと、通り道が4連結にならないため角のマスが条件を満たせず、斜めに掘り進めませんでした。横→縦→横…と1軸ずつ交互に分解すれば、斜めに倒しっぱなし+長押しで階段状に掘れます。GIFの左が斜めに飛ぶ版、右が交互に1歩ずつ動く版です。カーソルが指していない隣のマスも掘って橋渡しする案もありましたが、指していないマスが勝手に掘れてしまうので不採用にし、動き方の側で解決しました。
直し方: 斜め入力は1軸ずつ交互に分解して通り道の4連結を保つ。端で選んだ軸が動けないときはもう片方の軸へ切り替える(端沿いで半速にならない)。

設定画面の「データ削除」は、取り返しのつかない操作なので2段階の確認にしています。1段目の瑠璃の確認では「はい」が2秒、2段目の乙姫の最終確認では「削除する」が3秒、押せない状態で表示されます。押せない間はボタンを暗くし、ラベルに「削除する (3)」のように残り秒数を出して、時間が来ると通常の色に戻ります。設定画面はゲームの時間が止まったタイトル画面で開くため、秒数は時間停止の影響を受けないカウントで数えています。GIFの左は、もし確認がすぐ押せたら(連打でそのまま2段とも通ってしまう)という例、右が本作の動きです。
ポイント: キーボードやパッドの初期カーソルも、カウントダウン中は「いいえ/やめる」側に置くようにしました。決定キーを連打しても削除には進まず、勢いのままでは消せないようにしています。

日本語と英語に対応したとき、初めて起動した人には何も聞かずにOSの言語で始まるようにしました。OSの言語は Unity の Application.systemLanguage で読めます。OSに問い合わせる処理を自分で書く必要はなく、SystemLanguage という列挙型(Japanese / English / ChineseSimplified など、判別できなければ Unknown)で返ってきます。本作では Japanese かどうかだけを見て、それ以外は Unknown も含めてすべて英語にしています。判定するのは PlayerPrefs に言語の保存値が無いときだけで、決めた結果はすぐ保存します。2回目以降は保存値を読むだけなので、設定画面で選び直した言語が常に優先されます。ただし設定画面の「データ削除」は PlayerPrefs.DeleteAll() で全部消すため、言語の保存値も一緒に消えて、次の起動でまたOSの言語に戻ってしまう不具合がありました。図の左がその流れ、右が直したあとの動きです。
直し方: DeleteAll() の直後に言語だけを保存し直す。言語はプレイの記録ではなく遊ぶ人の環境設定なので、データ削除の対象から外しました。言語の切り替えは設定を閉じたときにシーンを読み直して反映します。