← к содержанию

Урок 15. Собственные события

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

Для этого надо создать функцию-обработчик для каждого из событий, которые требуется обрабатывать. Если вы взглянете на код модуля ddd.zavisimost.sobytija_SDL2, то увидите в объявлении класса ОбработчикСобытий, что он регистрирует функции двух типов, с незатейливыми названиями функция1 и функция2. Про них надо рассказать немного подробнее. Все они возвращают значение типа bool, которое говорит системе, надо ли уже выходить из программы (true), или можно дальше продолжать работать (false). Полная сигнатура регистрируемых функций (на самом деле делегатов, т. к. это функции-члены класса) выглядит так:

bool delegate(SDL_Event e)

Функции первого типа соответствуют событиям библиотеки SDL, таким как выход из программы (код SDL_QUIT), нажатие клавиши (код SDL_KEYDOWN), событие окна (код SDL_WINDOWEVENT) и т. д. Иногда информации о коде события достаточно, чтобы что-то сделать, например в случае получения кода SDL_QUIT, управление сразу можно передать на функцию, которая выполнит все требуемые при выходе из программы действия (в каких-то простых программах тут надо просто вернуть true, а в каких-то сначала уведомить пользователя, что ему бы надо сохраниться перед выходом). Но для большинства событий информации о типе события недостаточно, требуется ещё получить некий параметр, связанный с событием, например код нажатой или отжатой клавиши.

Для таких случаев у обработки событий окна и клавиатуры предусмотрены функции второго типа. Посмотрите, например, код функции обработка_нажатия_клавиши в классе МинимальныйОбработчикСобытий. Сама обработка_нажатия_клавиши – это функция первого типа, но уже внутри себя она вызывает функцию второго типа, и выбор этой функции основывается на том же типе события SDL_KEYDOWN, а также исходя из кода нажатой клавиши.

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

Кроме вопросов, связанных с распределением полномочий между функциями двух типов, у вас, как у пользователя есть выбор, как всё это оформить. Можно создать класс-потомок одного из классов модуля ddd.zavisimost.sobytija_SDL2. Если у вас достаточно крупная программа или игра, и вам требуется определить обработку большого количества событий нажатий клавиш и мыши, то, скорее всего, лучшим решением будет унаследоваться от класса МинимальныйОбработчикСобытий. Примерами, как это сделать, являются его классы-потомки (например, класс СтандартныйОбработчикСобытий). В этом же уроке поступим проще: мы не будем создавать новый класс, а просто зарегистрируем свою простую статическую функцию в качестве функции-обработчика второго типа.

  1. Откройте модуль scena.d и добавьте к списку импорта следующие модули: derelict.sdl2.sdl (в нём определены класс событий SDL_Event и константы, определяющие тип события и его параметры, например, коды нажатых клавиш), а также модуль стандартной библиотеки std.functional. У меня сейчас импорт модулей не из DDD выглядит так:

import derelict.sdl2.sdl; 
import std.math, std.conv, std.functional;
  1. В самом конце модуля (после функции обновление_сцены) добавьте нашу функцию-обработчик отжатия клавиши пробела (это обычная практика, вешать разовые, не продолжающиеся во времени события, на отжатие клавиши, а не на нажатие, тогда они гарантированно отработают один раз). В ней мы у объекта Места, к потомку которого присоединён наш самолётик, переключим признак отображения. Таким образом, у нас по нажатию на пробел самолётик будет исчезать и появляться обратно:

bool обработка_отжатия_клавиши_пробел(SDL_Event e) {
    центральное_место.поменять_видимость();
    return false;
}
  1. Возвращаемся к началу функции сцена, и второй строкой, после создания обработчика событий, регистрируем нашу только что созданную функцию в качестве обработчика события отжатия клавиши Пробел:

    обработчик_событий.зарегистрировать_функцию2(SDL_KEYUP, SDLK_SPACE, toDelegate(&обработка_отжатия_клавиши_пробел));

Здесь: SDL_KEYUP – код события отжатия клавиши, SDLK_SPACE – скан-код клавиши Пробел, а преобразователь toDelegate (взятый из модуля std.functional), как следует из его названия, преобразует сигнатуру нашей функции в сигнатуру делегата. В случае, если вы будете использовать наследование класса, и функция-обработчик будет его членом, такое преобразование не понадобится.

  1. Собственно, на этом всё, компилируем и запускаем программу. При нажатии на пробел самолётик исчезает и появляется: