アーキテクチャ(リアルタイム戦略ゲームのNPC)の多重継承に代わるものはありますか?


11

コーディングは実際にはそれほど難しくありません。難しい部分は、理にかなっており、読みやすく、理解可能なコードを書くことです。だから、私はより良い開発者を獲得し、強固なアーキテクチャを作りたいと思っています。

それで、私はビデオゲームでNPCのアーキテクチャを作成したいと思います。これは、Starcraft、Age of Empires、Command&Conquersなどのようなリアルタイム戦略ゲームです。したがって、さまざまな種類のNPCがあります。A NPCは、これらの多くの能力(メソッド)に1を持つことができますBuild()Farm()Attack()

例:
労働者がすることができますBuild()し、Farm()
戦士Attack()
市民Build()Farm()Attack()
漁師ができるFarm()Attack()

これまでのところすべてが明確であることを願っています。

だから今、私は私のNPCタイプとその能力を持っています。しかし、技術的/プログラム的な側面を見てみましょう。
さまざまな種類のNPCに適したプログラムアーキテクチャは何でしょうか。

さて、基本クラスを持つことができました。実際、これはDRYの原則を守る良い方法だと思います。WalkTo(x,y)すべてのNPCが移動できるため、基本クラスのようなメソッドを持つことができます。しかし今、本当の問題に取り掛かりましょう。自分の能力をどこで実装しますか?(覚えておいてください:Build()Farm()およびAttack()

能力は同じロジックで構成されるため、各NPC(Worker、Warrior、..)にそれらを実装することは、DRYの原則に迷惑をかける/破るでしょう。

さて、基本クラス内に能力を実装できました。NPCは、能力Xを使用することができる場合、これは検証というロジックのいくつかの種類が必要になりIsBuilderCanBuild、..私は私が表現したいものを明確だと思います。
しかし、私はこの考えにあまり気分がよくありません。これは、機能が多すぎる肥大化した基本クラスのように聞こえます。

プログラミング言語としてC#を使用しています。したがって、ここでは多重継承はオプションではありません。手段:のような追加の基本クラスがあるFisherman : Farmer, Attackerと機能しません。


3
私はこれに対する解決策のように見えるこの例を見ていた:en.wikipedia.org/wiki/Composition_over_inheritance
ショーンマクリーン14

回答:


14

構成とインターフェイスの継承は、従来の多重継承の通常の代替手段です。

上記で説明した「can」という単語で始まるものはすべて、、ICanBuildまたはなどのインターフェイスで表すことができる機能ですICanFarm。これらのインターフェースは、必要な数だけ継承できます。

public class Worker: ICanBuild, ICanFarm
{
    #region ICanBuild Implementation
    // Build
    #endregion

    #region ICanFarm Implementation
    // Farm
    #endregion
}

クラスのコンストラクターを介して、「ジェネリック」なビルドおよびファーミングオブジェクトを渡すことができます。それが作曲の出番です。


ただ、大声で思考:「関係は」(内部)BEは考えているの代わりである(労働者は、労働者がビルダーである代わりにビルダーを持っています)。あなたが説明した例/パターンは理にかなっており、私の問題を解決するための素晴らしい分離方法のように聞こえます。しかし、私はこの関係を得るのに苦労しています。
Brettetete

3
これは、ロバートのソリューションと直交していますが、複雑な副作用のネットワークの作成を避けるために、コンポジションを多用する場合は、不変(通常よりも)を優先する必要があります。理想的には、ビルド/ファーム/攻撃の実装は、他のものに触れることなくデータを取り込み、データを吐き出します。
ドーバル14

1
から継承したため、ワーカーは引き続きビルダーですICanBuild。構築用のツールも持っていることは、オブジェクト階層の副次的な関心事です。
ロバートハーヴェイ14

理にかなっています。この原理を試してみます。わかりやすく説明的な回答をありがとう。本当に感謝しています。この質問はしばらくの間公開します:)
Brettetete

4
@Bretteteteビルド、ファーム、攻撃、ハントなどの実装を、キャラクターが持っているスキルと考えると役立つ場合があります。BuildSkill、FarmSkill、AttackSkillなど。その後、構成ははるかに理にかなっています。
エリックキング14

4

他のポスターが述べているように、コンポーネントアーキテクチャがこの問題の解決策になる可能性があります。

class component // Some generic base component structure
{
  void update() {} // With an empty update function
} 

この場合、更新機能を拡張して、NPCに必要な機能を実装します。

class build : component
{
  override void update()
  { 
    // Check if the right object is selected, resources, etc
  }
}

コンポーネントコンテナを繰り返して各コンポーネントを更新すると、各コンポーネントの特定の機能を適用できます。

class npc
{
  void update()
  {
    foreach(component c in components)
      c.update();
  }

  list<component> components;
}

オブジェクトの各タイプが必要なため、何らかの形のファクトリパターンを使用して、必要な個々のタイプを作成できます。

npc worker;
worker.add_component<build>();
worker.add_component<walk>();
worker.add_component<attack>();

コンポーネントがますます複雑になるにつれて、常に問題に直面しています。コンポーネントの複雑さを低く抑える(1つの責任)ことで、その問題を軽減できます。


3

このようにインターフェースを定義する場合ICanBuild、ゲームシステムはタイプごとにすべてのNPCを検査する必要があります。これは通常、OOPで顔をしかめます。例:if (npc is ICanBuild)

次の方法をお勧めします。

interface INPC
{
    bool CanBuild { get; }
    void Build(...);
    bool CanFarm { get; }
    void Farm(...);
    ... etc.
}

次に:

class Worker : INPC
{
    private readonly IBuildStrategy buildStrategy;
    public Worker(IBuildStrategy buildStrategy)
    {
        this.buildStrategy = buildStrategy;
    }

    public bool CanBuild { get { return true; } }
    public void Build(...)
    {
        this.buildStrategy.Build(...);
    }
}

...またはもちろん、さまざまなタイプのNPCなどを定義するための流fluentなインターフェイスの形式のドメイン固有言語など、さまざまな動作を組み合わせてNPCタイプを構築する、さまざまなアーキテクチャを使用できます。

var workerType = NPC.Builder
    .Builds(new WorkerBuildBehavior())
    .Farms(new WorkerFarmBehavior())
    .Build();

var workerFactory = new NPCFactory(workerType);

var worker = workerFactory.Build();

NPCビルダーはDefaultWalkBehavior、飛行しても歩行できないNPCの場合にオーバーライドされる可能性のあるを実装できます。


さて、もう一度大声で考えてみてください。実際、if(npc is ICanBuild)との間に大きな違いはありませんif(npc.CanBuild)npc.CanBuild私の現在の態度からの方法を好む場合でも。
Brettetete

3
@Brettetete- この質問で、一部のプログラマーが型の検査を本当に嫌う理由を見ることができます。
スコットホイットロック14

0

私はすべての能力を、能力から継承する別々のクラスに入れました。次に、ユニットタイプクラスには、能力のリストと攻撃、防御、最大ヘルスなどのその他のものがあります。また、現在のヘルス、場所などを持ち、メンバー変数としてユニットタイプを持つユニットクラスがあります。

ハードコーディングするのではなく、設定ファイルまたはデータベースですべてのユニットタイプを定義します。

次に、レベルデザイナーはゲームを再コンパイルせずにユニットタイプの能力と統計を簡単に調整できます。また、ゲームのmodを作成することもできます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.