Страницы

понедельник, 26 сентября 2011 г.

События (Events)

Сегодня я хочу поговорить о событиях (Events). Но не о событиях встроенных в ГМ, а в более общем контексте, позволяющим увидеть всю структуру этого интересного приёма программирования.

Но сначала, конечно же, поговорим о событиях в ГМ. Все они происходят в определённый момент - по достижению неких условий, событий. Создаем объект - выполняется событие Create и вместе с ним весь код определённый в этом событий, наступил момент обновления вызываем событие Step, наступил момент рисования - Draw, нажали кнопку Keyboard_press и т.д. Т.е. мы не постоянно выполняем код и не постоянно опрашиваем объект:

  • Игра: Ты обновился?
  • Интерфейс: Нет.
  • Игра: Ты обновился?
  • Интерфейс: Нет!
  • Игра: Ты обновился?
  • Интерфейс: Да нет ещё! Я скажу когда я обновлюсь.

С этого момента, программистам пришла идея ввести так называемые Callback-функций. Мы не будем смотреть как они конкретно работают, т.к. нам потребуются знания не входящие в ГМ, но рассмотрим просто схему работы.

Когда наступают некие условия или необходимость, то вызывается функция по указателю. Происходит callback вызов: мы говорим что нам нужно это, т.с. заказываем и нам это присылают. Заказываем на то что указал наш палец в меню. Где палец - указатель, а выбор в меню это функций. Теперь переговоры выглядят так:

  • Игра: Интерфейсу необходимо обновление.
  • Интерфейс: Есть обновление!

Выявляются 2 действующих лица. Первый (в списке, верхний) - раздающий событие, второй (нижний) - слушающий. Раздающий всегда один, точнее одно событие раздает один слушающий. В то время как слушающих может быть сколько угодно. Это как вечеринка в клубе: DJ - один, слушающих много. Т.е. теперь список может выглядеть так:

  • Событие Draw: Необходимо обновление.
  • Карта: Есть обновление!
  • Инвентарь: Есть обновление!
  • Персонаж: Есть обновление!
  • ...
  • Враг №N: Есть обновление!

В ГМ уже есть много событий, но некоторые не подходят и нам необходимо определить свои и тут-то приятно осознавать что мы умеем это делать. Как именно, рассмотрим далее. А пока затронем тему, когда это нужно? Это нужно тогда, когда вы хотите уменьшить кол-во вызовов. Некоторые из вас уже наверняка пробовали имитировать создание событий. Например, обновление пути для персонажа. Если использовать этот код в событий Step, то код может выполняться чаще чем нужно. Вы подумали, а что если код выполнять не так часто? И вы старательно запихивали этот код в событие alarm и выполняли код каждые N step'ов. Этот приём и есть эмулирование работы событий. Но по сути, в чем минус этого события? В том что обновление пути может быть чаще, а может быть реже чем нужно. Т.е. вы выполняете код тогда когда прошли N игровых циклов, а не тогда когда путь нуждается в обновлений.

Плавно переходим к тому что надо сделать. А надо путь изменить тогда когда путь может измениться, т.е. когда произойдет событие "изменение обстановки" - onChangePath (при изменений). Пихнем код обновления пути в функцию onChangePath и будем вызывать тогда, когда наш путь может измениться. Теперь, изменение пути будет столько сколько необходимо, а не столько сколько пройдёт ваш персонаж и уж тем более не меньше чем нужно, дабы избежать глюков. Допустим, создали кубик который может повлиять на путь, то вызываем onChangePath у слушающих образцов. В этом случае, кубик - раздающий, тому кому надо путь изменить - слушающий.

Чтобы вызвать код обновления пути необходимо написать след. конструкцию (в Create кубика):

with(object)
onChangePath();

Где object - объекты в которых нужно обновить пути. Помнить нужно одно, что событие onChangePath принадлежит только одному объекту - object, выполнив этот код в object2, все может закончится плачевно. Т.к. в ГМ такие конструкций не безопасны, безопасность ложится на ваши плечи.

Теперь когда создается кубик, меняется структура карты и некоторые пути могут пересекать этот кубик, поэтому мы вызываем код у нужных объектов. Callback - называется так потому что этот код выполняется помимо основных функций объекта, мы как бы вызываем вновь этот объект.

Перейдем к примером, где этот прием может понадобится:

  • UI (User Interface, пользовательский интерфейс) - здесь эта техника используется очень часто. Т.е. интерфейс "работает" только тогда когда пользователь с ним взаимодействует, иначе трата сил впустую. Пользователь нажал кнопку мышки, вызываем событие нажатие мышки и обработку. Если курсор находился над кнопкой открытия инвентаря - выполняем onInventoryOn, в нем рисуем инвентарь. Нажали кнопку в инвентаре, то выполняем событие onChange и снова рисуем. Заметьте, инвентарь отрисуется за 150 степов только сколько раз он изменился + 1 на создание, а не 150 раз.
  • Обмен данными по сети. Нет смысла передавать одни и те же данные.
  • Искусственный Интеллект - передаем сообщения между объектами, обновляем ИИ только по событию изменения, если обстоятельства не изменились, то нечего заново решать ту же задачу.
  • Поиск пути
  • др. области

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

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

Комментариев нет:

Отправить комментарий