21

私は、Java2Dとニュートン物理学を使用する単純なゲームを設計しています。現在、私のメインの「ゲームループ」は次のようになっています。

do {
  for (GameEntity entity : entities) {
    entity.update(gameContext);
  }

  for (Drawable drawable : drawables) {
    drawable.draw(graphics2d);
  }
} while (gameRunning);

エンティティがそれ自体を更新するように指示されると、エンティティに適用されている現在の力に基づいて速度と位置を調整します。ただし、他の動作を示すエンティティが必要です。たとえば、「悪者」がプレイヤーに撃たれた場合、エンティティは破壊され、ゲームの世界から削除されます。

私の質問:オブジェクト指向の方法でこれを達成するための最良の方法は何ですか?これまでに見たすべての例では、ゲームループをのような神のクラスに組み込んでいます。このクラスは、衝突の検出、悪者の殺害の確認、プレーヤーの殺害の確認、塗り直しなどGameの手順を実行します。すべてのゲーム状態(残りのライブなど)をカプセル化します。言い換えれば、それは非常に手続き的であり、すべてのロジックはGameクラスにあります。誰かがより良いアプローチをお勧めできますか?

これまで私が考えたオプションは次のとおりです。

  • GameContextエンティティが必要に応じて自分自身を削除したり、ゲームの状態を更新したりできる各エンティティにaを渡します(たとえば、プレーヤーが殺された場合は「実行されていません」)。
  • GameEntityそれぞれを中央クラスのリスナーとして登録しGame、イベント指向のアプローチを取ります。たとえば、衝突は、衝突CollisionEventの2人の参加者に解雇される結果になります。
4

6 に答える 6

14

私は2つの商用ゲームエンジンと緊密に連携してきましたが、それらは同様のパターンに従います。

  • オブジェクトは、エンティティ全体ではなく、ゲームエンティティのコンポーネントまたは側面(物理的、レンダリング可能など)を表します。コンポーネントのタイプごとに、コンポーネントの巨大なリストがあり、コンポーネントを持つエンティティインスタンスごとに1つずつあります。

  • 「ゲームエンティティ」タイプ自体は、単なる一意のIDです。コンポーネントの各巨大リストには、エンティティIDに対応するコンポーネント(存在する場合)を検索するためのマップがあります。

  • コンポーネントが更新を必要とする場合、それはサービスまたはシステムオブジェクトによって呼び出されます。各サービスは、ゲームループから直接更新されます。または、依存関係グラフから更新順序を決定するスケジューラオブジェクトからサービスを呼び出すこともできます。

このアプローチの利点は次のとおりです。

  • 組み合わせごとに新しいクラスを記述したり、複雑な継承ツリーを使用したりすることなく、機能を自由に組み合わせることができます。

  • ゲームエンティティの基本クラスに入れることができるすべてのゲームエンティティについて想定できる機能はほとんどありません(ライトはレースカーやスカイボックスと何が共通していますか?)

  • IDからコンポーネントへのルックアップは費用がかかるように思われるかもしれませんが、サービスは特定のタイプのすべてのコンポーネントを反復処理することにより、集中的な作業のほとんどを実行しています。このような場合、必要なすべてのデータを1つの整頓されたリストに保存する方が適切です。

于 2010-02-10T16:29:56.453 に答える
6

私が取り組んだ特定のエンジンでは、ロジックをグラフィック表現から切り離し、目的のメッセージを送信するオブジェクトを作成しました。これは、ローカルマシン上またはネットワーク上にゲームを存在させ、コードの観点からゲームを区別できないようにするために行いました。(コマンドパターン)

また、実際の物理モデリングは、その場で変更できる別のオブジェクトで実行されました。これにより、重力などを簡単に混乱させることができます。

イベントドリブンコード(リスナーパターン)とたくさんのタイマーを多用しました。

たとえば、衝突イベントをリッスンできる交差可能なオブジェクトの基本クラスがありました。それをヘルスボックスにサブクラス化しました。衝突時に、プレイヤーエンティティがヒットした場合、コライダーにヘルスを取得するようにコマンドを送信し、それを聞くことができるすべての人にサウンドをブロードキャストするメッセージを送信し、衝突を非アクティブ化し、アニメーションをアクティブにしてグラフィックを削除しました。シーングラフを作成し、後で自分自身を復元するようにタイマーを設定します。複雑に聞こえますが、実際はそうではありませんでした。

私が思い出すと(そしてそれは12年になります)、私たちはシーンの抽象的な概念を持っていたので、ゲームは一連のシーンでした。シーンが終了すると、通常は現在のシーンを削除して別のシーンを開始するコマンドを送信するイベントが発生しました。

于 2010-02-09T19:02:20.637 に答える
3

メインのゲームクラスがあるため、すべてのロジックがそのクラスで発生する必要があることに同意しません。

ここでの過度の単純化は、私の主張を明確にするためにあなたの例を模倣しています。

mainloop:
  moveEntities()
  resolveCollisions()   [objects may "disappear"/explode here]
  drawEntities()        [drawing before or after cleanEntitites() ain't an issue, a dead entity won't draw itself]
  cleanDeadEntities()

これで、Bubbleクラスができました。

Bubble implements Drawable {

handle( Needle needle ) {
    if ( needle collide with us ) {
        exploded = true;
    }
}

draw (...) {
   if (!exploded) {
      draw();
     }
  }
}

したがって、確かに、エンティティ間でメッセージを渡すことを処理するメインループがありますが、バブルとニードルの間の衝突に関連するロジックは、メインのゲームクラスには含まれていません。

あなたの場合でも、動きに関連するすべてのロジックがメインクラスで発生しているわけではないと確信しています。

したがって、「すべてのロジックはメインクラスで発生する」という太字で書いたというあなたの声明には同意しません。

これは単に正しくありません。

優れたデザインについて:ゲームの別の「ビュー」(ミニマップなど)を簡単に提供でき、「フレームごとの完璧なリプレイヤー」を簡単にコーディングできる場合、デザインはおそらくそうではありません。それは悪いことです(つまり、入力とそれらが発生した時刻のみを記録することで、ゲームをプレイしたとおりに正確に再現できるはずです。これが、Age of Empires、Warcraft3などがリプレイを行う方法です。ユーザー入力とそれらが発生した時刻が記録されます[これが、再生ファイルが通常非常に小さい理由でもあります])。

于 2010-02-09T19:05:47.760 に答える
2

私は自分のエンジン(生とダーティ)を書いていますが、まともなOOモデルを持つ構築済みのエンジンはOgreです。それを見てみることをお勧めします(それはオブジェクトモデル/ APIです)。ノードの割り当ては少しファンキーですが、見れば見るほど完全に理にかなっています。また、多くの実用的なゲームの例が非常によく文書化されています。

私はそれからいくつかのトリックを自分で学びました。

于 2010-02-09T19:10:06.977 に答える
2

他の投稿にはまだコメントできないので、これを回答として投稿するだけです。

Evan Rogersの優れた回答の補足として、次の記事に興味があるかもしれません。

http://www.gamasutra.com/blogs/MeganFox/20101208/6590/Game_Engines_101_The_EntityComponent_Model.php

http://cowboyprogramming.com/2007/01/05/evolve-your-heirachy/

于 2011-02-23T00:27:19.110 に答える
1

このゲームは、モデルとビューを分離しておくための実験でした。オブザーバーパターンを使用して、ゲームの状態の変化をビューに通知しますが、イベントはおそらくより豊富なコンテキストを提供します。元々、モデルはキーボード入力によって駆動されていましたが、分離により、タイマー駆動のアニメーションを簡単に追加できました。

補遺:ゲームのモデルを分離しておく必要がありますが、そのモデルを必要な数のクラスにリファクタリングすることができます。

于 2010-02-09T19:07:29.533 に答える