Скриптинг двигателя и структура классов - PullRequest
2 голосов
/ 03 ноября 2010

Я начал кодировать движок 2d JRPG в C # / XNA, и у меня есть несколько вопросов о структуре классов и игровых событиях.

В среде типа «создатель игры» у вас есть игровой скрипт, который запускаетсясобытия и так далее на основе пользовательского ввода.Обычно это не ОО и не имеет ссылки на классы и т. Д.

Теперь, в моем собственном движке, как лучше кодировать игровые события?

Предположим, вы взаимодействуете с NPC на экране.Как это событие будет обрабатываться на языке ОО?Я предполагаю, что у меня будет событие в классе символов, и я предполагаю, что это событие будет назначено в разделе, который загружает карту, но где это событие будет связано?Какой класс будет содержать логику событий?

РЕДАКТИРОВАТЬ: я должен отметить, что у меня нет желания превращать это в полностью настраиваемый движок для использования другие , и поэтому полная реализация языка сценариев являетсяне совсем то, что я хочу.

class Character {
...
public delegate void InteractionEventHandler(object sender, EventArgs e);
...
public event InteractionEventHandler Interact;

...

}

class Map {

public Map(){

...

//add a new character to the NPC collection
newchar.Interact += ???
...
}

}

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

Основной класссодержит экземпляр класса Map.

Класс Map загружает карты в xml через конвейер содержимого.

Экземпляр карты содержит ссылки на все объекты и NPC, содержащиеся в карте.

Класс Map создает новые экземпляры этих объектов, которые все наследуются от класса Drawable, который содержит основные методы и свойства, необходимые для рисования объекта на экране.Все эти экземпляры добавляются в коллекцию в классе Map.

Класс Character является одним из классов, который наследуется от класса Drawable и будет содержать событие Interact.

Как я уже сказал,проект основан на платформе XNA с фиксированным временным шагом, поэтому каждый тик движок запускает методы Update и Draw в классе Main.

Метод update содержит логику для обработки ввода игрока.Таким образом, игрок может перейти к NPC на карте и нажать кнопку взаимодействия.Логика проверяет NPC перед игроком и запускает его событие Interact ... тогда что происходит?

1 Ответ

0 голосов
/ 03 ноября 2010

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

Но я не понимаю ваш аргумент в пользу "событий, не являющихся ОО", что заставляет вас в это верить?Что это за события, которые не могут быть объектно-ориентированными?

Теперь о том, как обрабатывать события и процесс в целом, у меня нет опыта работы с C #, но я сделал свой вклад в C ++, ивот подход, который я неоднократно использовал, который был достаточно гибким для моих нужд:

У меня был мета-класс Event, из которого каждое определенное событие могло бы быть подклассом (то есть события пользовательского интерфейса, события NPC,так далее).Был также EventManager, который создает и уничтожает все события («Emitters», компоненты, которые должны инициировать события, вызывал бы диспетчер для экземпляра, то есть GameObjects) и, что наиболее важно, имеет очередь обработки.

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

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

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

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

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

// singleton
class EventManager {
  public:
  Event* createEvt<EventType*>();
  void subscribe(Event*, Listener*);
  void hook(Event*);
  void update();
}

class Listener {
  // tells evt manager we want to listen to these events
  void register(Event* someEvtType, Handler* myMethod);

  // adds event to the queue (called by the EventManager)
  void enqueue(Event* someEvt); 

  // processes events in the queue
  void process();

  protected:
  // calls every subscribed handler to process the event
  // a handler is only a functor that is bound to an event
  // when calling "register()"
  void dispatch(Event*);

  map<Event*, list<Handler*>> myHandlers; // an event could have multiple handlers (methods)
}

class GameObject : public Listener {

}

class NPC : public GameObject {
  protected:
    void walkTo(NPCEvent_Walk* evt);
    void talk(NPCEvent_Talk* evt);
}

// inside wherever you're gonna trigger the event, i presumed on mouseclick here
void InputManager::mouseClicked(MouseEvent* evt) {
  // we want the NPC to move
  NPCEvent_Walk* evtWalk = EventManager->createEvt<NPCEvent_Walk>();
  evtWalk->setX(...);
  evtWalk->setY(...);
  // and talk
  NPCEvent_Talk* evtTalk = EventManager->createEvt<NPCEvent_Talk>();
  evtTalk->setText(...);

  // when the master queue is empty, these events will be
  // dispatched to their registered Listeners which will
  // process them on their own terms
  EventManager->hook(evtWalk);
  EventManager->hook(evtTalk);
}

// as an NPC, i'd like to be notified of NPCEvents
NPC::NPC() {
  register(NPCEvent_Walk*, &NPC::walkTo);
  register(NPCEvent_Talk*, &NPC::talk);
}

void NPC::walkTo(NPCEvent_Walk*) {
  // move it
  // ...

  // are we done?
  //  return true;
  // if we need more updates, return false and we will be called again
  //  return false;
}

void NPC::talk(NPCEvent_Talk*) {
  // update UI components or whatever
  // ...
  return true;
}

Ясно, что этот код не будет работать, поскольку в нем отсутствуют все детали реализации и он даже синтаксически не корректен, но он должен дать вамидея.Однако, как я сказал выше, вам не нужно делать это, если вы используете XNA.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...