Будь-які шаблони для моделювання настільних ігор? [зачинено]


93

Для розваги я намагаюся написати одну з улюблених настільних ігор сина як програмне забезпечення. Зрештою, я розраховую на те, щоб побудувати поверх нього інтерфейс WPF, але зараз я будую машину, яка моделює ігри та їх правила.

Роблячи це, я постійно бачу проблеми, які, на мою думку, є спільними для багатьох настільних ігор, і, можливо, інші вже вирішили їх краще, ніж я.

(Зверніть увагу, що ШІ для гри, і схеми навколо високої продуктивності мені не цікаві.)

Поки що мої шаблони такі:

  • Кілька незмінних типів, що представляють сутності в ігровій коробці, наприклад, кістки, шашки, картки, дошка, місця на дошці, гроші тощо.

  • Об’єкт для кожного гравця, який містить ресурси гравців (наприклад, гроші, рахунок), їх ім’я тощо.

  • Об’єкт, що представляє стан гри: гравці, хто зараз, розклад макетів на дошці тощо.

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

Чи є якийсь рівень техніки, яким я можу скористатися?

EDIT: Одне, що я недавно зрозумів, - це те, що стан гри можна розділити на дві категорії:

  • Стан ігрового артефакту . "У мене 10 доларів" або "моя ліва рука синя".

  • Стан послідовності гри . "Я двічі кинув дублі; наступний садить мене до в'язниці". Тут може мати сенс державна машина.

РЕДАГУВАТИ: Те, що я насправді шукаю тут, - це найкращий спосіб реалізувати багатокористувацькі покрокові ігри, такі як Шахи, Ерудит або Монополія. Я впевнений, що міг би створити таку гру, просто опрацювавши її, починаючи до кінця, але, як і інші шаблони дизайну, ймовірно, є кілька способів зробити все набагато плавніше, що не очевидно без ретельного вивчення. Це те, на що я сподіваюся.


3
Ви будуєте якийсь Hokey Pokey, Monopoly, шаради?
Ентоні Мастреан,

Вам потрібен автомат стану для будь-якого правила, яке покладається на стан (помилка ...), як правило трьох подвійних для Monopoly. Я б опублікував більш повну відповідь, але у мене немає досвіду робити це. Хоча я міг би про це понтифікувати.
MSN

Відповіді:


115

здається, це 2-місячна нитка, яку я щойно помітив, але яка біса. Раніше я розробляв і розробляв основу ігрового процесу для комерційної, мережевої настільної гри. У нас був дуже приємний досвід роботи з ним.

Ваша гра, можливо, перебуває у (майже) нескінченній кількості станів через перестановки таких речей, як кількість грошей у гравця А, скільки грошей у гравця В та ін ... Тому я впевнений, що ви хочете триматися подалі від державних машин.

Ідея нашого фреймворку полягала в тому, щоб представити ігровий стан як структуру з усіма полями даних, які разом забезпечують повний ігровий стан (тобто: якщо ви хочете зберегти гру на диск, ви виписуєте цю структуру).

Ми використовували шаблон команди для представлення всіх дійсних ігрових дій, які міг зробити гравець. Ось приклад дії:

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, переходячи в поточному ігровому стані. Якщо він дійсний, і гравець підтверджує дію, ви можете викликати функцію Застосувати, щоб фактично змінити стан гри. Переконавшись, що ваш код гри може змінювати стан гри лише шляхом створення та подання юридичних дій (отже, іншими словами, сімейство методів Action :: Apply - це єдине, що безпосередньо змінює стан гри), тоді ви гарантуєте, що ваша гра штат ніколи не буде недійсним. Крім того, використовуючи шаблон команди, ви даєте можливість серіалізувати бажані ходи гравця та відправляти їх через мережу для виконання в ігрових станах іншого гравця.

Врешті-решт з цією системою з’явилася одна помилка, яка, як виявилося, мала досить елегантне рішення. Іноді дії мали б дві або більше фази. Наприклад, гравець може сісти на майно в Монополії, і тепер він повинен прийняти нове рішення. Який стан гри між тим, коли гравець кидав кубики, і до того, як вони вирішили придбати майно чи ні? Ми впорались із подібними ситуаціями, представивши учасника "Контексту дії" нашої ігрової держави. Зазвичай контекст дії буде нульовим, що вказує на те, що гра в даний час не перебуває в якомусь спеціальному стані. Коли гравець кидає кістки, і дія кидання кубика застосовується до стану гри, він зрозуміє, що гравець приземлився на власності, що не є власністю, і може створити нову "PlayerDecideToPurchaseProperty" контекст дії, що містить індекс гравця, від якого ми чекаємо рішення. На момент завершення дії RollDice наш ігровий стан представляє, що в даний час він чекає, коли вказаний гравець вирішить, чи купувати нерухомість, ні. Тепер метод IsLegal для всіх інших дій повертає false, за винятком дій "BuyProperty" та "PassPropertyPurchaseOpportunity", які є законними лише тоді, коли стан гри має контекст дії "PlayerDecideToPurchaseProperty".

Завдяки використанню контекстів дій, у житті настільної гри ніколи не виникає жодного моменту, коли структура стану гри не повністю відображає ТОЧНО те, що відбувається в грі на той момент часу. Це дуже бажана властивість вашої настільної ігрової системи. Вам буде набагато простіше писати код, коли ви зможете знайти все, що хочете знати про те, що відбувається в грі, вивчивши лише одну структуру.

Крім того, він дуже добре поширюється на мережеві середовища, де клієнти можуть надсилати свої дії через мережу на хост-машину, яка може застосувати дію до "офіційного" стану гри хосту, а потім повторити цю дію до всіх інших клієнтів, щоб нехай вони застосовують його до своїх відтворених ігрових станів.

Сподіваюся, це було стисло та корисно.


4
Я не думаю, що це стисло, але корисно! Проголосував.
Джей Базузі,

Радий, що це було корисно ... Які частини не були стислими? Я був би радий внести уточнення.
Andrew Top

Зараз я будую покрокову гру, і ця публікація була дуже корисною!
Kiv

Я читав, що Memento - це шаблон для скасування ... Memento проти командного шаблону для скасування, ваші думки plz ..
zotherstupidguy

Це найкраща відповідь, яку я досі читав у Stackoverflow. ДЯКУЮ!
Папіпо

18

Основна структура вашого ігрового механізму використовує державний шаблон . Елементи вашої ігрової коробки - це одинаки різних класів. Структура кожного штату може використовувати шаблон стратегії або метод шаблону .

Factory використовується для створення гравців , які вставляються в список гравців, інший одноплідних. Графічний інтерфейс буде стежити за Game Engine, використовуючи шаблон Observer, і взаємодіяти з цим, використовуючи один із декількох об'єктів Command, створених за допомогою Command Pattern . Використання Observer і Command можна використовувати в контексті пасивного перегляду, але майже будь-який шаблон 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може прийняти. Ця установка також дозволяє Strategyреалізовувати за допомогою AI!

Також Ендрю Топ сказав:

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

Я вважаю, що це твердження вводить в оману, хоча це правда, що існує багато різних ігрових станів, є лише кілька етапів гри. Щоб обробити його приклад, все, що це буде, є цілочисельним параметром для конструкторів мого конкретного GamePhases.

Монополія

Прикладом деяких GamePhases може бути:

  • GameStarts
  • PlayerRolls
  • PlayerLandsOnProperty (FreeParking, GoToJail, Go тощо)
  • PlayerTrades
  • PlayerPurchasesProperty
  • PlayerPurchasesHouse
  • Покупки гравцівГотелі
  • PlayerPaysRent
  • Прокурор гравця
  • (Карти всіх шансів та спільних скриньок)

І деякі штати в основі GamePhase:

  • Список гравців
  • Поточний гравець (хто черга)
  • Гроші / майно гравця
  • Будинки / Готелі на власності
  • Позиція гравця

І тоді деякі фази фіксувалимуть власний стан за необхідності, наприклад PlayerRolls реєстрував, скільки разів гравець робить послідовний дубль. Після виходу з фази PlayerRolls ми вже не дбаємо про послідовні кидки.

Багато фаз можуть бути використані повторно та пов'язані між собою. Наприклад, GamePhase CommunityChestAdvanceToGoбуде створено наступну фазу PlayerLandsOnGoз поточним станом і повернуто її. У конструкторі PlayerLandsOnGoпоточного гравця буде переміщено в Go, а їхні гроші збільшаться на 200 доларів.


8

Звичайно, є багато, багато, багато, багато, багато, багато, багато ресурсів на цю тему. Але я думаю, що ви знаходитесь на правильному шляху, розділяючи об’єкти і дозволяючи їм обробляти власні події / дані тощо.

Виконуючи настільні ігри, що базуються на кахлях, вам буде приємно мати процедури для картографування між масивом дошки та рядком / кольором та назад, а також інші функції. Я пам’ятаю свою першу настільну гру (давно давно), коли я боровся з тим, як отримати row / col з 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/


Чому б вам просто не використовувати двовимірний масив? Тоді компілятор може впоратися з цим за вас.
Джей Базузі,

Моє виправдання, що це було дуже давно. ;)
Стефан

1
gamedev має купу тонн, але я бачив не зовсім те, що шукав.
Джей Базузі,

якою мовою ти користувався?
zotherstupidguy

Basic, Basica, QB, QuickBasic тощо. ;)
Стефан

5

Значну частину матеріалів, які я можу знайти в Інтернеті, складають списки опублікованих посилань. У розділі публікацій " Шаблони дизайну ігор" є посилання на PDF-версії статей та тез. Багато з них схожі на академічні роботи, такі як Design Pattern for Games . Також є принаймні одна книга від Amazon, “ Шаблони в ігровому дизайні” .


3

Three Rings пропонує бібліотеки Java від LGPL'd. Неня та Віля - це бібліотеки, пов’язані з іграми.

Звичайно, було б корисно, якщо б у вашому запитанні згадувались платформа та / або мовні обмеження, які ви можете мати.


"Врешті-решт я розраховую на створення інтерфейсу WPF" - це означає .NET. Принаймні, наскільки я можу судити.
Mark Allen

Я не знаю про алфавітний суп.
jmucchiello

Так, я роблю .NET, але моє запитання не залежить від мови чи платформи.
Джей Базузі,

2

Я погоджуюся з відповіддю Піролістіка, і я віддаю перевагу його способу здійснення речей (я просто прокрутив інші відповіді).

За збігом обставин я також використовував його імена "GamePhase". В основному, що я б зробив у випадку покрокової настільної гри, так це щоб ваш клас GameState містив об’єкт абстрактного GamePhase, як згадував Pyrolistics.

Скажімо, ігрові стани такі:

  1. Рулон
  2. Рухайся
  3. Купити / не купувати
  4. В'язниця

Ви можете мати конкретні похідні класи для кожного штату. Майте віртуальні функції принаймні для:

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.
}

Я відрізняюся від піролістичного тим, що повинен бути етап для всього, в тому числі, коли гравець приземляється на сундук Громади чи щось інше. Я б впорався з цим у MovePhase. Це пов’язано з тим, що якщо у вас забагато послідовних фаз, гравець, швидше за все, почуватиметься занадто «керованим». Наприклад, якщо є фаза, коли гравець може ТІЛЬКИ купувати нерухомість, а потім ТІЛЬКИ купувати готелі, а потім ТІЛЬКИ купувати будинки, це начебто немає свободи. Просто з’єднайте всі ці частини в один BuyPhase і дайте гравцеві свободу купувати все, що він захоче. Клас BuyPhase досить легко впорається із тим, які покупки є законними.

Нарешті, звернімося до ігрової дошки. Хоча 2D-масив - це нормально, я б рекомендував мати графік плитки (де плитка - це позиція на дошці). У випадку монополії це скоріше був би подвійно пов'язаний список. Тоді кожна плитка мала б:

  1. попередняПлитка
  2. nextTile

Тож було б набагато простіше зробити щось на зразок:

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

Функція AdvanceTo може обробляти ваші поетапні анімації або що завгодно. А також зменшити залишок ходів, звичайно.

Поради RS Conley щодо шаблону спостерігача для графічного інтерфейсу є хорошими.

Я не писав багато раніше. Сподіваюся, це комусь допомагає.


1

Чи є якийсь рівень техніки, яким я можу скористатися?

Якщо ваше запитання не стосується мови чи платформи. тоді я рекомендую вам розглянути зразки AOP для штату, пам'ятки, командування тощо.

Що таке .NET відповідь на AOP ???

Також спробуйте знайти кілька цікавих веб-сайтів, таких як http://www.chessbin.com

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.