3
コンポーネントベースのエンティティシステムでメッセージ処理を適切に実装する方法は?
エンティティシステムのバリアントを実装しています。 Entityクラス IDより少しであることが結合コンポーネント一緒に 「コンポーネントロジック」がなく、データのみを持つコンポーネントクラスの束 多数のシステムクラス(別名「サブシステム」、「マネージャー」)。これらはすべてのエンティティロジック処理を行います。ほとんどの基本的な場合、システムは、関心のあるエンティティのリストを反復処理し、各エンティティに対してアクションを実行するだけです。 MessageChannelクラスオブジェクトのすべてのゲームシステムで共有されています。各システムは、特定のタイプのメッセージをサブスクライブしてリッスンし、チャネルを使用して他のシステムにメッセージをブロードキャストすることもできます システムメッセージ処理の最初のバリアントは、次のようなものでした。 各ゲームシステムで順次更新を実行する システムがコンポーネントに対して何かを実行し、そのアクションが他のシステムにとって重要である場合、システムは適切なメッセージを送信します(たとえば、システム呼び出し messageChannel.Broadcast(new EntityMovedMessage(entity, oldPosition, newPosition)) エンティティが移動されるたびに) 特定のメッセージをサブスクライブした各システムは、そのメッセージ処理メソッドを取得します システムがイベントを処理しており、イベント処理ロジックで別のメッセージをブロードキャストする必要がある場合、メッセージはすぐにブロードキャストされ、メッセージ処理メソッドの別のチェーンが呼び出されます このバリアントは、衝突検出システムの最適化を開始するまでは問題ありませんでした(エンティティの数が増えると、本当に遅くなりました)。最初は、単純なブルートフォースアルゴリズムを使用して各エンティティペアを繰り返します。次に、特定のセルの領域内にあるエンティティを格納するセルのグリッドを持つ「空間インデックス」を追加しました。これにより、隣接セルのエンティティのみをチェックできます。 エンティティが移動するたびに、衝突システムはエンティティが新しい位置にあるものと衝突しているかどうかを確認します。そうである場合、衝突が検出されます。衝突するエンティティが両方とも「物理オブジェクト」である場合(両方にRigidBodyコンポーネントがあり、同じスペースを占有しないように互いを押しのけようとしている場合)、専用の剛体分離システムがエンティティを移動するように移動システムに要求しますそれらを分離する特定の位置。これにより、移動システムは変更されたエンティティの位置を通知するメッセージを送信します。衝突検出システムは、空間インデックスを更新する必要があるため、反応するように設計されています。 場合によっては、セルの内容(C#のEntityオブジェクトの汎用リスト)が繰り返し処理されている間に変更され、イテレーターによって例外がスローされるため、問題が発生します。 だから... 衝突をチェックしている間に衝突システムが中断されるのを防ぐにはどうすればよいですか? もちろん、セルの内容が正しく繰り返されることを保証する「巧妙な」/「トリッキーな」ロジックを追加することもできますが、問題は衝突システム自体にあるのではないと思います(他のシステムにも同様の問題がありました)メッセージはシステムからシステムへ移動するときに処理されます。私が必要とするのは、特定のイベント処理メソッドが中断することなく仕事を確実に行うための何らかの方法です。 私が試したもの: 着信メッセージキュー。あるシステムがメッセージをブロードキャストするたびに、そのメッセージは関心のあるシステムのメッセージキューに追加されます。これらのメッセージは、システム更新が各フレームで呼び出されると処理されます。問題:システムAがシステムBのキューにメッセージを追加する場合、システムBがシステムAよりも後(同じゲームフレーム内)に更新される場合、それはうまく機能します。それ以外の場合、メッセージが次のゲームフレームを処理します(一部のシステムでは望ましくありません) 発信メッセージキュー。システムがイベントを処理している間、システムがブロードキャストするメッセージは送信メッセージキューに追加されます。メッセージは、システムの更新が処理されるのを待つ必要はありません。最初のメッセージハンドラが処理を完了した後、「すぐに」処理されます。メッセージの処理によって他のメッセージがブロードキャストされる場合、それらも発信キューに追加されるため、すべてのメッセージが同じフレームで処理されます。問題:エンティティライフタイムシステム(システムでエンティティライフタイム管理を実装)がエンティティを作成した場合、いくつかのシステムAとBに通知します。システムAがメッセージを処理している間、メッセージチェーンが発生し、最終的に作成されたエンティティが破棄されます(たとえば、弾丸エンティティが障害物と衝突する場所で作成され、弾丸が自己破壊します)。メッセージチェーンが解決されている間、システムBはエンティティ作成メッセージを取得しません。したがって、システムBがエンティティ破壊メッセージにも関心がある場合、システムBはそれを取得し、「チェーン」の解決が完了した後にのみ、最初のエンティティ作成メッセージを取得します。これにより、破棄メッセージは無視され、作成メッセージは「受け入れられます」、 編集-質問への回答、コメント: 衝突システムがセルの内容を繰り返し処理している間、誰がセルの内容を変更しますか? 衝突システムが一部のエンティティとその近隣で衝突チェックを行っている間、衝突が検出される可能性があり、エンティティシステムは他のシステムがすぐに反応するメッセージを送信します。メッセージに対する反応により、他のメッセージが作成され、すぐに処理される場合があります。そのため、他のシステムは、以前の衝突チェックがまだ終了していない場合でも、衝突システムがすぐに処理する必要があるメッセージを作成する場合があります(たとえば、衝突システムが空間インデックスを更新する必要があるため、エンティティを移動します)。 グローバルな送信メッセージキューを使用できませんか? 最近、単一のグローバルキューを試しました。新しい問題を引き起こします。問題:タンクエンティティを壁エンティティに移動します(タンクはキーボードで制御されます)。それからタンクの方向を変えることにしました。タンクと壁を各フレームに分離するために、CollidingRigidBodySeparationSystemはタンクを壁から可能な限り最小の距離だけ移動します。分離方向は、戦車の移動方向の反対方向でなければなりません(ゲームの描画が開始されると、戦車は壁に移動したことがないように見えるはずです)。しかし、方向はNEW方向とは逆になり、タンクを壁の最初とは異なる側に移動します。問題が発生する理由:これがメッセージの処理方法です(簡略化されたコード): public void Update(int deltaTime) { m_messageQueue.Enqueue(new TimePassedMessage(deltaTime)); while (m_messageQueue.Count > 0) { Message message = m_messageQueue.Dequeue(); this.Broadcast(message); } } private …