2012-04-08 3 views
0

RTS 게임을 작성 중이며 사용자 작업을 "명령"으로 캡슐화하고 싶습니다. 명령을 허용합니다 :RTS 게임용 명령 시스템

  • 다른 플레이어에게 네트워크를 통해 사용자의 동작을 전송, 그래서 모든이 다른 실행을 방지하기 전에 각 동작을 검증하는 재생 기능
  • 수 있도록 파일을
  • 기록 사용자 작업이 무엇을하고 있는지 알고있다 예를 들어 UI 변경 (카메라 이동), 게임 상태 변경 (유닛 이동),지도 편집기 만 발행 할 수있는 동작 (예 : 변경) 등의 동작을 분류하는 행위 지형) 등

또한 최소한의 오버 헤드를 추가해야합니다. 이 때문에 반사 사용을 피하고 싶습니다. 필자가 필자가 거대한 switch 문으로 끝내기 때문에 지금까지 내가 얻은 최선은 나빴다.

enum CommandType { 
    MoveCamera, 
    ChangeTerrain 
} 

// Base class for all commands (public fields used for brevity) 
abstract class Command { 
    protected Command(CommandType type) { 
     Type = type; 
    } 
    public CommandType Type; 
} 

class CommandMoveCamera : Command { 
    public CommandMoveCamera() : base(CommandType.MoveCamera) {} 
    public int DeltaX; 
    public int DeltaY; 
} 

class CommandChangeTerrain : Command { 
    public CommandChangeTerrain() : base(CommandType.ChangeTerrain) {} 
    public int NewTerrainType; 
    public int X; 
    public int Y; 
} 

class Game { 
    Queue<Command> m_commands; 
    void Update() { 
     // Example of adding a command 
     m_commands.Enqueue(new CommandMoveCamera { DeltaX = -10, DeltaY = 0 }); 

     // Processing commands 
     while (m_commands.Count > 0) { 
      var c = m_commands.Dequeue(); 
      switch(c.Type) { 
      case CommandType.MoveCamera: 
       var command = (CommandMoveCamera)c; 
       MoveCamera(c.DeltaX, c.DeltaY); 
       break; 
      case CommandType.ChangeTerrain: 
       var command = (CommandChangeTerrain)c; 
       ChangeTerrain(c.X, c.Y, c.NewTerrainType); 
       break; 
      } 
     } 
    } 
} 

언어는 C#입니다. 내 질문은 : 요구 사항을 충족시키는 방법으로이 시스템을 구현하고 커다란 스위치 명령문을 사용하여 각기 다른 유형의 명령을 분기하지 않도록하는 방법은 무엇일까요?

감사합니다.

+1

코드에서 다른 눈 모음을 찾고 있다면 [codereview] (http://codereview.stackexchange.com)는 아마도 당신의 질문에 더 좋은 집일 것입니다. 그렇지 않으면 더 많은 것을해야 할 것입니다 게시 된 코드에 대한 문제와 관련하여 –

+0

코드 검토를 요청하는 것이 아닙니다. 코드는 내가 성취하려는 것을 설명하기위한 것입니다. 내가 찾고있는 것은 내가 말한 다양한 요구 사항을 충족시킬 수있는 동안 거대한 switch 문을 피하는 방식으로이 시스템을 설계하는 방법이다. – Asik

+1

그것은 SO의 의도는 아닙니다. 당신이 당신의 시스템을 설계 할 사람이 필요하다면 나는 당신의 돈을 가지고 싶어하는 많은 일하는 소프트웨어 디자이너가있을 것이라고 확신한다. 진지하게, 당신의 질문은 아마도 게임 사이트 나 다른 포럼을 시도해 볼만한 것은 아닙니다. –

답변

0

몇 가지 아이디어가 있습니다. 먼저, 각 명령 유형에 대해 On<Event> 메소드를 노출 할 수 있습니다. 다음 Game 인 에 Execute과 같은 추상 메소드를 추가하십시오. 각 명령 구현은 Game (명령과 관련된 다른 논리 포함)에서 적절한 메소드를 호출 할 수 있습니다. 열거 형이 더 이상 필요하지 않습니다.

다른 아이디어는 다소 유사하지만 대리인을 사용합니다. 각 명령 유형에 대한 위임을 정의하고 각 Command 구현에 대해이 위임을 사용하는 이벤트를 노출합니다. 그런 다음 Command 초록에서 Execute 메서드를 사용할 수 있습니다.이 메서드는 구현 될 때 적절한 이벤트를 발생시킵니다. 각 이벤트를 만들 때 해당 명령을 처리 할 멤버에게 Game에 이벤트를 위임하기 만하면됩니다.

+1

나는 당신의 제안과 함께 갈 것입니다. 그것은 비슷한 방식으로 이중 디스패치 문제를 해결하는 Visitor 패턴을 생각 나게합니다. (누군가에게 당신에게 전화해서 과부하로 적절한 유형의 메소드를 호출하도록하십시오). 어쨌든 감사합니다! – Asik

0

얼마 전에 게임 엔진에서 매우 유사한 작업을 수행했습니다. 사실 몇 가지 다른 버전을 통해 갔고, 내가 생각해내는 버전은 이와 비슷한 것입니다.

  • 모든 명령은
  • 모든 명령은 CommandRegistry에 등록해야합니다, 그래서 CommandRegistry 각 명령에 대한 고유 ID가 (직렬화되는 것을 의미) 명령 인터페이스를 구현합니다.

그런 다음 유선을 통해 명령을 보내려면 CommandRegistry에 일련 번호를 요청하십시오. 명령과 명령을 직렬화하여 이드에게 전달합니다. 수신 측의 CommandRegistry는 ID를 읽고 해당 유형의 명령을 만든 다음 나머지 데이터에서 자체를 deserialize하도록 요청합니다.

나는이 기본 설정을 C++과 Go 모두에서 수행 했으므로 C#에서 제대로 작동해야한다고 생각합니다.

+0

내가 닮았지만, ID를 아는 적절한 유형의 명령을 어떻게 작성합니까? 지금까지 제가 발견 할 수있는 것은 막대한, 결국 유지가 불가능한 switch 선언문을 만드는 것입니다. C++에서는 템플릿과 함수 포인터를 사용할 수는 있었지만 C#에서이 코드가 어떻게 사용되는지는 알 수 없습니다. – Asik

+0

특정 부분을 수행하기 위해 언어 리플렉션을 사용 하겠지만 C#을 사용할 수 있는지 잘 알지 못합니다. 리플렉션 이외에 가장 쉬운 방법은 등록 할 때 적절한 유형을 생성하고 추상 클래스로 반환하는 함수 포인터를 전달하는 것입니다. 레지스트리는 Id에서 함수 포인터로의 매핑을 유지하므로 필요할 때마다 만들 수 있습니다. –

+0

Wild 실행 감사합니다. 그 두 가지에 대해서는 생각했지만 C# reflection은 내 요구 사항에 비해 너무 느리고 "함수 포인터"(델리게이트)는 강력하게 형식화되므로 기본 형식을 사용하지 않으면 다른 서명을 가진 델리게이트를 함께 저장할 수 없습니다 그것은 단지 문제를 되 돌린다). – Asik