Anthill Game Framework

Tiny framework for creating Flash games.

Инструменты пользователя

Инструменты сайта


guide:signals_and_events

Создание и обработка событий

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

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

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


Обработка событий

Рассмотрим самый простой пример использования. Рассмотрение начнем с обработки простого клика по кнопке AntButton.

// Создадим простую кнопку.
var btnPlay:AntButton = new AntButton();
btnPlay.addAnimationFromCache("ButtonPlay_mc");
add(btnPlay);
 
// Подписываемся на событие клика.
btnPlay.eventClick.add(onPlay);
 
// Обработчик события клика onPlay
private function onPlay(aButton:AntButton):void
{
  trace("Start playing!");
}

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

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


Создание событий

Вы можете использовать сигналы и слоты в своих проектах — это простой и удобный способ создания обратных связей.

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

  1. Начало строительства;
  2. Процесс строительства;
  3. Завершение строительства.

Объявляем события:

public var eventBegin:AntSignal;
public var eventProcess:AntSignal;
public var eventComplete:AntSignal;

Первое событие будет отправлять в подписавшиеся методы только указатель на здание, которое начинает строиться, второе событие будет отправлять указатель на здание и процесс завершенности в процентах, и третье событие, как и первое, будет отправлять только указатель на здание.

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

После объявления событий их нужно перед использованием создать:

eventBegin = new AntSignal(Building);
eventProcess = new AntSignal(Building, int);
eventComplete = new AntSignal(Building);

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

Примечание: При строгой типизации для события AntSignal(String, int, Number), подписывающиеся методы должны иметь соответствующие типы аргументов: function onEvent(str:String, i:int, j:Number).

Теперь события инициализированы и остается только вызывать их в нужных нам местах:

// Начало строительства
public function build():void
{
  _buildingProcces = 0;
  _buildingComplete = 57;
  // ...
 
  // Вызываем событие начала строительства, 
  // в качестве аргумента подписчикам передаем 
  // указатель на себя.
  eventBegin.dispatch(this);
}
 
// Процесс строительства
override public function update():void
{
  // Выполняем строительство с заданной скоростью
  _buildingProcess += _buildingSpeed * AntG.elapsed;
 
  // Рассчитываем текущий процент завершенности
  var newPercent:int = AntMath.toPercent(_buildingProcess, _buildingComplete);
 
  // Отправляем событие, только если процент изменился
  if (newPercent != _oldPercent)
  {
    // Передаем указатель на себя и новый процент завершенности
    eventProcess.dispatch(this, newPercent);
    _oldPercent = newPercent;
  }
 
  // Если здание достроилось
  if (newPercent == _buildingComplete)
  {
    // Вызываем событие, которое уведомит
    // подписчиков о завершении строительства
    eventComplete.dispatch(this);
  }
}

Так может выглядеть код, генерирующий события, остается написать код, который будет подписываться и обрабатывать эти события:

// Создаем дом
var house:Building = new Building();
add(house);
 
// Подписываемся на процесс строительства
house.eventProcess.add(onBuildProcess);
 
// Подписываемся на завершение строительства
house.eventComplete.add(onBuildComplete);
 
// Обработка строительства
private function onBuildProcess(aHouse:Building, aPercent:int):void
{
  trace("House " + aHouse.name + " completed on " + aPercent + "%.";
}
 
// Завершение процесса строительства
private function onBuildComplete(aHouse:Building):void
{
  // Удаляем всех подписчиков
  aHouse.eventProcess.clear();
  aHouse.eventComplete.clear();
 
  // А также тут можно создать какой-нибудь красивый эффект
  // завершения стройки и запустить заселение жильцов ;)
}

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

Метод clear() удаляет все методы, подписавшиеся на определенное событие. Но можно удалять и конкретного подписчика методом remove():

house.eventProcess.remove(onBuildProcess);

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

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

house.eventComplete.addInstant(onBuildComplete);

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

Так же по мимо возможности перехватывать и обрабатывать события (сигналы) от конкретных объектов, вы можете реализовывать и перехватывать всплывающие события что позволяет подписываться не на конкретные объекты, а на целые группы объектов. Подробнее об этом читайте в следующем разделе: «Всплывающие события».


guide/signals_and_events.txt · Последние изменения: 24.05.2013 07:51 — Anton Karlov