Я пишу компьютерную версию игры Dominion . Это пошаговая карточная игра, в которой карты действий, карты сокровищ и карты победных очков накапливаются в личной колоде игрока. У меня довольно хорошо развита структура классов, и я начинаю разрабатывать игровую логику. Я использую Python, и я могу добавить простой графический интерфейс с Pygame позже.
Последовательность ходов игроков регулируется очень простым конечным автоматом. Повороты проходят по часовой стрелке, и игрок не может выйти из игры, пока она не закончится. Игра одного хода также является конечным автоматом; в общем, игроки проходят через «фазу действия», «фазу покупки» и «фазу очистки» (в таком порядке). На основании ответа на вопрос Как реализовать пошаговый игровой движок? конечный автомат - стандартная техника для этой ситуации.
Моя проблема в том, что во время фазы действия игрока она может использовать карту действия, которая имеет побочные эффекты, либо на себя, либо на одного или нескольких других игроков. Например, одна карта действия позволяет игроку сделать второй ход сразу после завершения текущего хода. Другая карта действия заставляет всех других игроков сбросить две карты из своих рук. Еще одна карта действий ничего не делает для текущего хода, но позволяет игроку взять дополнительные карты в свой следующий ход. Чтобы сделать вещи еще более сложными, в игру часто добавляются новые дополнения, которые добавляют новые карты. Мне кажется, что жесткое кодирование результатов каждой карты действий в конечный автомат игры было бы и уродливым, и непригодным для использования. Ответ на пошаговый стратегический цикл не идет в уровень детализации, который обращается к проектам, чтобы решить эту проблему.
Какую модель программирования я должен использовать, чтобы охватить тот факт, что общий шаблон смены может быть изменен с помощью действий, выполняемых в ходе очереди? Должен ли объект игры отслеживать эффекты каждой карты действий? Или, если карты должны реализовывать свои собственные эффекты (например, посредством реализации интерфейса), какая настройка требуется, чтобы дать им достаточно энергии? Я придумал несколько решений этой проблемы, но мне интересно, есть ли стандартный способ ее решения. В частности, я хотел бы знать, какой объект / класс / что-либо отвечает за отслеживание действий, которые каждый игрок должен выполнять в результате разыгрываемой карты действий, а также как это связано с временными изменениями в нормальной последовательности действий. конечный автомат.
источник
Ответы:
Я согласен с Яри Комппой в том, что определение эффектов карт с помощью мощного языка сценариев - это путь. Но я считаю, что ключом к максимальной гибкости является обработка событий с помощью сценариев.
Чтобы карты могли взаимодействовать с более поздними игровыми событиями, вы можете добавить API сценариев, чтобы добавить «ловушки сценариев» к определенным событиям, таким как начало и окончание этапов игры, или определенные действия, которые могут выполнять игроки. Это означает, что сценарий, который выполняется, когда играется карта, может зарегистрировать функцию, которая вызывается при следующем достижении определенной фазы. Количество функций, которые можно зарегистрировать для каждого события, должно быть неограниченным. Когда их несколько, они затем вызываются в порядке их регистрации (если, конечно, не существует основного правила игры, которое говорит что-то другое).
Должна быть возможность зарегистрировать эти хуки для всех игроков или только для определенных игроков. Я бы также предложил добавить возможность для хуков самим решать, следует ли им продолжать звонить или нет. В этих примерах возвращаемое значение функции ловушки (true или false) используется для выражения этого.
Ваша карта с двойным поворотом будет делать что-то вроде этого:
(Я понятия не имею, есть ли у Dominion что-то вроде «фазы очистки» - в этом примере это гипотетическая последняя фаза хода игроков)
Карта, которая позволяет каждому игроку взять дополнительную карту в начале фазы розыгрыша, будет выглядеть следующим образом:
Карта, из-за которой целевой игрок теряет хит-пойнт при каждой игре, будет выглядеть следующим образом:
Вы не будете иметь дело с жестким кодированием некоторых игровых действий, таких как рисование карт или потеря очков жизни, потому что их полное определение - что именно означает «вытянуть карту» - является частью основной игровой механики. Например, я знаю некоторые TCG, в которых, когда по какой-либо причине вам приходится брать карту по какой-либо причине, а колода пуста, вы проигрываете игру. Это правило напечатано не на каждой карточке, которая заставляет вас рисовать карточки, потому что оно есть в книге правил. Так что вам не нужно проверять это условие потери в скрипте каждой карты. Проверка подобных вещей должна быть частью жестко запрограммированной
drawCard()
функции (которая, кстати, также будет хорошим кандидатом на поддающееся перехвату событие).Кстати: маловероятно, что вы сможете заранее планировать каждый непонятный механик, который могут появиться в будущих выпусках , поэтому независимо от того, что вы делаете, вам все равно придется время от времени добавлять новые функции для будущих выпусков (в этом случай, конфетти, бросая мини-игру).
источник
Я дал эту проблему - гибкий компьютерный движок карточных игр - некоторые думали некоторое время назад.
Во-первых, сложная карточная игра, такая как Chez Geek или Fluxx (и, я полагаю, Dominion) потребовала бы, чтобы карты были пригодны для сценариев. По сути, каждая карта поставляется со своим набором скриптов, которые могут по-разному изменять состояние игры. Это позволит вам дать системе возможность заглянуть в будущее, поскольку сценарии могут делать то, о чем вы не можете думать прямо сейчас, но могут появиться в будущем расширении.
Во-вторых, жесткий «поворот» может вызывать проблемы.
Вам нужен какой-то «стек ходов», который содержит «специальные ходы», например, «сбросить 2 карты». Когда стек пуст, нормальный ход по умолчанию продолжается.
Во Fluxx вполне возможно, что один ход будет выглядеть примерно так:
..и так далее и тому подобное. Поэтому разработка структуры поворота, которая может справиться с вышеуказанным нарушением, может быть довольно сложной. Добавьте к этому многочисленные игры с картами «когда-либо» (как в «chez geek»), в которых карты «всякий раз» могут нарушить нормальный поток, например, отменив ту карту, которая была сыграна последней.
Поэтому я бы начал с разработки очень гибкой структуры поворотов, спроектировав ее так, чтобы ее можно было описать как сценарий (поскольку каждая игра должна иметь свой собственный «основной сценарий», обрабатывающий основную структуру игры). Тогда любая карта должна быть в сценарии; большинство карт, вероятно, не делают ничего странного, но другие делают. Карты также могут иметь различные атрибуты - могут ли они храниться в руке, разыгрываться «когда угодно», могут ли они храниться в виде активов (например, «хранители» fluxx или различные вещи в «чиз-гике», например, еда) ...
На самом деле я никогда не начинал реализовывать это, поэтому на практике вы можете столкнуться с множеством других проблем. Самый простой способ начать - это начать с того, что вы знаете о системе, которую вы хотите внедрить, и реализовать их с помощью сценариев, стараясь изо всех сил, поэтому при появлении расширения вам не нужно будет пересматривать Базовая система - много. знак равно
источник
Похоже, что Hearthstone делает что-то связанное, и, честно говоря, я думаю, что лучший способ добиться гибкости - это механизм ECS с ориентированным на данные дизайном. Я пытался сделать клон очага, и в противном случае это оказалось невозможным. Все крайние случаи. Если вы сталкиваетесь со многими из этих странных крайних случаев, то это, вероятно, лучший способ сделать это. Я довольно предвзят, хотя из недавнего опыта пробую эту технику.
Изменить: ECS может даже не понадобиться в зависимости от того, какая гибкость и оптимизация вам нужна. Это всего лишь один из способов сделать это. DOD Я ошибочно считал процедурным программированием, хотя они имеют много общего. Я имею в виду. Что вам следует подумать об отказе от ООП полностью или, по крайней мере, в основном, и вместо этого сфокусировать свое внимание на данных и их организации. Избегайте наследования и методов. Вместо этого сосредоточьтесь на общедоступных функциях (системах) для манипулирования данными вашей карты. Каждое действие не является шаблонной вещью или какой-либо логикой, а является необработанными данными. Где ваши системы, то используйте его для выполнения логики. Случай целочисленного переключения или использование целого числа для доступа к массиву указателей на функции помогают эффективно определить желаемую логику из входных данных.
Основные правила, которым нужно следовать, - избегать привязки логики напрямую к данным, избегать зависимости данных друг от друга, насколько это возможно (могут применяться исключения), и когда вам нужна гибкая логика, которая кажется недоступной ... Рассмотрите возможность преобразования его в данные.
Есть преимущества, которые будут иметь это. Каждая карта может иметь значение перечисления (ей) или строку (и) для представления их действия. Этот стажер позволяет создавать карточки с помощью текстовых или json-файлов и позволяет программе автоматически импортировать их. Если вы делаете действия игрока списком данных, это дает еще большую гибкость, особенно если карта зависит от логики прошлого, как это делает Hearthstone, или если вы хотите сохранить игру или переиграть игру в любой момент. Есть потенциал для создания ИИ более легко. Особенно при использовании «служебных систем» вместо «дерева поведения». Сетевое взаимодействие также становится проще, потому что вместо того, чтобы выяснять, как заставить целые, возможно, полиморфные объекты передавать по проводам и как будет настроена сериализация, ваши игровые объекты уже являются не чем иным, как простыми данными, которые в конечном итоге очень легко перемещать. И, наконец, что не менее важно, это позволяет вам легче оптимизировать, потому что вместо того, чтобы тратить время на беспокойство о коде, вы сможете лучше организовать свои данные, чтобы процессору было легче справляться с этим. У Python могут быть проблемы здесь, но посмотрите «строку кэша» и как она связана с разработчиком игры. Возможно, это не важно для прототипирования, но в будущем это пригодится.
Несколько полезных ссылок.
Примечание: ECS позволяет динамически добавлять / удалять переменные (называемые компонентами) во время выполнения. Пример c программы того, как ECS может «выглядеть» (есть масса способов сделать это).
Создает группу объектов с данными текстуры и положения и в конце уничтожает объект, имеющий компонент текстуры, который находится в третьем индексе массива компонентов текстуры. Выглядит странно, но это один из способов делать вещи. Вот пример того, как вы будете рендерить все, что имеет компонент текстуры.
источник