Skip to content

feat: merge and explosion#20

Open
faithia-anastasia wants to merge 2 commits intomainfrom
feature/merge_and_explosion
Open

feat: merge and explosion#20
faithia-anastasia wants to merge 2 commits intomainfrom
feature/merge_and_explosion

Conversation

@faithia-anastasia
Copy link
Contributor

・合体か爆発かを判定する関数を追加
・月を追加

問題

現在のコードで惑星の合体が発生すると

Uncaught TypeError: Cannot read properties of undefined (reading 'velocity')
    at step (bca76753-1553-4bea-b7f8-12ac13f0a41f:12814:19)
    at self.onmessage (bca76753-1553-4bea-b7f8-12ac13f0a41f:12876:11)

という風にエラーが発生する

Copy link
Contributor

@tknkaa tknkaa left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

変更内容の確認

追加された機能の設計自体はよくできています。decideCollisionOutcome.ts の脱出速度・衝突角・質量比を組み合わせた判定ロジック、mergePlanets.ts の運動量保存に基づく合体計算、そして planetRegistryvelocity を追加した拡張はいずれも適切です。


バグの原因

TypeError: Cannot read properties of undefined (reading 'velocity') は、以下の競合状態(race condition)によって発生しています。

  1. 惑星AとBが衝突すると、物理エンジンが両方onCollide コールバックをほぼ同時に発火します
  2. 先に処理された側(例:惑星A)の onCollideonMerge を呼び出し、handleMerge 内で planetRegistry.current.delete(idA)planetRegistry.current.delete(idB)同期的に実行します(index.tsx 175〜176行目)
  3. しかし setPlanets() は非同期のため、両惑星の PlanetMesh コンポーネントはまだマウントされたままです
  4. 直後に惑星B側の onCollide が発火しますが、このとき planetRegistry.current.get(otherId) はすでに削除済みのため undefined を返します
  5. その undefined に対して .velocity を読もうとしてクラッシュします

PlanetMesh.tsx の72〜73行目にある if (!myPlanet || !otherPlanet) return のガードは、planetRegistry.current.has() が通過した直後に削除が起きるケースを防げないため、このチェックをすり抜けてしまいます。


修正案

index.tsxPage コンポーネント内に "合体処理中" のIDを管理する useRef<Set<string>> を1つ追加し、onCollide の先頭でそのSetを確認するのが最もシンプルで確実な修正です。

index.tsx

const mergingPlanets = useRef<Set<string>>(new Set());

handleMerge の先頭で両IDをSetに登録します。

const handleMerge = (obsoletePlanetIdA: string, obsoletePlanetIdB: string, newPlanetData: Planet) => {
  mergingPlanets.current.add(obsoletePlanetIdA);
  mergingPlanets.current.add(obsoletePlanetIdB);
  // 以降は既存の処理のまま
  planetRegistry.current.delete(obsoletePlanetIdA);
  ...
};

PlanetMesh.tsxonCollide の先頭)

onCollide: (e) => {
  const myId = e.target.userData.id;
  const otherId = e.body.userData.id;

  // どちらかが合体処理中なら即リターン
  if (mergingPlanets.current.has(myId) || mergingPlanets.current.has(otherId)) {
    return;
  }
  // 既存の処理...
}

このアプローチが最適な理由:

  • 最小限の変更で根本原因を解消できる
  • 処理が同期的・決定的であり、タイマーや遅延に依存しない
  • 合体中の惑星のIDは UUID であるため再利用されず、Setのクリーンアップも不要
  • 既存のレジストリ削除タイミングやReactのレンダリングサイクルを変更する必要がない

@tknkaa tknkaa changed the title Feature/merge and explosion feat: merge and explosion Mar 15, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants