エンティティは自分の動きを処理する必要がありますか?
私は現在、タイルオブジェクトの2D配列で構成される部屋を使用して単純なターンベースのゲームを構築しています。それらはWallTileまたはFloorTileにすることができます。タイルオブジェクトには、北、東、南、西のネイバーがあります。FloorTilesにはエンティティのスタックがあるため、一番上のエンティティを簡単に描画できます。
今、私は疑問に思っています、私のプレーヤーはエンティティです、プレーヤーが動くとき、プレーヤーはそのロジックを処理するべきですか、それとも私のプレーヤーを保持するゲームですか?
最初のケースでは、次のようなものになります。
class Player : Entity {
public Tile CurrentLocation
public bool CanMoveTo(Direction) {
// Let's say that direction is East
return CurrentLocation.EastNeighbour == FloorTile
}
// This method does the actual move
public void MoveTo(Direction)
}
2番目のケースでは、次のようになります。
class Game {
public Player Player;
public Room Room;
public void Update() {
// Lets say we get direction from some input from somewhere
if Room.TileAt(player.Y, player.X) is FloorTile then MoveThere()
}
}
2つのオプションの中で最も優れているものが何であるかはよくわかりません。最初は最初の解決策を考えています。ヘルプやヒントをいただければ幸いです。
回答
物事を行うための正しい方法も間違った方法もありません。あなたのために働くか、あなたのために働かない方法だけ。
主流のゲームアーキテクチャには、いくつかの競合する哲学があります。
A:オブジェクト指向アーキテクチャ
すべてのエンティティはオブジェクトで表す必要があります。これらのオブジェクトには、独自のロジックがすべて含まれ、内部状態がカプセル化され、明確に定義されたインターフェイスを介して他のオブジェクトと通信する必要があります。コアゲームエンジンは、ループ内のすべてのエンティティの「Update」メソッドと「Render」メソッドを呼び出すだけで、残りはエンティティに実行させます。
いくつかの機能を共有するオブジェクトがある場合、通常は継承を通じて、つまり共通の機能を共通の基本クラスに抽出することによってそれを行います。たとえば、すべての戦闘コードを含むクラスCombatantから継承し、移動の基本コードを含むEntityから継承するクラスPlayerとクラスEnemyが必要な場合があります。また、敵は、すべての敵に共通のロジックのみを含む抽象クラスである可能性が高く、さまざまな敵のタイプと、敵から継承するクラスに実装された固有の動作があります。
基本的にあなたが本によってOOPをすることになっている方法。
このスタイルは、アプリケーション開発から来た開発者によく見られます。これは、そこで主流のスタイルだからです。そして、アプリケーションで機能することは、ゲームにとって必ずしも完全に間違っているわけではありません。
B:エンティティ-コンポーネントアーキテクチャ
このスタイルは、継承よりも構成を優先します。エンティティは1つのオブジェクトではなく、各オブジェクトがそのエンティティの異なる機能を実装するオブジェクトのコレクションです。したがって、プレーヤーが実行できるすべてのことを実装する「プレーヤー」クラスはありません。「Sprite」、「Mover」、「Shooter」、「Hitpoints」、および「PlayerInput」コンポーネントを持つ汎用エンティティがあります。
このアプローチの利点は、コンポーネントを非常に簡単に組み合わせて、新しい種類のゲームエンティティを作成できることです。これは、ゲームデザインの迅速な反復に非常に役立ちます。これは、いくつかの人気のあるゲームエンジンで人気のあるアプローチです。たとえばUnityのように。
C:エンティティ-コンポーネント-システムアーキテクチャ
エンティティは、「エンティティコンポーネント」アーキテクチャと同様に、コンポーネントのコレクションである必要があります。しかし、このアーキテクチャでは、これらのコンポーネントは単なるデータホルダーである必要があります。通常、パブリック変数のみを含み、メソッドを含まない構造の形式です。すべてのロジックはシステム内にある必要があります。各システムは、1つのタイプのコンポーネントまたはコンポーネントのセットを管理する責任があります。たとえば、すべてのエンティティの位置コンポーネントを移動コンポーネントで更新するMovementSystemがあります。
しかし、これは単純化しすぎです。詳細を知りたい場合は、「純粋なECSとは」という質問を確認してください。。また、entity-component-systemタグの下にある他の質問もご覧ください。
ECSアプローチは現在非常に流行しています。その主な論拠は、エンティティコンポーネントアプローチのモジュール性と反復速度を犠牲にすることなく、(特にメモリの局所性とマルチスレッドに関して)非常に優れたパフォーマンスの最適化を可能にするということです。ただし、不利な点は、堅牢で柔軟なECSアーキテクチャには、「舞台裏」で実行される非常に多くのアーキテクチャコードが必要であり、初心者には実装が難しい場合があることです。
どちらを使うべきですか?
それはあなたが自分でそれを理解する必要があるものです。ただし、ゲームにどのアプローチを選択する場合でも、それに固執することをお勧めします。可能であれば、ゲーム内でアーキテクチャパターンを組み合わせて使用することは避けてください。
これは基本的に、ゲームネットワークを通過するデータの量に依存します。大規模なマルチプレイヤーは、誰がどこで何をしているのかを常に追跡する必要があります(そうしないと、大規模な不正行為が発生します)。ただし、サーバーからすべてのクライアント(権限のあるサーバーと呼ばれる)へのパケットの送受信は、重要なユーザーの数に関係なく実用的ではありません。したがって、哲学は、他のプレイヤーに影響を与えることなく、プレイヤーに制御させることができるロジックの量のようです。言い換えれば、あなたとあなただけに影響を与えるもの(略奪を除く)は、クライアントによって処理することができます。一方、複数のプレーヤーに影響を与えるものはすべてサーバーで処理する必要があります。または、処理されない場合は、効果が発生する前に少なくとも許可されます。