はじめに
本日、花火のアニメーションを実装していた際、粒子のパーティクルを作成し上空で拡散して広がった後、メッシュを削除してもて消えないバグに直面。
AIに聞いても解決できなかったので、丸ごとTHREE.Group()を削除する強硬手段に出ようとしましたが、コードを読んるとバグに気づき修正できたので、再発防止も兼ねて備忘録メモとして残しておきます。
AIにコードは何度か見せてるのですが、それでも気づかないようで、AIも万能ではないし、相変わらず、人間側にコードを読むスキルが求められるようです。
1. 症状
- 粒子が消えない
- 不透明な状態では気づかないが、透明にすると上空に残る
- update や dispose を呼んでも表示が消えない
scene.remove(mesh)やgroup.remove(mesh)を試しても改善しない- 粒子数を減らしても症状は変わらない
2. 最初に疑ったこと(全部ハズレ)
最初は Three.js の描画や親子関係の問題を疑った。
scene.remove(mesh)しても消えないgroup.remove(mesh)しても消えないmaterial.dispose()/geometry.dispose()も効かないstate = 'dead'で update を止めても残るrenderer.renderLists.dispose()まで試した
それでも、透明にした粒子が 上空に居座り続ける。
ここまで来ると 「Three.js のバグでは?」 「粒子数が多すぎる?」 と考え始める。
3. 問題のコード(見た目は正しい)
問題の核心は、この一行だった。
this.particles = this.particles.filter((p) => p.life > 0);
JavaScript 的には自然で、ロジックも綺麗に見える。
- 寿命が残っている粒子だけ残す
- 寿命が尽きた粒子は配列から消す
一見、何も問題が無さそうに見える。
4. なぜこれがバグになるのか
filter がやっているのは 配列の更新だけ。
Three.js 的に見ると、こうなる。
- 粒子の
meshはscene / groupに add 済み filterによって 配列から参照だけが消えるmesh自体は scene に残り続ける- update も dispose も二度と呼ばれない
- 結果、上空に「ゴースト粒子」が残る
つまり、
配列から消えた = Scene から消えた ではない
5. C言語っぽい挙動になる理由
感覚的には、C言語でこう書いたのと同じ。
Particle* p = malloc(sizeof(Particle));
array[i] = NULL; // ポインタだけ消した
// free(p) をしていない
- 参照は消えた
- 実体は残った
- もう触れない
- 片付け不能
Three.js は GC と手動解放が混ざった世界なので、 この罠にハマりやすい。
6. 正しい順序(重要)
Three.js で安全なのは、必ずこの順序。
Scene から remove
↓
material / geometry を dispose
↓
最後に配列から削除
7. 正しいコード例
this.particles = this.particles.filter((p) => {
p.mesh.visible = true;
p.velocity.y -= gravity * delta;
p.mesh.position.addScaledVector(p.velocity, delta);
p.life -= delta;
if (p.life <= 0) {
if (p.mesh.parent) p.mesh.parent.remove(p.mesh);
p.mesh.material.dispose();
p.mesh.geometry.dispose();
return false;
}
return true;
});
これで、
- 見た目も消える
- Scene にも残らない
- GPU リソースも解放される
8. 花火エフェクトなら別解もある
粒子を個別に消さず、
- 全粒子の寿命が尽きたら
Fireworkごと dispose
という設計でも成立する。
if (this.particles.every(p => p.life <= 0)) {
this.dispose(); // group を丸ごと remove
}
短命エフェクトでは、この方が安全な場合も多い。
9. まとめとして残したい一文
Three.js では
配列管理と Scene 管理は別物。
Scene に add した Mesh は、
remove してから参照を切れ。
このまま出しても十分記事になる内容。
忘れた頃に読んだ自分も、たぶん助かる。
💬 コメント