メインコンテンツまでスキップ

「Best Practices」タグの記事が4件件あります

Development best practices and tips

全てのタグを見る

クロスシーンイベント:誰も語らないが誰もがハマる永続化の問題

TinyGiants
GES Creator & Unity Games & Tools Developer

AudioManagerがBGMを再生している。プレイヤーが新しいエリアに入った時にトラックを切り替えるため、OnLevelStartにサブスクライブしている。AudioManagerDontDestroyOnLoadオブジェクトに配置して、シーンロードをまたいで永続化させた。開発中は常に同じシーンでテストしているのですべて正常に動作する。

ある日、誰かが初めてレベル1からレベル2をロードする。BGMが切り替わらなくなる。AudioManagerはまだ生きている——DontDestroyOnLoadがその仕事を果たした——しかしイベントサブスクリプションはシーン遷移を生き延びなかった。あるいはもっと悪い状況:古いサブスクリプションがまだ残っていて、破棄されたレベル1のイベント発火元を指しており、次に何かがそれを呼び出そうとすると、ゲームプレイの最中にMissingReferenceExceptionが発生する。

これが永続化問題であり、複数のシーンを持つすべてのUnityプロジェクトがいずれぶつかるものだ。

リリース後に発覚するイベントシステムの罠:メモリリーク、データ汚染、再帰トラップ

TinyGiants
GES Creator & Unity Games & Tools Developer

ゲームを5分間テストしてきた。快適に動作する。するとQAがレポートを上げてくる:「30分のプレイセッションでメモリ使用量が着実に増加。6シーンをロードした後、フレームレートが60から40に低下。」プロファイリングすると、本来12であるべきイベントに847のリスナーが登録されている。各シーンロードが新しいサブスクリプションを追加しつつ、古いものを削除していなかった。オブジェクトは破棄されていたが、デリゲート参照が残り続け、ガベージコレクタが手を出せない場所に死んだMonoBehaviourをピン留めしていた。

あるいはこっち:「2回目のPlay Modeセッションでヘルス値がおかしい。1回目は問題なし。」Playを押す。戦闘をテスト。停止。もう一度Play。プレイヤーのHPが100ではなく73でスタートする。前回のセッションのScriptableObjectの状態が持ち越されていた。誰もリセットしなかったから。

あるいは定番:3秒間ゲームがハングし、Unityがクラッシュする。イベントAのリスナーがイベントBを発火。イベントBのリスナーがイベントAを発火。スタックオーバーフロー。ただし、クラッシュしないこともある——目に見えるエラーを出さずにCPUを食い潰す無限ループでハングするだけ。

これらは仮定の話ではない。実際に本番ゲームで出荷されたのを見たバグだ。そしてすべて同じ根本原因を持つ:単独では正しく見えるが、スケールすると壊れるイベントシステムパターン。

実行順序のバグ:「誰が先に反応するか」に潜む危険

TinyGiants
GES Creator & Unity Games & Tools Developer

プレイヤーが25ダメージを受ける。ヘルスシステムが現在のHPからダメージを差し引く。UIがヘルスバーを更新する。...はずが、ヘルスバーに表示されるのは75ではなく100。20分間コードを見つめた末に気づく。UIのリスナーがヘルスシステムのリスナーより先に実行されていた。UIは古いHP値を読み取り、それを描画し、その後にヘルスシステムがデータを更新した。データが正しくなった頃には、フレームはすでに描画済みだった。

あなたが発見したのは実行順序バグだ。イベント駆動アーキテクチャで何かをリリースした経験があるなら、気づかないうちにこのバグをいくつも出荷している可能性が高い。テスト中はスクリプトがたまたま正しい順序で初期化されたから動いていただけで、本番環境ではUnityのロード順が変わって壊れる——そういう類のバグだ。

これはレアなエッジケースではない。ほとんどのイベントシステム——UnityのUnityEventや標準のC# eventデリゲートを含む——が持つ構造的な欠陥だ。そして一度理由を理解してしまうと、もう元には戻れない。

イベント200個超え:なぜイベント管理は破綻するのか

TinyGiants
GES Creator & Unity Games & Tools Developer

新しいUnityプロジェクトを始める。イベントを10個作る。OnPlayerDeathOnScoreChangedOnLevelComplete。分かりやすい名前を付けて、フォルダに入れて、次に進む。快適。イベント構造全体が頭に入る。

半年後。イベントが200個ある。Projectウィンドウはもう、ScriptableObjectファイルの壁だ。OnPlayerHealthDepletedが必要。いやOnPlayerHPLowだったっけ?それともOnPlayerHealthZero?全部OnPlayerで始まる名前のリストをスクロールしながら目を凝らす。3分後、欲しいイベントが既にあるかすら分からないので諦めて新しいのを作る。

これがイベント駆動のUnityプロジェクトが最終的に行き着く場所だ。イベントパターンが間違っているからじゃない。スケールでのイベント管理ツーリングを誰も作っていないからだ。Unityにはアニメーションウィンドウ、Shader Graph、Timeline、Input Systemデバッガがある。イベントが使えるのは...Projectウィンドウ。