Linear ArchiveArchived issues viewer
← Back to list
INS-816

Enemy.tsx: useScaledTick の props クロージャ依存を worldRef 直参照に直し、target-lock の workaround を撤去する

StatusDone
TeamInstansys
Assigneeyasuhisa.hiraga@instansys.co.jp
PriorityNo priority
Created2026/06/01 03:41
Completed2026/06/01 06:16
Archived2026/06/09 04:00

Description

背景

apps/client/src/scenes/Battle/Enemy.tsxuseScaledTick (~line 192 付近) は

enemy props のクロージャに依存している。EnemyLayer は memo されており、Enemy

自身も memo + forceRender で「離散変更時のみ再描画」設計のため、本来は毎フレーム

再描画されない。

しかし worldRef.current.enemies 側で enemy オブジェクトが毎フレーム置換される

構造のため、再描画されない限り Enemy.tsx の useScaledTick は古い enemy 参照を

握ったままになり、position 等が更新されず敵が**「足踏み + 0.5秒ごとにワープ」**

する。

偶然成立していたカラクリ

battle-logic.tshandleEnemyClick ラッパが useTargetLock 由来の

rawHandleEnemyClick を deps に取り、その rawHandleEnemyClick が isEnemyInRange

(deps: [characters, alta] → 毎フレーム不安定) を deps にとっていたため、

handleEnemyClick → wrapper → EnemyLayer の props onEnemyClick が毎フレーム新

identity になり、EnemyLayer の memo が毎フレーム破れて再描画 → Enemy も再描画

→ クロージャ更新、という偶然のチェーンで正常動作していた。

現状の workaround (15e45752)

apps/client/src/hooks/battle-logic/target-lock.tshandleEnemyClick deps に

未使用_isEnemyInRange を残し、identity を毎フレーム不安定にしている。

ファイル先頭・宣言直上にコメントで意図を強調 (88e7547b)。

// useTargetLock 内
const _isEnemyInRange = useCallback(/* ... */, [characters, alta]);

const handleEnemyClick = useCallback(
  /* 本体では _isEnemyInRange を使わない */,
  [_isEnemyInRange, setTargetLockEnemyId]  // ← workaround
);

本来の修正方針
Enemy.tsx の useScaledTick 内で enemy を props クロージャでなく、
worldRef.current.enemies から instanceId 引きで毎フレーム取り直す形に
直す。

// 概念コード
useScaledTick(() => {
  const current = worldRef.current.enemies.find(e => e.instanceId === enemy.instanceId);
  if (!current) return;
  // current.position 等を読む
});

そうすれば EnemyLayer の memo が本来の最適化として機能し、

  • target-lock.ts の _isEnemyInRange workaround を撤去できる
  • 毎フレーム VDOM diff を回避できて性能改善も見込める

影響範囲・優先度

  • 優先度: 中。現状 workaround で機能はしているが、

    • lint/自動修正で _isEnemyInRange が消えると即回帰

    • characters / alta が参照変化しないフレームで identity が安定する条件が

成立すると、workaround の有効性自体が崩れる可能性

  • Character / Projectile / Alta など他の per-frame コンポーネントにも同種の

クロージャ問題が潜在している可能性 (要監査)

関連

- workaround 適用コミット: 15e45752

- workaround 強化コミット: 88e7547b (PR レビュー指摘対応)

- 関連ファイル:

  • apps/client/src/scenes/Battle/Enemy.tsx (本修正対象)

  • apps/client/src/hooks/battle-logic/target-lock.ts (workaround 撤去対象)

  • apps/client/src/scenes/Battle/EnemyLayer.tsx (memo の挙動確認用)

完了条件

  • [ ] Enemy.tsx の useScaledTick が worldRef 直参照に変更されている
  • [ ] target-lock.ts の _isEnemyInRange が削除されている (or 純粋に使う方向で書き直されている)
  • [ ] 「足踏み + ワープ」回帰がないことを実機確認
  • [ ] Character / Projectile / Alta など他の per-frame コンポーネントも同種問題がないか監査