От переводчика
Доброго времени суток! Эта статья представляет собой перевод документации к движку Cocos2d-x.
В предыдущих частях мы уже рассмотрели большинство основных компонентов движка:
Sprite
Action
UI Components
Scene и другие
Нам осталось совсем немного для создания полноценной игры. А именно, обеспечить сам игровой процесс. Для этого, в Cocos2d-x существует диспетчер событий.
Диспетчер событий
Что такое механизм EventDispatch? EventDispatch — это механизм реагирования на пользовательские события.
Основы:
- Слушатели событий инкапсулируют ваш код обработки событий.
- Диспетчер событий уведомляет слушателей о пользовательских событиях.
- Объекты событий содержат информацию о событии.
5 типов слушателей событий
EventListenerTouch — реагирует на касание сенсорного экрана
EventListenerKeyboard — реагирует на нажатия клавиатуры
EventListenerAcceleration — реагирует на события акселерометра
EventListenMouse — реагирует на события мышки
EventListenerCustom — реагирует на настраиваемые события
FixedPriority vs SceneGraphPriority
EventDispatcher использует приоритеты, чтобы решать, какие слушатели получат событие первыми.
Fixed Priority(фиксированный приоритет) представляет целочисленное значение. Слушатели событий с более низким значение приоритета получают события на обработку, раньше, чем слушатели с высоким значением приоритета.
Scene Graph Priority — это указатель на Node-объект. Слушатели событий, чьи узлы имеют более высокое значение z-прядка(которые рисуются сверху), получают события прежде, чем слушатели, чьи узлы имеют более низкие значения z-порядка(которые рисуются снизу). Это гарантирует, что события касания получат передние элементы, как и следовало ожидать.
Помните граф сцены? Когда мы говорили об этой диаграмме?

При использовании Scene Graph Priority вы фактически проходите это дерево в обратную сторону… I, H, G, F, E, D, C, B, A. Если сработает событие, H заметит это и либо поглотит его (подробнее об этом ниже), либо пропустит дальше к I. Тоже самое с I, он либо потребит действие, либо пропустит его дальше к G, и так далее.
События касания
События касания — самые важные события в мобильном гейминге. Они легки в создании и предоставляют гибкую функциональность. Давайте уточним, что такое событие касания. Когда вы дотрагиваетесь до экрана вашего мобильного девайса, он воспринимает касание, смотрит где вы прикоснулись и решает, на что вы нажали. Затем, вы получаете ответ. Возможно, вы коснулись не активного объекта, а чего-то под ним. Событиям касания, обычно, присваивается приоритет, а отвечает событие с наивысшим приоритетом.
Как создать базовый слушатель события касания:
// Создание "одиночного" слушателя событий касания
// (обрабатывается одно касание за раз)
auto listener1 = EventListenerTouchOneByOne::create();
// срабатывает при нажатии
listener1->onTouchBegan = [](Touch* touch, Event* event){
// ваш код
return true; // Если вы его приняли
};
// срабатывает при перемещении касания
listener1->onTouchMoved = [](Touch* touch, Event* event){
// ваш код
};
// срабатывает при отпускании
listener1->onTouchEnded = [=](Touch* touch, Event* event){
// ваш код
};
// Добавляем слушатель
_eventDispatcher->addEventListenerWithSceneGraphPriority(listener1, this);
Как вы могли заметить, существуют 3 отдельных действия, которые вы можете использовать для слушателя касаний. Каждое из них вызывается при определенных условиях.
onTouchBegan — срабатывает при нажатии.
onTouchMoved — срабатывает при перемещении касания.
onTouchEnded — срабатывает при отпускании.
Поглощение событий
Когда у вас есть слушатель и вы хотите чтобы объект принял поданное событие, вы должны поглотить его. Другими словами, вы принимаете его, чтобы оно не прошло дальше, к другим объектам с более низким приоритетом. Это легко реализовать.
// Чтобы "поглотить" действие, возвращаем true
// Метод onTouchBegan поглотит действие касания,
// не позволяя другим слушателям использовать его
listener1->setSwallowTouches(true);
// Также вы должны вернуть true в onTouchBegan()
listener1->onTouchBegan = [](Touch* touch, Event* event){
// ваш код
return true;
};
Создание событий клавиатуры
Для компьютерных игр, вам может понадобится использование клавиатуры. Cocos2d-x поддерживает клавиатуру. Как и события касания, клавиатурные события легко создаются.
// создание слушателя клавиатурных событий
auto listener = EventListenerKeyboard::create();
listener->onKeyPressed = CC_CALLBACK_2(KeyboardTest::onKeyPressed, this);
listener->onKeyReleased = CC_CALLBACK_2(KeyboardTest::onKeyReleased, this);
_eventDispatcher->addEventListenerWithSceneGraphPriority(listener, this);
// реализация прототипа callback функции
void KeyboardTest::onKeyPressed(EventKeyboard::KeyCode keyCode, Event* event)
{
log("Key with keycode %d pressed", keyCode);
}
void KeyboardTest::onKeyReleased(EventKeyboard::KeyCode keyCode, Event* event)
{
log("Key with keycode %d released", keyCode);
}
Создание событий акселерометра
Некоторые мобильные устройства оснащены акселерометром. Акселерометр — это датчик, который измеряет перегрузку, а также изменения направления. Используется, например, когда необходимо перемещать телефон из стороны в сторону, для имитации балансирующего действия. Cocos2d-x также поддерживает эти события. Перед использованием событий акселерометра, вам необходимо подключить их.
Device::setAccelerometerEnabled(true);
// создание слушателя
auto listener = EventListenerAcceleration::create(CC_CALLBACK_2(
AccelerometerTest::onAcceleration, this));
_eventDispatcher->addEventListenerWithSceneGraphPriority(listener, this);
// реализация прототипа callback функции
void AccelerometerTest::onAcceleration(Acceleration* acc, Event* event)
{
// Processing logic here
}
Создание событий мыши
Как всегда, Cococ2d-x поддерживает события мыши.
_mouseListener = EventListenerMouse::create();
_mouseListener->onMouseMove = CC_CALLBACK_1(MouseTest::onMouseMove, this);
_mouseListener->onMouseUp = CC_CALLBACK_1(MouseTest::onMouseUp, this);
_mouseListener->onMouseDown = CC_CALLBACK_1(MouseTest::onMouseDown, this);
_mouseListener->onMouseScroll = CC_CALLBACK_1(MouseTest::onMouseScroll, this);
_eventDispatcher->addEventListenerWithSceneGraphPriority(_mouseListener, this);
void MouseTest::onMouseDown(Event *event)
{
// для демонстрации события...
EventMouse* e = (EventMouse*)event;
string str = "Mouse Down detected, Key: ";
str += tostr(e->getMouseButton());
}
void MouseTest::onMouseUp(Event *event)
{
// для демонстрации события...
EventMouse* e = (EventMouse*)event;
string str = "Mouse Up detected, Key: ";
str += tostr(e->getMouseButton());
}
void MouseTest::onMouseMove(Event *event)
{
// для демонстрации события...
EventMouse* e = (EventMouse*)event;
string str = "MousePosition X:";
str = str + tostr(e->getCursorX()) + " Y:" + tostr(e->getCursorY());
}
void MouseTest::onMouseScroll(Event *event)
{
// для демонстрации события...
EventMouse* e = (EventMouse*)event;
string str = "Mouse Scroll detected, X: ";
str = str + tostr(e->getScrollX()) + " Y: " + tostr(e->getScrollY());
}
Создание пользовательских событий
Типы событий, описанных выше, определены системой, эти события (такие как касание экрана, нажатие клавиши и др.) обрабатываются системой автоматически. К тому же, вы можете сделать свое собственное событие, которое будет обрабатываться не системой, а вашим кодом.
_listener = EventListenerCustom::create("game_custom_event1", [=](EventCustom* event){
std::string str("Custom event 1 received, ");
char* buf = static_cast<char*>(event->getUserData());
str += buf;
str += " times";
statusLabel->setString(str.c_str());
});
_eventDispatcher->addEventListenerWithFixedPriority(_listener, 1);
Слушатель пользовательского события был определен с методом реакции и добавлен к диспетчеру событий. Как будет вызываться пользовательское событие? Смотрите далее:
static int count = 0;
++count;
char* buf[10];
sprintf(buf, "%d", count);
EventCustom event("game_custom_event1");
event.setUserData(buf);
_eventDispatcher->dispatchEvent(&event);
В примере выше, мы создали объект EventCustom и задали значение UserData. Затем, событие вызывается вручную, с помощью _eventDispatcher->dispatchEvent(&event). Это запускает событие, определенное ранее. Обработчик событий вызывается непосредственно, поэтому в качестве параметра UserData, может использоваться локальная переменная стека.
Регистрация событий в диспетчере
Зарегистрировать событие просто. Вот пример для слушателя событий касания:
// Добавляем слушателя
_eventDispatcher->addEventListenerWithSceneGraphPriority(listener1,
sprite1);
Важно отметить, что событие касания может быть зарегистрировано только на один объект. Если вам необходимо использовать один слушатель для множества объектов, для этого необходимо использовать clone().
// Добавляем слушателя
_eventDispatcher->addEventListenerWithSceneGraphPriority(listener1,
sprite1);
// Добавляем того же слушателя к другому объекту
_eventDispatcher->addEventListenerWithSceneGraphPriority(listener1->clone(),
sprite2);
_eventDispatcher->addEventListenerWithSceneGraphPriority(listener1->clone(),
sprite3);
Удаление событий из диспетчера
Мы можем его удалить из диспетчера, следующим методом:
_eventDispatcher->removeEventListener(listener);
Хотя они могут казаться особенными, встроенные Node-объекты используют диспетчер событий тем же самым методом, о котором мы говорили. Имеет смысл, не так ли? Возьмем Menu для примера. Когда вы кликаете по элементам меню, вызывается событие. Слушатели Node-объектов также просто удалить.
От переводчика
Вот мы и закончили с основными компонентами движка! Следующие на очереди главы, в основном, очень короткие, а большую часть в них занимает один лишь пример кода. Я считаю, что неуместно будет выкладывать их по одной на Хабр. Совесть мне этого не позволит. Миксовать темы тоже не очень хорошая идея.
Но все же, я планирую опубликовать еще сколько-нибудь уроков, которые уже не будут чистыми переводами. Ниже я оставлю опрос, чтобы узнать что вам будет интересно. Также, можете предлагать свои варианты в комментариях.
Only registered users can participate in poll. Log in, please.
Что еще вы хотите узнать о Cocos2d-x?
36% 3D9
40% Встроенная физика объектов10
48% Скриптинг12
64% Оптимизация игры16
25 users voted. 4 users abstained.