Помогите с дизайном программы

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

Мне нужна помощь с:

Создание хорошего класса "Event" для моей игры. Я хотел бы, чтобы все работало так, чтобы каждый предмет и комната могли быть связаны с событием.

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

Я хочу иметь возможность создавать различные виды событий, например:

  • Выведите текст, а затем задайте игроку вопрос. Если игрок дает правильный ответ, сделайте что-нибудь.

  • Выведите некоторый текст, затем удалите предмет из инвентаря игрока, а затем переместите игрока в другую комнату.

Чего я пытаюсь избежать:

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

  • Необходимость жестко кодировать каждое событие, чтобы элемент или комната просто вызывали определенный метод в классе Event.

Что я думаю на данный момент:

Создание нескольких подклассов, чтобы я мог, например, создавать новые события (и связывать их), используя: itemObject.setEvent(new Event(new Question("introText", "outroText", "correctAnswer")));

Пожалуйста, дайте мне знать, если нужна дополнительная информация! Спасибо!

4 ответа

Решение

Книги о программировании игр великолепны... хотя и немного дорогостоящи.

Вероятно, вы можете найти одну или две соответствующие статьи в списке статей GameDev, и, вероятно, на их форумах спрятаны некоторые хорошие материалы.

Что касается класса Event, вам почти наверняка нужен базовый класс с некоторым количеством подклассов (MoveEvent, InventoryEvent и т. Д.). И если вы действительно хотите избежать жесткого кодирования, вы должны определить свою игру в файлах данных. Комнаты, предметы и события... плюс все остальное, что вы добавите позже. Формат, который вы используете для определения вещей, полностью зависит от вас. Я рекомендую кое-что, что даст вам практический опыт, так как это учебное упражнение: например, анализатор XML, который вы не использовали. Не самый эффективный формат для разбора, но это консольная игра, так что это не такая проблема.

Одно из предложений, которое я имею для подкласса Event, - это то, что может связывать другие события вместе. Интерфейс не меняется, и он позволяет вам делать такие вещи, как "добавить это в инвентарь игрока, забрать это и переместить их сюда".

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

Например, ваш домен может состоять из следующих интерфейсов:

public enum Action { HIT, TALK_TO, EAT, USE };

public interface GameEntity {
  /**
   * Performs the specified action on this GameEntity, passing in zero or
   * more other GameEntity instances as parameters.
   */
  void applyAction(Action action, GameEntity... params);    
}

public interface Item extends GameEntity { }
public interface Person extends GameEntity { }

Кроме того, вы можете определить класс TextParser, который будет обрабатывать входную строку и делать результирующие вызовы на уровне домена, либо возвращать ответ по умолчанию: "Я не понимаю". Например, входная фраза "использовать ключ на двери" приведет к вызову метода:

key.applyAction(Action.USE, door);

Говоря о событиях, на ум приходит картина наблюдателя. Вы можете думать, что ваша игра будет представлена ​​внутри нескольких конечных автоматов (вы также можете посмотреть это в Википедии, я не могу добавить 2-ю ссылку самостоятельно, потому что она новичок в SO). Каждый переход с одного этапа на другой - это событие, которое может быть сообщено зарегистрированным оберсерверам этого события.

Пример:

USE KEY ON LOCK: запускает удаление ключа и открытие двери; имплицитно "каскадный" переход к следующему событию без дополнительного взаимодействия с пользователем (открытие двери): запуск комнаты с дополнительным выходом и т. д.

Недавно я реализовал нечто похожее на то, что вы описываете. Я начал с создания класса EventManager с методами "Опубликовать" и "Подписаться". Когда система запускается, она сканирует все загруженные сборки (библиотеки) на предмет классов, которые реализуют интерфейс IEventListener:

/// <summary>
/// A class that intends to listen for specific events to be published by an
/// event manager.
/// <see cref="EventManager"/>
/// </summary>
public interface IEventListener
{
    /// <summary>
    /// Subscribes to the event types that this listener wishes to listen for.
    /// </summary>
    /// <param name="eventManager"></param>
    void RegisterSubscriptions(EventManager eventManager);
}

... и вызывает метод RegisterSubscription для каждого из них. (Существует только один экземпляр EventManager, который используется везде; я использую внедрение зависимостей, чтобы связать его как одноэлементный экземпляр.)

RegisterSubscription просто вызовет метод Subscribe для каждого типа события и обработчика, которые данный класс предназначен для обработки. (Я использую.NET, поэтому с событиями и обработчиками немного проще работать, но вы можете сделать то же самое в Java, используя анонимные подклассы.)

Типичное событие может быть чем-то вроде RoomEntered, а аргументы, связанные с этим событием, могут включать идентификатор комнаты и, возможно, некоторые другие свойства комнаты. Движок игры публикует это событие, когда пользователь входит в комнату, предоставляя соответствующие аргументы события для этой комнаты, и класс EventManager отвечает за вызов каждого обработчика, подписавшегося на этот тип события. Одним из обработчиков этого события может быть "HandleEnterRoomWithMonsters", который проверяет, есть ли в комнате монстры, и выполняет соответствующее действие.

Имеет ли это смысл в целом? У вас есть вопросы по поводу этого подхода?

Другие вопросы по тегам