2008-12-11 3 views
88

Для удовольствия я пытаюсь написать одну из любимых настольных игр моего сына как часть программного обеспечения. В конце концов, я ожидаю, что на нем будет создан пользовательский интерфейс WPF, но сейчас я строю машину, которая моделирует игры и свои правила.Любые шаблоны для моделирования настольных игр?

Как я это делаю, я все время вижу проблемы, которые, как мне кажется, являются общими для многих настольных игр, и, возможно, другие уже решили их лучше, чем я.

(. Обратите внимание, что AI играть в этой игре, и узоры вокруг высокой производительности не интересны мне)

До сих пор мои шаблоны:

  • Несколько неизменных типов, представляющих объекты в игре поле, например кости, шашки, карты, доска, пространства на борту, деньги и т.д.

  • Объект для каждого игрока, который содержит игроков ресурсы (например, деньги, оценка), их имена и т.д.

  • Объект, который представляет состояние игры: игроки, кто его поворачивает, макет пек на доске и т. Д.

  • Конечный автомат, управляющий чередой. Например, во многих играх есть небольшая пред-игра, где каждый игрок бросается посмотреть, кто идет первым; это начальное состояние. Когда начинается поворот игрока, сначала они катятся, затем двигаются, затем им приходится танцевать на месте, а затем другие игроки угадывают, какая у них курица, затем они получают очки.

Есть ли некоторые из предшествующих уровней техники, которыми я могу воспользоваться?

EDIT: Одна вещь, которую я понял, в последнее время является то, что состояние игры можно разделить на две категории:

  • игры артефакт состояние. «У меня 10 долларов», или «моя левая рука на синем».

  • Состояние последовательности воспроизведения игр. «Я дважды удвоился, а следующий посадил меня в тюрьму». Здесь может иметь смысл конечный автомат.

EDIT: Что я действительно ищет здесь является лучший способ реализации многопользовательских пошаговых игр, таких как шахматы или Эрудит или Монополии. Я уверен, что смогу создать такую ​​игру, просто работая над ней, и начнет ее завершать, но, как и другие шаблоны проектирования, возможно, есть некоторые способы сделать вещи более плавными, которые не очевидны без тщательного изучения. Это то, на что я надеюсь.

+2

Вы строите своего рода Hokey Pokey, Monopoly, шарады mashup? – 2008-12-11 21:29:15

+0

Вам понадобится машина состояний для любого правила, которое полагается на состояние (err ...), как правило трех двойников для монополии. Я бы опубликовал более полный ответ, но у меня нет опыта в этом. Хотя я мог бы понтификатировать об этом. – MSN 2009-02-19 17:51:26

ответ

107

Кажется, это 2-месячная тема, которую я только что заметил сейчас, но что это такое. Я разработал и разработал структуру игрового процесса для коммерческой, сетевой настольной игры. У нас был очень приятный опыт работы с ним.

Возможно, ваша игра может находиться в (близком к) бесконечном количестве состояний из-за перестановок таких вещей, как, сколько денег у игрока A, сколько денег у игрока B и т. Д. Поэтому я довольно уверен, что вы хотите держаться подальше от государственных машин.

Идея нашей структуры заключалась в представлении состояния игры как структуры со всеми полями данных, которые вместе обеспечивают полное состояние игры (то есть: если вы хотите сохранить игру на диск, вы напишите эту структуру).

Мы использовали Command Pattern для представления всех действительных действий, которые игрок мог сделать. Вот бы пример действия:

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 - это единственное, что непосредственно изменяет состояние игры), то вы гарантируете, что ваша игра состояние никогда не будет недопустимым. Кроме того, используя шаблон команды, вы позволяете сериализовать требуемые ходы вашего плеера и отправлять их по сети, которая будет выполняться в игровых состояниях другого игрока.

В результате оказалось, что у этой системы появилось довольно элегантное решение. Иногда действия имели бы две или более фазы. Например, игрок может приземлиться на собственность в Монополии и должен теперь принять новое решение. Каково состояние игры между тем, когда игрок свернул кости, и прежде чем они решат приобрести недвижимость или нет? Мы управляли ситуациями, подобными этому, показывая участника Action Context нашего игрового состояния. Контекст действия обычно будет нулевым, что указывает на то, что игра в настоящее время не находится в каком-либо специальном состоянии. Когда игрок бросает кубики, и действие качки в кости применяется к состоянию игры, он поймет, что игрок приземлился на принадлежащее ему имущество и может создать новый контекст действия PlayerDecideToPurchaseProperty, который содержит индекс игрока мы ждем решения от. К моменту завершения действия RollDice наше состояние игры означает, что он в настоящее время ждет, пока указанный игрок не решит, покупать ли недвижимость. Теперь для всех других действий метод IsLegal легко вернуть false, за исключением действий «BuyProperty» и «PassPropertyPurchaseOpportunity», которые являются законными только в том случае, если в игровом состоянии используется контекст действия PlayerDecideToPurchaseProperty.

С помощью контекстов действий никогда не бывает ни одной точки в жизни игровой настольной игры, где государственная структура игры не полностью отражает ТОЧНО, что происходит в игре в этот момент времени. Это очень желательное свойство вашей настольной игровой системы. Вам будет намного проще писать код, когда вы сможете найти все, что захотите узнать о том, что происходит в игре, исследуя только одну структуру.

Кроме того, он очень хорошо распространяется на сетевые среды, где клиенты могут отправлять свои действия по сети хост-машине, которая может применить действие к «официальному» состоянию игры хоста, а затем повторить это действие ко всем другим клиентам, чтобы они применяли его к своим реплицированным состояниям игры.

Надеюсь, это было кратким и полезным.

3

Three Rings предлагает библиотеки LGPL'd Java. Ненья и Виля - это библиотеки для игр.

Конечно, это помогло бы, если бы на вашем вопрос были упомянуты ограничения на платформу и/или язык, которые у вас могут быть.

+0

«В конечном счете, я ожидаю, что вы создадите интерфейс WPF» - это значит.СЕТЬ. По крайней мере, насколько я могу судить. – 2008-12-11 21:53:13

+0

Алфавит суп Я не знаю. – jmucchiello 2008-12-11 22:04:01

8

Конечно, есть много, множество, многих, многих, многих, многих, многих ресурсов по этой теме. Но я думаю, что вы находитесь на правильном пути, разделяя объекты и позволяете им обрабатывать свои собственные события/данные и так далее.

При создании настольных игр на плитке вам будет удобно иметь подпрограммы для сопоставления между массивом досок и столбцом/столбцом и обратно по другим функциям. Я помню свою первую настольную игру (давным-давно), когда я struggeled с тем, как получить строку/седловины от boardarray 5.

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

Ностальгия. ;)

В любом случае, http://www.gamedev.net/ - это хорошее место для информации. http://www.gamedev.net/reference/

17

Основная структура вашего игрового движка использует State Pattern. Элементы вашего игрового поля - singletons различных классов. Структура каждого государства может использовать Strategy Pattern или the Template Method.

A Factory Используется для создания игроков, которые вставляются в список игроков, еще один синглтон. GUI будет следить за Game Engine с помощью Observer pattern и взаимодействовать с ним, используя один из нескольких объектов Command, созданных с использованием Command Pattern. Использование Observer и Command может использоваться в контексте Passive View. Но любой шаблон MVP/MVC можно использовать в зависимости от ваших предпочтений. Когда вы сохраняете игру, вам необходимо захватить memento его текущего состояния.

Я рекомендую просмотреть некоторые шаблоны на этом site и посмотреть, хватит ли кто-нибудь из вас в качестве отправной точки. Снова сердце вашей игровой доски будет государственной машиной. Большинство игр будут представлены в двух штатах: перед игрой/настройкой и реальной игрой. Но вы можете иметь больше состояний, если игра, которую вы моделируете, имеет несколько различных режимов игры. Штатам не обязательно быть последовательными, например, военная игра Axis & Сражения имеют боевую доску, которую игроки могут использовать для разрешения сражений. Таким образом, есть три состояния перед игрой, основная доска, борт с игрой, постоянно переключающейся между основной доской и бортовой доской. Конечно, последовательность поворота также может быть представлена ​​конечным автоматом.

5

Большая часть материалов, которые я могу найти в Интернете, - это списки опубликованных ссылок. Раздел публикаций Game Design Patterns имеет ссылки на версии PDF статей и тезисов. Многие из них выглядят как академические документы, такие как Design Patterns for Games. Существует также, по крайней мере, одна книга, доступная от Amazon, Patterns in Game Design.

13

Я только что закончил разработку и реализацию государственной игры с использованием полиморфизма.

Используя базовый абстрактный класс под названием 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. Эта настройка также позволяет реализовать Strategy с AI!

Также Andrew Top сказала:

Ваша игра, вероятно, может быть в (около) бесконечного количества состояний из-за перестановки вещей, как, сколько денег у игрока А, сколько денег игрока B имеет, и т. д. Поэтому я уверен, что вы хотите держаться подальше от государственных машин.

Я думаю, что это утверждение очень вводит в заблуждение, хотя верно, что существует множество разных состояний игры, есть только несколько игровых фаз. Чтобы справиться с его примером, все это будет целочисленным параметром для конструкторов моего конкретного GamePhase.

Монополия

Пример некоторых GamePhase с будет:

  • GameStarts
  • PlayerRolls
  • PlayerLandsOnProperty (FreeParking, GoToJail, Go, и т.д.)
  • PlayerTrades
  • PlayerPurchasesProperty
  • PlayerPurchasesHouses
  • PlayerPurchasesHotels
  • PlayerPaysRent
  • PlayerBankrupts
  • (Все Chance и Community Chest карты)

И некоторые штаты в базе GamePhase являются:

  • Список игроков
  • Текущий Игрок (который включить)
  • игрока Деньги/Недвижимость
  • Дома/Отели на недвижимость
  • Игрок Позиция

И тогда некоторые фазы запишет свое собственное государство по мере необходимости, например PlayerRolls запишут количество раз, когда игрок имеет несколько удвоений. Как только мы покинем фазу PlayerRolls, нам больше не нужны последовательные ролики.

Много фаз можно повторно использовать и связать вместе. Например, GamePhaseCommunityChestAdvanceToGo создаст следующий этап PlayerLandsOnGo с текущим состоянием и вернет его. В конструкторе PlayerLandsOnGo текущий игрок будет перемещен в Go, и их деньги будут увеличены на 200 долларов.

1

Есть ли некоторые из предшествующих уровней техники, которыми я могу воспользоваться?

Если ваш вопрос не зависит от языка или платформы. то я бы рекомендовал вам рассмотреть шаблоны АОП для государства, памяти, командования и т. д.

Что такое .NET для AOP ???

пытаются также найти некоторые интересные сайты, такие как http://www.chessbin.com

2

Я согласен с ответом Pyrolistical и я предпочитаю его способ делать вещи (я только обезжиренное другие ответы, хотя).

Кстати, я также использовал его именование «GamePhase». В основном, что я делал бы в случае пошаговой настольной игры, ваш класс GameState должен содержать объект абстрактного GamePhase, как упоминается Pyrolistical.

Допустим, игра государства:

  1. Ролл
  2. Move
  3. Покупка/Не покупайте
  4. Jail

Вы могли бы иметь конкретные производные классы для каждого состояния , Есть виртуальные функции, по крайней мере для:

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

В StartPhase() функции вы можете установить все начальные значения для состояния, например отключение входа другого игрока, и так далее.

Когда вызывается roll.EndPhase(), убедитесь, что указатель GamePhase установлен в следующее состояние.

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

В этом MovePhase :: StartPhase() вы бы, например, установить оставшиеся шаги активного игрока на сумму проката в предыдущей фазе.

Теперь с этой конструкцией вы можете решить проблему «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 может обрабатывать свой шаг, анимации шага или все, что вам нравится. А также, конечно, уменьшение остальных ходов.

Совет RS Conley по шаблону наблюдателя для графического интерфейса является хорошим.

Я раньше не писал. Надеюсь, это поможет кому-то.