내 아키텍처의 다중 상속에 대한 대안 (실시간 전략 게임의 NPC)?


11

코딩은 실제로 그렇게 어렵지 않습니다 . 어려운 부분은 이해하기 쉽고 이해하기 쉬운 코드를 작성하는 것입니다. 그래서 더 나은 개발자를 원하고 견고한 아키텍처를 만들고 싶습니다.

비디오 게임에서 NPC 를 위한 아키텍처를 만들고 싶습니다 . Starcraft, Age of Empires, Command & Conquers 등과 같은 실시간 전략 게임입니다. 그래서 저는 다른 종류의 NPC를 가질 것입니다. NPC 많은 능력이의 (방법)에 하나를 가질 수 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를 사용할 수 있는지를 검증 그 논리의 어떤 요구 IsBuilder, CanBuild.. 나는 내가 표현하고 싶은 분명한 생각합니다.
그러나 나는이 생각에별로 좋지 않습니다. 기능 이 너무 많은 부풀린 기본 클래스처럼 들립니다 .

C #을 프로그래밍 언어로 사용합니다. 따라서 다중 상속은 옵션이 아닙니다. 의미 : 같은 추가 기본 수업 Fisherman : Farmer, Attacker이 작동하지 않습니다.


3
나는 이것에 대한 해결책처럼 보이는이 예제를보고 있었다. en.wikipedia.org/wiki/Composition_over_inheritance
Shawn Mclean

답변:


14

구성 및 인터페이스 상속은 일반적인 다중 상속에 대한 일반적인 대안입니다.

위에서 설명한 "can"이라는 단어로 시작하는 모든 것은 ICanBuild또는 로 인터페이스처럼 표현할 수있는 기능입니다 ICanFarm. 필요한만큼 많은 인터페이스를 상속 할 수 있습니다.

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

    #region ICanFarm Implementation
    // Farm
    #endregion
}

클래스의 생성자를 통해 "일반적인"건물 및 농업 개체를 전달할 수 있습니다. 그 곳에서 작곡이 시작됩니다.


그냥 큰 소리로 생각하십시오 '관계'(내부적으로) 될 것은 대신 IS (근무 근무 빌더입니다 대신 빌더가 있습니다). 당신이 설명 한 예제 / 패턴은 내 문제를 해결하는 좋은 분리 된 방법처럼 말이되고 소리가납니다. 그러나 나는이 관계를 얻는 데 어려움을 겪고 있습니다.
Brettetete

3
이것은 Robert의 솔루션과 직교하지만 복잡한 부작용 네트워크를 만들지 않도록 컴포지션을 많이 사용하는 경우 불변성 (보통 평상시보다)을 선호해야합니다. 이상적으로는 빌드 / 팜 / 공격 구현이 다른 데이터를 건드리지 않고 데이터를 가져오고 뱉어냅니다.
Doval

1
에서 상속되었으므로 Worker는 여전히 빌더입니다 ICanBuild. 또한 객체 계층에서 빌드 도구가 두 번째로 중요합니다.
Robert Harvey

말이된다. 이 원칙을 시도해 보겠습니다. 친절하고 설명적인 답변에 감사드립니다. 정말 고맙습니다. 나는이 질문을 잠시 동안 열어
보겠다

4
@Brettetete 빌드, 농장, 공격, 사냥 등의 구현을 캐릭터가 가진 기술 이라고 생각하면 도움이 될 수 있습니다. BuildSkill, FarmSkill, AttackSkill 등. 그러면 구성이 훨씬 더 합리적입니다.
Eric King

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>();

구성 요소가 점점 복잡 해짐에 따라 항상 문제가 발생했습니다. 구성 요소의 복잡성을 낮게 유지하면 (하나의 책임) 해당 문제를 완화하는 데 도움이 될 수 있습니다.


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 유형을 구축하는 다양한 유형의 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- 이 질문 에서 일부 프로그래머가 실제로 유형 검사를 싫어하는 이유를 알 수 있습니다 .
Scott Whitlock

0

모든 능력을 능력에서 상속받은 별도의 클래스에 넣었습니다. 그런 다음 유닛 유형 클래스에는 능력, 공격, 방어, 최대 체력 등과 같은 다른 것들의 목록이 있습니다. 또한 현재 체력, 위치 등이 있고 유닛 유형이 멤버 변수 인 유닛 클래스가 있습니다.

하드 코딩이 아닌 구성 파일 또는 데이터베이스에 모든 단위 유형을 정의하십시오.

그러면 레벨 디자이너는 게임을 다시 컴파일하지 않고도 유닛 유형의 능력과 통계를 쉽게 조정할 수 있으며 사람들은 게임에 대한 모드를 작성할 수 있습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.