ビデオゲームはどのようにオブジェクト指向ですか?[閉まっている]


18

私は、ビデオゲーム、特に巨大な3Dプロジェクトでオブジェクトの向きがどの程度使用されているのか、常に疑問に思っていました。両方ともその属性trollwerewolf継承しenemy、それらの一部を上書きしたのはクールだと思います。しかし、クラスは重すぎて実用的ではないかもしれません。


2
オブジェクト指向をどのように定量化(または客観的に修飾)しますか?MeyersまたはDahl-Nygaardsで回答が必要ですか?

明らかにそれらは非常に重いので、OOベースの重い言語が業界標準です。
共産主義のダック

4
C ++を意味する場合は、タイプセーフな汎用プログラミングを速度重視の方法で探索するのに適した言語であるため、選択されました。いくつかのOOP機能は明らかに、下位互換性のためだけに残された残念な誤設計です。;)

回答:


20

あなたの例に来るために、ビデオゲームのエンティテについての私の考えを説明します。オブジェクトとクラスの一般的な長所と短所については説明しません。ビデオゲームにおけるそれらの役割全体についても説明しません。

小さなビデオゲームのエンティティは、主に別々のクラスで定義されます。大きなゲームでは、多くの場合ファイルで定義されます。次の2つの一般的なアプローチがあります。

拡張:あなたの例TrollWerewolfは拡張しEnemy、拡張しEntityます。Entity一方で、などの運動、健康などの基本的な事柄についての世話をするEnemy(Minecraftのは、このアプローチを以下の)敵としてサブクラスを述べると、他のことをするだろう。

コンポーネントベース:2番目のアプローチ(および私のお気に入り)はEntity、コンポーネントを持つ1つのクラスです。コンポーネントはMoveableまたはEnemyです。これらのコンポーネントはの世話を1運動や物理学のようなもの。この方法により、長い拡張チェーンが破損し、競合が発生するリスクが最小限に抑えられます。例:移動可能なキャラクターを継承する場合、移動できない敵を作成するにはどうすればよいですか?

World of WarcraftやStarcraft 2のような大きなビデオゲームでは、エンティティはクラスではなく、エンティティ全体を指定するデータのセットです。エンティティがクラスではなくスクリプトで定義するので、スクリプトも重要です(一意の場合は、移動などではありません)。

私は現在RTSをプログラミングしており、ゲーム内で動的なEntites、Skill、Items、およびその他すべての記述子とスクリプトファイルを使用して、コンポーネントベースのアプローチを採用しています。


2
興味深いですね。しかし、私はうそをつくことはありません、私はcomponentこのコンテキストで何がわからない。リンクをいただければ幸いです。
vemv

コンポーネントは、単一の機能に責任を持つエンティティです。コンポーネントベースのエンティティは、コンポーネントを所有し(該当する場合)、それから継承しません。所有権は、エンティティがその動作を変更するコンポーネントの1つを非アクティブ化できることを意味します。古典的な拡張パラダイムでは、動作はスーパークラスに「属する」ためです。エンティティはコンポーネントにすることができ、コンポーネントは必要に応じてクラスまたは構造体としてモデル化できます。
FxIII

コンポーネント(時々 、エンティティ・システムと呼ばれる) -あなたがアイデアのためにこのブログを検索することができt-machine.org あり変種があるが、基本的な考え方は、コンポーネントがモデルを構築するように、特別な目的のオブジェクトとあなたの俳優の絆一緒にコンポーネントの集まりであるということですレゴから。
パトリックヒューズ

2
ゲーム開発に関するブログを開始し、これに関する最初のブログエントリを書きました。新しいものはありませんが、少しのコードがあります:jagamedev.wordpress.com/2011/06/26/…–
マルコ

23

トロルと狼男の両方が敵からその属性を継承し、それらのいくつかを上書きしたことはクールだと思います。

残念ながら、90年代に人気の多型へのこのアプローチは、実際には悪い考えであることが示されています。敵リストに「オオカミ」を追加することを想像してください-それは、いくつかの属性を狼と共有しているので、共有された基本クラスにそれらを統合したいと思います。「ウルフライク」。ここで、「Human」を敵リストに追加しますが、狼男は時々人間であるため、2本の足で歩く、話すことができるなどの属性も共有します。人間と狼男の共通の「Humanoid」ベースを作成しますか、そしてウルフライクに由来する狼男を停止する必要がありますか?または、両方から継承しますか?その場合、ヒューマノイドの何かがWolfLikeの何かと衝突するときにどの属性が優先されますか?

問題は、実世界をクラスのツリーとしてモデル化しようとしても効果がないことです。3つのことを考えてみると、それらの動作を共有と非共有に分ける4つの異なる方法を見つけることができます。オブジェクトが全体を構成するいくつかの小さなオブジェクトで構成され、必要に応じて小さなオブジェクトを交換したり変更したりできるように、多くの人が代わりにモジュール式またはコンポーネントベースのモデルに変わりました。ここには、1つの敵クラスのみがあり、その歩行方法(2足歩行と4足歩行など)、攻撃方法(咬傷、爪、武器、拳など)、皮膚(緑色)のサブオブジェクトまたはコンポーネントが含まれます。トロール、狼男と狼の毛皮など)

これはまだ完全にオブジェクト指向ですが、有用なオブジェクトの概念は、教科書で一般的に教えられているものとは異なります。さまざまな抽象クラスの大規模な継承ツリーと、ツリーの先端にいくつかの具体的なクラスを持たせるのではなく、通常、抽象概念(たとえば「敵」)を表す1つの具体クラスだけが、より抽象的な概念を表すより具体的なクラスを含む(例、攻撃、肌のタイプ)。これらの2番目のクラスは、1レベルの継承(たとえば、基本攻撃クラスと特定の攻撃の複数の派生クラス)を介して最適に実装できる場合もありますが、深い継承ツリーはなくなります。

しかし、クラスは重すぎて実用的ではないかもしれません。

現代のほとんどの言語では、クラスは「重く」ありません。これらはコードを記述するための別の方法に過ぎず、通常は、構文が簡単な場合を除き、とにかく記述した手続き型アプローチをラップするだけです。しかし、必ず、関数が実行するクラスを作成しないでください。クラスは本質的に優れているのではなく、コードを管理または理解しやすくする場合のみです。


3
この回答が大好きです:)
チャレク・トムザック

8

「重すぎる」とはどういう意味かわかりませんが、C ++ / OOPは他の場所で使用されているのと同じ理由でゲーム開発の共通語です。

キャッシュを吹き飛ばすという話は設計上の問題であり、言語機能ではありません。C ++は過去15〜20年間ゲームで使用されてきたため、それほど悪くはなりません。Javaは、スマートフォンの非常に厳しい実行環境で使用されるため、それほど悪くはなりません。

さて、本当の問題に移ります。長年、クラス階層は深くなり、それに関連する問題に悩まされ始めました。最近では、クラス階層がフラット化されており、論理駆動型の継承ではなく、構成やその他のデータ駆動型の設計が行われています。

それはすべてOOPであり、新しいソフトウェアを設計するときのベースラインとして使用されますが、クラスが具体化するものに焦点を当てているだけです。


2

クラスには、実行時のオーバーヘッドは含まれません。実行時ポリモーフィズムは、関数ポインタを介して呼び出しているだけです。これらのオーバーヘッドは、Draw呼び出し、同時実行同期、ディスクI / O、カーネルモードスイッチ、メモリのローカリティの低下、動的メモリ割り当ての過剰使用、アルゴリズムの選択の不備、スクリプト言語の使用、リストなどの他のコストと比較して非常に小さいです。に行く。オブジェクト指向がそれ以前に来ていたものに本質的に取って代わる理由があります。


さまざまなポリモーフィック型間でのディスパッチは、メモリの局所性が低い方法で行われます。C ++ vtablesやObjective-C IMPキャッシングなどの最も軽量な実装であっても、実際にポリモーフィズムを使用してさまざまなタイプのオブジェクトを処理する場合、キャッシュが吹き飛ばされます。もちろん、ポリモーフィズムを使用しない場合、vtableがキャッシュ内に留まるか、IMPキャッシュメッセージが正しい場所につながりますが、なぜ気になるのでしょうか?また、JavaまたはC#ではコストが高くなり、JavaScriptまたはPythonではさらにコストが高くなります。

1
@Joe Wreschnig:これらの遅い言語を使用している場合、オブジェクト指向のコストは、通訳/ JITなどの他のコストと比較して簡単です。さらに重要なことは、メモリの局所性が乏しいことについて理論を立てることができますが、実際には、CPUの分岐予測、アウトオブオーダー実行、および同様の機能により、コストがほぼ完全に無効になります。それ以外の場合、なぜオブジェクト指向を使用して非常に多くの高性能ソフトウェアが作成されるのですか?
-DeadMG

1

留意すべきことの1つは、オブジェクト指向コードの概念は、多くの場合、言語での実装よりも価値があることです。特に、メインループでエンティティに対して1000回の仮想更新呼び出しを行うと、360やPS3などのプラットフォームのC ++でキャッシュが混乱する可能性があります。スピードの。

多くの場合、継承とカプセル化のオブジェクト指向の概念に従います-言語自体とは異なる方法で実装するだけです(たとえば、奇妙な再帰テンプレート、継承の代わりに構成(Marcoによって記述されたコンポーネントベースの方法))。コンパイル時に継承を常に解決できる場合、パフォーマンスの問題はなくなりますが、テンプレート、カスタムRTTI実装(ここでも組み込みのものは通常非実用的です)、またはマクロなど

iOS / Mac用のObjective-Cには、メッセージングスキームに同様の、しかしより悪い問題があります(派手なキャッシングの場合でも)...


0

使用しようとしているメソッドは、クラスの継承とコンポーネントを組み合わせています。(まず、私はEnemyをクラスにしません。それをコンポーネントにします。)すべてに1つの汎用Entityクラスを使用し、必要なコンポーネントを追加してエンティティを肉付けするという考えは好きではありません。代わりに、コンストラクターでコンポーネント(およびデフォルト値)を自動的に追加するクラスを作成することを好みます。次に、サブクラスはコンストラクターに追加のコンポーネントを追加するため、サブクラスにはコンポーネントと親クラスコンポーネントが含まれます。たとえば、武器クラスはすべての武器に共通する基本的なコンポーネントを追加し、次に剣サブクラスは剣に固有の追加コンポーネントを追加します。私はまだtext / xmlアセットを使用してエンティティ(または特定の剣など)の値を定義するかどうかについて議論しています。またはそれをすべてコードで実行するか、適切な組み合わせを選択します。これにより、ゲームはコード言語の型情報とポリモーフィズムを引き続き使用でき、ObjectFactoriesがより簡単になります。


3
こんにちはクリス。あなたの経験と計画を共有することは素晴らしいことですが、それは実際に質問に直接答えているわけではありません。質問は、あなたがそれをどうやって計画しているかに答えるとき、これらのことが通常どのように行われるに関するものだと思います。それらは似たような質問ですが、実際には同じではありません。
マイケルハウス
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.