イベントマネージャシステムを設計する際に考慮すべきことは何ですか?


9

私はJavaゲームエンジンの基本をいじくり回しており、イベントマネージャーシステムに追加する準備ができたところに到達しました。

理論的には、イベントマネージャーがすべきことはわかっています。特定のイベントにオブジェクトを「登録」できるようにし、イベントマネージャーがイベントの通知を受けるたびに、イベントを「登録済み」リスナーにブロードキャストします。私が困惑しているのは、それを実装し始める方法です。

イベントシステムを最初から実装することについて、オンラインで何も見つけることができなかったので、この場合のベストプラクティスとは何か、私がすべきことすべきでないことについてのインプットを探しています。

たとえば、ゲームオブジェクトごとにEventManagerフィールドが本当に必要なのでしょうか。すべてのゲームオブジェクトは単一の抽象親クラスを継承しているため、静的参照を使用して、すべてのゲームオブジェクト間で共有されるイベントマネージャーのインスタンスが1つだけになるようにする必要があります。アプレットを使って、各オブジェクトのレンダリングにすでに使用しているのと同じようなことをしています。

可能なサブスクライブイベントごとに、ある種のコレクションを維持する必要があると思います。必要に応じて、リストにゲームオブジェクトを追加したり削除したりします。ブロードキャストする必要があるイベントのキューを作成することは可能だと思います。その場合は、メインのゲームループに「EventManager.Update()」を追加し、Update()メソッドが最後に発生したイベントをブロードキャストするようにすることができます各フレームの。最後に、各オブジェクトにはHandleEvent(Event e)メソッドがあり、それらを適切に解析して応答することができます。


これは、そのようなシステムの実装に向けた適切な方向性のように聞こえますか、それとも、軌道から外れているのか、および/または非常に明白なものがないのですか?


2
gamedev.stackexchange.com/questions/7718/…これは、始める前に役立つ以前に提供されたQ / AIです。
ジェームズ

@ジェームスああ-良いリンクのように見えます。ありがとう!先ほどは見逃したと思います。
レイヴンドリーマー

4
いくつかの追加のヒント:特にイベントハンドラーが追加のイベントをトリガーする場合、一部のイベントをフレームの最後ではなく、実際にすぐに有効にするかどうかを決定します。イベントループが発生するとどうなるかを考えてください。キューを使用する場合は、優先システム、またはキューをバイパスする方法が必要になる場合があります。
sam hocevar

回答:


6

これは、必要に応じて簡単にすることができます。

for each object in the subscriber list:
    object.notify(this event) // (or 'HandleEvent' if you prefer)

イベントマネージャーが「すべき」ことを考え出さないでください。必要なことを考え出してください。残りはそこから、または少なくとも、いくつかのより具体的な質問を提案する必要があります。


3
「Xが何をすべきかを考え出してみてはいけない-あなたがそれを行うために必要なことを考え出してください。」実際、関数を呼び出すために分離されたまだ集中化された方法が必要になるまでは、イベントマネージャを追加しないでください。

@JoeWreschnigああ神はい=)「あなたがそれを行うために必要なことを考え出す」これは、開発者が心に留めておくべき上位3ビットのアドバイスの1つです。
Patrick Hughes

最初は、イベントマネージャーを追加しないことについての2番目の文に同意しませんでした。ほとんどのゲームは、分離されたが集中化された方法で関数を呼び出すことで利益を得ることができるからです。あらゆる種類のイベントマネージャーがあります。そう、そうです
ジョッキング

3

イベントシステムに必要な3つの重要なメソッドは、addListener()メソッド、removeListener()メソッド、およびdispatchEvent()へのメソッドです。つまり、メソッドオブジェクトはイベントの登録に使用し、メソッドオブジェクトは登録解除に使用し、メソッドは実際にすべてのリスナーにイベントをブロードキャストします。他のすべては肉汁です。

お気づきのように、登録されたリスナーを追跡するには、何らかのデータ構造が必要です。最も簡単な方法は、ベクトル(または言語によっては単純に配列)をイベントに関連付ける連想配列(Dictionary、またはJavaScriptのオブジェクト)です。そのベクトルは、登録されているすべてのリスナーのリストです。リストにリスナーを追加するか、add / removeListener()メソッドでリスナーを削除します。

dispatchEvent()でイベントをブロードキャストすることは、ベクトルをループするのと同じくらい簡単です。イベントの優先順位などを並べ替えることで、ディスパッチをさらに複雑にすることができますが、必要になるまで心配する必要はありません。多くの場合、それは必要ありません。

イベントとともに渡すデータを検討する場合、dispatchEvent()には少し微妙な違いがあります。最も基本的なレベルでは、追加のデータを渡さない可能性があります。リスナーが知る必要があるのは、イベントが発生したことだけです。ただし、ほとんどのイベントには追加のデータ(イベントが発生した場所など)があり、dispatchEvent()でいくつかのパラメーターを受け入れて渡す必要があります。

たとえば、私のゲームオブジェクトのそれぞれに「EventManagner」フィールドが本当に必要ですか?すべてのゲームオブジェクトは単一の抽象親クラスから継承するので、静的参照を使用して、すべてのゲームオブジェクト間で共有されるEventManagerのインスタンスが1つだけになるようにする必要があります。

すべてのゲームオブジェクトにEventManagerクラスへの参照を与える方法はたくさんあります。静的参照は確かに1つの方法です。もう1つはシングルトンです。ただし、これらのアプローチはどちらも柔軟性に欠けるため、ここにいるほとんどの人はサービスロケータまたは依存関係の注入を推奨しています。私は依存性注入を行ってきました。これは、オブジェクトごとに個別のEventManagerフィールドを意味します。これは避けたいように思われますが、これが問題である理由はわかりません。大量のポインタを格納することによるオーバーヘッドが多いようではありません。


ええ、私が初めて依存性注入を把握して実装したのは、EventDispatcherオブジェクト用でした。それを身につけたら、私はそのパターンで多くのものをリファクタリングするのに悩みました。
11

0

免責事項

私はJavaプログラマーではありませんが、最も簡単な言語(IMHO)の1つであるC89でイベントシステムを作成する方法を説明する記事を書きました。

https://prdeving.wordpress.com/2017/04/03/event-driven-programming-with-c-89/

イベント処理の基本はかなり単純です。

  • コールバックでイベントを登録する
  • トリガーイベント
  • リスナーをループして、一致があるかどうかを確認します
  • 見つかった場合はハンドラを起動します
  • 繰り返す

これを知っていれば(しかし、Javaでの実装方法を知らなくても)、いくつかのハンドラー(イベントを処理する関数)が必要であり、それらをイベント名と対になっている配列(Cのポインターを使用)に追加する必要があることが簡単にわかります。イベントをトリガーすると、トリガーされたイベント名とその引数がスタックに格納されます。これはイベントプールと呼ばれます。

次に、そのスタックの最初のイベントをポップし、そのハンドラーを見つけようとするイベントダイジェスト(単純なループ)があり、存在する場合は、paramsでそれを実行します。

もちろん、このイベントダイジェストはいつでもトリガーできます(たとえば、Update()関数を呼び出した後、フレームごとに1回)。


-2

EventManagerフィールドには、静的変数を使用します。EventManagerのシングルトンも素晴らしいアイデアです。

あなたのアプローチは良いように思えますが、スレッドセーフにすることを忘れないでください。

「GameEvent」の前または後にIOイベントを実行することをお勧めします。そのため、1つのフレームで「GameEvent」はすべて同じデータを処理します。


「スレッド保存」?うーん
U62

完全に不要で正当化されていないシングルトンの提案の場合は-1。
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.