В последнее время я развлекаюсь, программируя простую текстовую приключенческую игру, и застрял в том, что кажется очень простым вопросом дизайна.
Чтобы дать краткий обзор: игра разбита на Room
объекты. У каждого Room
есть список Entity
объектов, которые находятся в этой комнате. У каждого Entity
есть состояние события, которое представляет собой простую строку-> логическая карта, и список действий, который является строкой-> карта функции.
Пользовательский ввод принимает форму [action] [entity]
. Room
Использует имя лица , чтобы вернуть соответствующий Entity
объект, который затем использует имя действия , чтобы найти правильную функцию и выполняет его.
Чтобы сгенерировать описание комнаты, каждый Room
объект отображает свою собственную строку описания, а затем добавляет строки описания каждого Entity
. Entity
Описание может меняться в зависимости от его состояния ( «Дверь открыта», «Дверь закрыта», «Дверь заперта», и т.д.).
Вот в чем проблема: с помощью этого метода количество описывающих функций и функций, которые мне нужно реализовать, быстро выходит из-под контроля. Одна моя стартовая комната имеет около 20 функций между 5 объектами.
Я могу объединить все действия в одну функцию и переключать их, если это все еще, но это по-прежнему две функции для каждой сущности. Я также могу создавать специальные Entity
подклассы для общих / общих объектов, таких как двери и ключи, но это только доходит до меня.
РЕДАКТИРОВАТЬ 1: По запросу, примеры псевдокода этих функций действия.
string outsideDungeonBushesSearch(currentRoom, thisEntity, player)
if thisEntity["is_searched"] then
return "There was nothing more in the bushes."
else
thisEntity["is_searched"] := true
currentRoom.setEntity("dungeonDoorKey")
return "You found a key in the bushes."
end if
string dungeonDoorKeyUse(currentRoom, thisEntity, player)
if getEntity("outsideDungeonDoor")["is_locked"] then
getEntity("outsideDungeonDoor")["is_locked"] := false
return "You unlocked the door."
else
return "The door is already unlocked."
end if
Функции описания действуют почти одинаково, проверяя состояние и возвращая соответствующую строку.
РЕДАКТИРОВАТЬ 2: пересмотрел формулировку моего вопроса. Предположим, что может существовать значительное количество внутриигровых объектов, которые не имеют общего поведения (реакции на конкретные состояния на конкретные действия) с другими объектами. Есть ли способ, которым я могу определить эти уникальные поведения более понятным и более понятным способом, чем написание пользовательской функции для каждого конкретного действия?
Ответы:
Вместо того, чтобы создавать отдельную функцию для каждой комбинации существительных и глаголов, вы должны настроить архитектуру, в которой есть один общий интерфейс, который реализуют все объекты в игре.
Один из подходов к моей голове - определить объект Entity, который расширяет все конкретные объекты в вашей игре. Каждый объект будет иметь таблицу (какую бы структуру данных ваш язык не использовал для ассоциативных массивов), которая связывает разные действия с разными результатами. Действия в таблице, скорее всего, будут Strings (например, «open»), в то время как связанный результат может даже быть частной функцией в объекте, если ваш язык поддерживает функции первого класса.
Аналогично, состояние объекта сохраняется в различных полях объекта. Так, например, вы можете иметь массив вещей в кусте, и тогда функция, связанная с «поиском», будет воздействовать на этот массив, либо возвращая найденный объект, либо строку «В кустах больше ничего не было».
Между тем, один из публичных методов является то, как Entity.actOn (действие String) Затем в этом методе сравнения действия передается с использованием таблицей действий для этого объекта; если это действие в таблице затем возвращает результат.
Теперь все различные функции, необходимые для каждого объекта, будут содержаться внутри объекта, что позволяет легко повторять этот объект в других комнатах (например, создавать экземпляр объекта Door в каждой комнате, в которой есть дверь)
И, наконец, определить все номера в XML или JSON или независимо от того, так что вы можете иметь много уникальных номера, без необходимости писать отдельный код для каждой отдельной комнаты. Загрузите этот файл данных при запуске игры, и анализировать данные, чтобы создавать экземпляры объектов, которые населяют вашу игру. Что-то типа:
Сложение: ах, я только что прочитал ответ FXIII и это немного ближе к концу выскочил на меня:
Хотя я не согласен, что пламегаситель быть вызвано то, что будет происходить только один раз (я мог видеть эту ловушка повторного использования для различных объектов) Я думаю, что я, наконец, получить то, что вы имели в виду о сущностях, которые реагируют однозначно на ввод данных пользователя. Я бы, вероятно, адрес вещи, как сделать одну двери в вашем подземелье есть огненная ловушка, построив все мои объекты с компонентом архитектурой (подробно в другом месте).
Таким образом, каждая сущность двери строится как пучок компонентов, и я могу гибко смешивать и подгонять компоненты между различными объектами. Например, большинство дверей будет иметь конфигурации что-то вроде
но одна двери с огненным шаром ловушкой будет
и тогда единственный уникальный код, который мне пришлось бы написать для этой двери, - это компонент FireballTrap. Он будет использовать те же компоненты Lock и Portal, что и все другие двери, и если позже я решу использовать FireballTrap в сундуке с сокровищами или что-то такое же простое, как добавление компонента FireballTrap в этот сундук.
Независимо от того, определяете ли вы все компоненты в скомпилированном коде или на отдельном языке сценариев, я не вижу большого различия (в любом случае вы будете писать код где-нибудь ), но важно то, что вы можете значительно сократить количество уникального кода, который нужно написать. Черт возьми, если вы не беспокоитесь о гибкости для дизайнеров / моддеров уровней (в конце концов, вы сами пишете эту игру), вы можете даже заставить все сущности наследовать от Entity и добавлять компоненты в конструктор, а не в файл конфигурации или скрипт или без разницы:
источник
Entity
только для одного объекта группирует код, но не уменьшает объем кода, который я должен написать. Или это неизбежная ловушка в этом отношении?Entity
а атрибуты определяют его начальное состояние. Я предполагаю, что дочерние теги объекта действуют как параметры для любого действия, с которым связан этот тег, верно?Проблема размерности, к которой вы обращаетесь, вполне нормальна и почти неизбежна. Вы хотите , чтобы найти способ выразить свои объекты , что является одновременно coincise и гибким .
«Контейнер» (куст в ответе джокера) - это совпадающий путь, но вы видите, что он недостаточно гибок .
Я не предлагаю вам попытаться найти общий интерфейс , а затем использовать файлы конфигурации для определения поведения, потому что вы всегда будете иметь неприятные ощущения быть между молотом (стандартные и скучные лица, легко описать) и наковальней ( уникальные фантастические объекты, но слишком долго для реализации).
Мое предложение заключается в использовании в интерпретируемый язык для кода поведения.
Подумайте о примере куста: это контейнер, но в нашем кусте должны быть определенные предметы; контейнерный объект может иметь:
У одного из этих предметов есть веревка, которая запускает хитрое устройство, которое, в свою очередь, запускает пламя, сжигающее куст… (видите, я могу читать ваши мысли, чтобы я знал, что вам нравится).
Вы можете использовать скрипт для описания этого куста вместо файла конфигурации, помещая соответствующий дополнительный код в ловушку, которую вы выполняете из своей основной программы каждый раз, когда кто-то выбирает элемент из контейнера.
Теперь у вас есть много вариантов архитектуры: вы можете определить поведенческие инструменты в качестве базовых классов , используя язык кода или язык сценариев (вещи таких контейнеров, двери , как и так далее). Цель из theese вещей , чтобы позволить вам описать объекты Исли на общую простые модели поведения и их настройки с помощью привязок на языке сценариев .
Все сущности должны быть доступны для сценария: вы можете связать идентификатор с каждой сущностью и поместить их в контейнер, который экспортируется в расширение сценария языка сценариев.
Использование стратегии сценариев пусть вам сохранить ваши настройки простого (нет вещи , как ,
<item triggerFlamesOnPicking="true">
что вы будете использовать только один раз), позволяя вам выразить нечетный beaviours (забавные из них) , добавив некоторые строки кодыВ двух словах: скрипты как файл конфигурации, который может запускать код.
источник