보드 게임 모델링을위한 패턴이 있습니까? [닫은]


93

재미로 나는 아들이 가장 좋아하는 보드 게임 중 하나를 소프트웨어로 작성하려고합니다. 결국 나는 그 위에 WPF UI를 구축 할 것으로 예상하지만 지금은 게임과 규칙을 모델링하는 시스템을 구축하고 있습니다.

이렇게하면서 나는 많은 보드 게임에 공통적이라고 생각하는 문제를 계속보고 있으며 아마도 다른 사람들이 이미 내가 생각하는 것보다 더 잘 해결했을 것입니다.

(게임을하는 AI와 고성능 패턴은 흥미롭지 않습니다.)

지금까지 내 패턴은 다음과 같습니다.

  • 주사위, 체커, 카드, 보드, 보드의 공간, 돈 등과 같이 게임 상자의 엔티티를 나타내는 몇 가지 불변 유형.

  • 플레이어 리소스 (예 : 돈, 점수), 이름 등을 포함하는 각 플레이어의 개체입니다.

  • 게임의 상태를 나타내는 객체 : 플레이어, 차례가 된 사람, 보드에있는 조각의 레이아웃 등.

  • 턴 시퀀스를 관리하는 상태 머신. 예를 들어, 많은 게임에는 각 플레이어가 누가 먼저하는지보기 위해 굴리는 작은 사전 게임이 있습니다. 그것이 시작 상태입니다. 플레이어의 차례가 시작되면 먼저 굴리다가 움직이고 제자리에서 춤을 추고 다른 플레이어가 자신이 어떤 종류의 닭인지 추측 한 다음 점수를받습니다.

내가 활용할 수있는 선행 기술이 있습니까?

편집 : 최근에 깨달은 한 가지는 게임 상태를 두 가지 범주로 나눌 수 있다는 것입니다.

  • 게임 아티팩트 상태 . "나는 10 달러가 있습니다"또는 "내 왼손이 파란색입니다."

  • 게임 시퀀스 상태 . "나는 복식을 두 번 굴렸다. 다음 선수는 나를 감옥에 넣는다." 여기서 상태 머신이 의미가있을 수 있습니다.

편집 : 내가 여기서 정말로 찾고있는 것은 체스, 스크래블 또는 모노 폴리와 같은 멀티 플레이어 턴 기반 게임을 구현 하는 가장 좋은 방법입니다. 처음부터 끝까지 작업하는 것만으로도 그런 게임을 만들 수 있다고 확신하지만, 다른 디자인 패턴과 마찬가지로 신중한 연구 없이는 분명하지 않은 일을 훨씬 더 원활하게 진행할 수있는 몇 가지 방법이있을 것입니다. 그게 제가 바라는 것입니다.


3
일종의 Hokey Pokey, Monopoly, charades mashup을 만들고 있습니까?
Anthony Mastrean

Monopoly에 대한 세 개의 복식 규칙과 같이 상태 (err ...)에 의존하는 모든 규칙에 대한 상태 머신이 필요합니다. 나는 더 완전한 답변을 게시하고 싶지만 이것을 한 경험이 없습니다. 나는 그것에 대해 숭고 할 수 있었다.
MSN

답변:


115

이것은 내가 방금 눈치 채는 2 개월 된 스레드 인 것 같지만 도대체. 저는 이전에 상용 네트워크 보드 게임을위한 게임 플레이 프레임 워크를 설계하고 개발했습니다. 우리는 그것으로 작업하는 매우 즐거운 경험을했습니다.

플레이어 A가 얼마나 많은 돈을 가지고 있는지, 플레이어 B가 얼마나 많은 돈을 가지고 있는지 등과 같은 것들의 순열 때문에 당신의 게임은 아마도 무한한 양의 상태에있을 수 있습니다. 상태 머신에서 멀리 떨어져 있습니다.

우리 프레임 워크의 아이디어는 전체 게임 상태를 제공하는 모든 데이터 필드가있는 구조로 게임 상태를 표현하는 것이 었습니다 (예 : 게임을 디스크에 저장하려면 해당 구조를 작성합니다).

우리 는 플레이어가 할 수있는 모든 유효한 게임 동작을 나타 내기 위해 명령 패턴 을 사용했습니다 . 다음은 작업의 예입니다.

class RollDice : public Action
{
  public:
  RollDice(int player);

  virtual void Apply(GameState& gameState) const; // Apply the action to the gamestate, modifying the gamestate
  virtual bool IsLegal(const GameState& gameState) const; // Returns true if this is a legal action
};

따라서 이동이 유효한지 결정하기 위해 해당 동작을 구성한 다음 IsLegal 함수를 호출하여 현재 게임 상태를 전달할 수 있습니다. 유효하고 플레이어가 액션을 확인하면 Apply 함수를 호출하여 실제로 게임 상태를 수정할 수 있습니다. 게임 플레이 코드가 법적 조치를 생성하고 제출하여 게임 상태 만 수정할 수 있도록함으로써 (즉, Action :: Apply 메서드 제품군이 게임 상태를 직접 수정하는 유일한 것임), 게임이 상태는 유효하지 않습니다. 또한 명령 패턴을 사용하여 플레이어가 원하는 동작을 직렬화하고 네트워크를 통해 전송하여 다른 플레이어의 게임 상태에서 실행되도록 할 수 있습니다.

이 시스템은 상당히 우아한 솔루션으로 판명되었습니다. 때로는 작업에 두 개 이상의 단계가 있습니다. 예를 들어, 플레이어는 Monopoly의 부동산에 착륙 할 수 있으며 이제 새로운 결정을 내려야합니다. 플레이어가 주사위를 굴 렸을 때와 부동산을 구매하기로 결정하기 전 사이의 게임 상태는 무엇입니까? 우리는 게임 상태의 "Action Context"멤버를 특징으로하여 이와 같은 상황을 관리했습니다. 액션 컨텍스트는 일반적으로 null이며 게임이 현재 특별한 상태가 아님을 나타냅니다. 플레이어가 주사위를 굴리고 주사위 굴리 동작이 게임 상태에 적용되면 플레이어가 소유하지 않은 속성에 착륙했음을 인식하고 새 "PlayerDecideToPurchaseProperty"를 만들 수 있습니다. 결정을 기다리고있는 플레이어의 인덱스를 포함하는 액션 컨텍스트. RollDice 액션이 완료 될 때까지 게임 상태는 현재 지정된 플레이어가 속성을 구매할지 여부를 결정하기를 기다리고 있음을 나타냅니다. 이제 게임 상태에 "PlayerDecideToPurchaseProperty"작업 컨텍스트가있을 때만 합법적 인 "BuyProperty"및 "PassPropertyPurchaseOpportunity"작업을 제외하고 다른 모든 작업의 ​​IsLegal 메서드가 false를 쉽게 반환 할 수 있습니다.

액션 컨텍스트의 사용을 통해, 게임 상태 구조가 해당 시점에서 게임에서 일어나는 일을 정확히 표현하지 못하는 보드 게임 수명의 단일 지점은 없습니다. 이것은 보드 게임 시스템의 매우 바람직한 속성입니다. 하나의 구조 만 검사하여 게임에서 일어나는 일에 대해 알고 싶은 모든 것을 찾을 수있을 때 코드를 작성하는 것이 훨씬 더 쉬울 것입니다.

또한 클라이언트가 네트워크를 통해 자신의 작업을 호스트 시스템에 제출하여 호스트의 "공식적인"게임 상태에 작업을 적용한 다음 해당 작업을 다른 모든 클라이언트에 다시 에코 할 수있는 네트워크 환경으로 매우 잘 확장됩니다. 복제 된 게임 상태에 적용하도록합니다.

간결하고 도움이 되었기를 바랍니다.


4
간결하다고 생각하지 않지만 도움이됩니다! 찬성.
Jay Bazuzi

도움이되어 기쁩니다 ... 어떤 부분이 간결하지 않습니까? 명확하게 편집 해 드리겠습니다.
Andrew Top

지금 턴제 게임을 만들고 있는데이 게시물이 정말 도움이되었습니다!
Kiv

나는 기념물이 취소에 사용하는 패턴이 ... 메멘토 실행 취소 명령 패턴 대, 당신의 생각을 plz .. 것을 읽기
zotherstupidguy

이것은 지금까지 Stackoverflow에서 읽은 가장 좋은 답변입니다. 감사!
Papipo

18

게임 엔진의 기본 구조는 State Pattern을 사용합니다 . 게임 상자의 아이템은 다양한 클래스의 싱글 톤 입니다. 각 상태의 구조는 전략 패턴 또는 템플릿 방법을 사용할 수 있습니다 .

공장 플레이어, 또 다른 싱글의 목록에 삽입하는 플레이어를 만드는 데 사용됩니다. GUI는 Observer 패턴 을 사용하여 게임 엔진을 계속 감시 하고 Command Pattern을 사용하여 생성 된 여러 Command 개체 중 하나를 사용하여 이와 상호 작용 합니다. Observer 및 Command의 사용은 Passive View 의 컨텍스트에서 사용할 수 있지만 사용자의 선호도에 따라 거의 모든 MVP / MVC 패턴을 사용할 수 있습니다. 게임을 저장할 때 현재 상태에 대한 메모가져와야 합니다.

사이트 의 일부 패턴을 살펴보고 그중 하나가 당신을 출발점으로 삼는 지 확인하는 것이 좋습니다 . 다시 말하지만 게임 보드의 핵심은 상태 머신이 될 것입니다. 대부분의 게임은 게임 전 / 설정 및 실제 게임의 두 가지 상태로 표시됩니다. 그러나 모델링하는 게임에 여러 가지 고유 한 플레이 모드가있는 경우 더 많은 상태를 가질 수 있습니다. 예를 들어, 전쟁 게임 Axis & Battles에는 플레이어가 전투를 해결하는 데 사용할 수있는 전투 판이 있습니다. 따라서 게임 전, 메인 보드, 배틀 보드의 세 가지 상태가 있으며 게임은 메인 보드와 배틀 보드 사이를 지속적으로 전환합니다. 물론 턴 시퀀스는 상태 머신으로도 표현할 수 있습니다.


15

다형성을 사용하여 상태 기반 게임을 디자인하고 구현하는 작업을 방금 완료했습니다.

GamePhase하나의 중요한 메서드가 있는 기본 추상 클래스 사용

abstract public GamePhase turn();

이것이 의미하는 것은 모든 GamePhase객체가 게임의 현재 상태를 보유하고 있으며 현재 상태를 turn()보고 다음을 반환 하는 호출 GamePhase입니다.

각 콘크리트 GamePhase에는 전체 게임 상태 를 보유하는 생성자가 있습니다 . 각 turn()방법에는 내부에 약간의 게임 규칙이 있습니다. 이것은 규칙을 퍼뜨리는 동안 관련 규칙을 밀접하게 유지합니다. 각각의 최종 결과 turn()는 다음을 생성 GamePhase하고 전체 상태를 다음 단계로 전달하는 것입니다.

이것은 turn()매우 유연하게 할 수 있습니다. 게임에 따라 주어진 상태는 여러 다른 유형의 단계로 분기 될 수 있습니다. 이것은 모든 게임 단계의 그래프를 형성합니다.

최고 수준에서이를 구동하는 코드는 매우 간단합니다.

GamePhase state = ...initial phase
while(true) {
    // read the state, do some ui work
    state = state.turn();
}

이제 테스트를 위해 게임의 모든 상태 / 단계를 쉽게 만들 수 있으므로 매우 유용합니다.

이제 질문의 두 번째 부분에 답하기 위해 멀티 플레이어에서 어떻게 작동합니까? 특정 내에서 GamePhase사용자의 입력을 요청들로부터의 호출은 turn()현재 물어 보곤 Player자신의 Strategy주어진 현재 상태 / 상. Strategy가능한 모든 결정의 인터페이스 일뿐 Player입니다. 이 설정은 또한 StrategyAI로 구현할 수 있습니다!

또한 Andrew Top은 다음과 같이 말했습니다.

플레이어 A가 얼마나 많은 돈을 가지고 있는지, 플레이어 B가 얼마나 많은 돈을 가지고 있는지 등과 같은 것들의 순열 때문에 당신의 게임은 아마도 무한한 양의 상태에있을 수 있습니다. 상태 머신에서 멀리 떨어져 있습니다.

나는 그 진술이 매우 오해의 소지가 있다고 생각하지만, 많은 게임 상태가 있다는 것은 사실이지만 게임 단계는 몇 개 밖에 없습니다. 그의 예제를 처리하기 위해 내 콘크리트 생성자에 대한 정수 매개 변수 만 있으면 GamePhase됩니다.

전매권

일부 GamePhase의 예 는 다음과 같습니다.

  • GameStarts
  • PlayerRolls
  • PlayerLandsOnProperty (FreeParking, GoToJail, Go 등)
  • PlayerTrades
  • PlayerPurchasesProperty
  • PlayerPurchasesHouses
  • PlayerPurchasesHotels
  • PlayerPaysRent
  • PlayerBankrupts
  • (모든 기회 및 커뮤니티 상자 카드)

그리고 기지의 일부 상태는 다음 GamePhase과 같습니다.

  • 선수 목록
  • 현재 플레이어 (누가 차례)
  • 플레이어의 돈 / 재산
  • 부동산의 주택 / 호텔
  • 선수 위치

그런 다음 일부 단계는 필요에 따라 자체 상태를 기록합니다. 예를 들어 PlayerRolls는 플레이어가 연속 복식을 굴린 횟수를 기록합니다. PlayerRolls 단계에서 나가면 더 이상 연속적인 롤에 대해 신경 쓰지 않습니다.

많은 단계를 재사용하고 함께 연결할 수 있습니다. 예를 들어는 현재 상태로 GamePhase CommunityChestAdvanceToGo다음 단계 PlayerLandsOnGo를 만들고 반환합니다. PlayerLandsOnGo현재 플레이어 의 생성자 에서 Go로 이동하고 그들의 돈은 $ 200 씩 증가합니다.


8

물론이 주제에 대한 많은, 많은, 많은, 많은, 많은, 많은, 많은 자원이 있습니다. 그러나 나는 당신이 올바른 길을 가고 있다고 생각합니다.

바둑판 식 기반 보드 게임을 할 때 보드 배열과 행 / 열 사이를 매핑하는 루틴과 다른 기능을 함께 사용하는 것이 좋습니다. boardarray 5에서 row / col을 얻는 방법에 어려움을 겪었을 때 (오래 전에) 첫 번째 보드 게임을 기억합니다.

1  2  3  
4 (5) 6  BoardArray 5 = row 2, col 2
7  8  9  

향수. ;)

어쨌든 http://www.gamedev.net/ 은 정보를 얻기에 좋은 곳입니다. http://www.gamedev.net/reference/


2 차원 배열을 사용하지 않는 이유는 무엇입니까? 그러면 컴파일러가이를 처리 할 수 ​​있습니다.
Jay Bazuzi

제 변명은 이것이 오래 전 일이라는 것입니다. ;)
Stefan

1
gamedev는 엄청난 양의 물건을 가지고 있지만 내가 찾고있는 것을 보지 못했습니다.
Jay Bazuzi

어떤 언어를 사용 했습니까?
zotherstupidguy

Basic, Basica, QB, QuickBasic 등. ;)
Stefan


3

Three Rings 는 LGPL의 Java 라이브러리를 제공합니다. Nenya와 Vilya는 게임 관련 라이브러리입니다.

물론 질문에 플랫폼 및 / 또는 언어 제한이 언급 된 경우 도움이 될 것입니다.


"결국 WPF UI를 구축 할 예정입니다."-이는 .NET을 의미합니다. 적어도 내가 말할 수있는 한.
Mark Allen

내가 모르는 알파벳 수프.
jmucchiello

예, .NET을 사용하고 있지만 제 질문은 언어 또는 플랫폼별로 다릅니다.
Jay Bazuzi

2

나는 Pyrolistical의 대답에 동의하고 그의 일을하는 방식을 선호합니다 (나는 다른 대답을 훑어 보았습니다).

우연히도 그의 "GamePhase"이름도 사용했습니다. 기본적으로 턴 기반 보드 게임의 경우 Pyrolistical에서 언급 한 것처럼 GameState 클래스에 추상 GamePhase의 개체가 포함되도록하는 것입니다.

게임 상태는 다음과 같습니다.

  1. 움직임
  2. 구매 / 구매 안함
  3. 교도소

각 상태에 대한 구체적인 파생 클래스를 가질 수 있습니다. 최소한 다음을위한 가상 기능이 있어야합니다.

StartPhase();
EndPhase();
Action();

StartPhase () 함수에서 상태에 대한 모든 초기 값을 설정할 수 있습니다 (예 : 다른 플레이어의 입력 비활성화 등).

roll.EndPhase ()가 호출되면 GamePhase 포인터가 다음 상태로 설정되었는지 확인합니다.

phase = new MovePhase();
phase.StartPhase();

이 MovePhase :: StartPhase ()에서 예를 들어 활성 플레이어의 남은 움직임을 이전 단계에서 굴린 양으로 설정합니다.

이제이 디자인을 사용하면 Roll 단계에서 "3 x double = jail"문제를 해결할 수 있습니다. RollPhase 클래스는 자체 상태를 처리 할 수 ​​있습니다. 예를 들면

GameState state; //Set in constructor.
Die die;         // Only relevant to the roll phase.
int doublesRemainingBeforeJail;
StartPhase()
{
    die = new Die();
    doublesRemainingBeforeJail = 3;
}

Action()
{
    if(doublesRemainingBeforeJail<=0)
    {
       state.phase = new JailPhase(); // JailPhase::StartPhase(){set moves to 0};            
       state.phase.StartPhase();
       return;
    }

    int die1 = die.Roll();
    int die2 = die.Roll();

    if(die1 == die2)
    {
       --doublesRemainingBeforeJail;
       state.activePlayer.AddMovesRemaining(die1 + die2);
       Action(); //Roll again.
    }

    state.activePlayer.AddMovesRemaining(die1 + die2);
    this.EndPhase(); // Continue to moving phase. Player has X moves remaining.
}

나는 플레이어가 커뮤니티 상자에 착륙 할 때를 포함하여 모든 것에 대한 단계가 있어야한다는 점에서 Pyrolistical과 다릅니다. 이 모든 것을 MovePhase에서 처리합니다. 순차 단계가 너무 많으면 플레이어가 너무 "안내"된 느낌을받을 가능성이 높기 때문입니다. 예를 들어 플레이어가 부동산 만 구매하고 호텔 만 구매 한 다음 집만 구매할 수있는 단계가 있다면 자유가없는 것과 같습니다. 모든 부품을 하나의 BuyPhase로 묶어 플레이어가 원하는 것을 구매할 수있는 자유를 제공하십시오. BuyPhase 클래스는 합법적 인 구매를 쉽게 처리 할 수 ​​있습니다.

마지막으로 게임 보드를 다루겠습니다. 2D 배열이 좋지만 타일 그래프 (타일이 보드의 위치)를 갖는 것이 좋습니다. 독점의 경우 이중으로 연결된 목록이 될 것입니다. 그런 다음 모든 타일에는 다음이 있습니다.

  1. previousTile
  2. nextTile

따라서 다음과 같이하는 것이 훨씬 쉬울 것입니다.

While(movesRemaining>0)
  AdvanceTo(currentTile.nextTile);

AdvanceTo 함수는 단계별 애니메이션 또는 원하는 모든 것을 처리 할 수 ​​있습니다. 물론 남은 움직임도 줄입니다.

GUI에 대한 관찰자 패턴에 대한 RS Conley의 조언은 좋습니다.

나는 전에 많이 게시하지 않았습니다. 이것이 누군가를 돕기를 바랍니다.


1

내가 활용할 수있는 선행 기술이 있습니까?

질문이 언어 또는 플랫폼별로 다르지 않은 경우. 그런 다음 State, Memento, Command 등에 대한 AOP 패턴을 고려하는 것이 좋습니다.

AOP에 대한 .NET 답변은 무엇입니까 ???

또한 http://www.chessbin.com 과 같은 멋진 웹 사이트를 찾아보십시오 .

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