Skip to content

Latest commit

 

History

History
280 lines (222 loc) · 9.45 KB

8.md

File metadata and controls

280 lines (222 loc) · 9.45 KB

Chapter 8: Event Dispatcher

What is the EventDispatch mechanism?

EventDispatch is a mechanism for responding to user events.

The basics:

  • Event listeners encapsulate your event processing code.
  • Event dispatcher notifies listeners of user events.
  • Event objects contain information about the event.

5 types of event listeners.

EventListenerTouch - responds to touch events

EventListenerKeyboard - responds to keyboard events

EventListenerAcceleration - responds to accelerometer events

EventListenMouse - responds to mouse events

EventListenerCustom - responds to custom events

FixedPriority vs SceneGraphPriority

The EventDispatcher uses priorities to decide which listeners get delivered an event first.

FixedPriority is an integer value. Event listeners with lower Priority values get to process events before event listeners with higher Priority values.

SceneGraphPriority is a pointer to a Node. Event listeners whose Nodes have higher z-order values (that is, are drawn on top) receive events before event listeners whose Nodes have lower Z order values (that is, are drawn below). This ensures that touch events, for example, get delivered front-to-back, as you would expect.

Remember Chapter 2? Where we talked about the scene graph and we talked about this diagram?

XXX: Check order... I goes first, then H

Well, when use SceneGraphPriority you are actually walking this above tree backwards... H, I, G, F, E, D, C, B, A. If an event is triggered, H would take a look and either swallow it (more on this below) or let is pass through to I. Same thing, I will either consume it or let is pass thought to G.. and so on until the event either swallowed or it does not get answered.

XXX: Add reverse Scene Graph screenshot

XXX: Keep using the scene graph in the sample... assume that each node is a consumer. how the events are propagated.

Touch Events

Touch events are the most important event in mobile gaming. They are easy to create and provide versatile functionality. Let's make sure we know what a touch event is. When you touch the screen of your mobile device, it accepts the touch, looks at where you touched and decides what you touched. Your touch is then answered. It is possible that what you touched might not be the responding object but perhaps something underneath it. Touch events are usually assigned a priority and the event with the highest priority is the one that answers. Here is how you create a basic touch event listener:

//  Create a "one by one" touch event listener
// (processes one touch at a time)
auto listener1 = EventListenerTouchOneByOne::create();

// trigger when you push down
listener1->onTouchBegan = [](Touch* touch, Event* event){
    // your code
};

// trigger when moving touch
listener1->onTouchMoved = [](Touch* touch, Event* event){
    // your code
};

// trigger when you let up
listener1->onTouchEnded = [=](Touch* touch, Event* event){
    // your code
};

// Add listener
_eventDispatcher->addEventListenerWithSceneGraphPriority(listener1, this);

As you can see there are 3 distinct events that you can act upon when using a touch event listener. They each have a distinct time in which they are called.

onTouchBegan is triggered when you press down.

onTouchMoved is triggered if you move the object around while still pressing down.

onTouchEnded is triggered when you let up on the touch.

Swallowing Events

When you have a listener and you want an object to accept the event it was given you must swallow it. In other words you comsume it so that it doesn't get passed to other objects in highest to lowest priority. This is easy to do.

// When "swallow touches" is true, then returning 'true' from the
// onTouchBegan method will "swallow" the touch event, preventing
// other listeners from using it.
listener1->setSwallowTouches(true);

// you should also return true in onTouchBegan()

listener1->onTouchBegan = [](Touch* touch, Event* event){
    // your code

    return true;
};

Creating a keyboard event

For dekstop games, you might want find using keyboard mechanics useful. Cocos2d-x supports keyboard events. Just like with touch events above, keyboard events are easy to create.

// creating a keyboard event listener
auto listener = EventListenerKeyboard::create();
listener->onKeyPressed = CC_CALLBACK_2(KeyboardTest::onKeyPressed, this);
listener->onKeyReleased = CC_CALLBACK_2(KeyboardTest::onKeyReleased, this);

_eventDispatcher->addEventListenerWithSceneGraphPriority(listener, this);

// Implementation of the keyboard event callback function prototype
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);
}

Creating an accelerometer event

Some mobile devices come equipped with an accelerometer. An accelerometer is a sensor that measures g-force as well as changes in direction. A use case would be needing to move your phone back and forth, perhaps to simulate a balancing act. Cocos2d-x also supports these events and creating them is simple. Before using accelerometer events, you need to enable them on the device:

Device::setAccelerometerEnabled(true);
// creating an accelerometer event
auto listener = EventListenerAcceleration::create(CC_CALLBACK_2(
AccelerometerTest::onAcceleration, this));

_eventDispatcher->addEventListenerWithSceneGraphPriority(listener, this);

// Implementation of the accelerometer callback function prototype
void AccelerometerTest::onAcceleration(Acceleration* acc, Event* event)
{
    //  Processing logic here
}

Creating a mouse event

As it always has Cocos2d-x supports mouse events.

_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());
    // ...
}

Creating a custom event listener

XXX: REview the sample code

The event types above are defined by the system, and the events are triggered by the system automatically. In addition, you can make your own custom events which are triggered by your code and not the system. These are called Custom Events. It is easy to use a lambda to house the functionality of these events.

_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 = new char[10];
sprintf(buf, "%d", count);

// create the custom event
EventCustom event("game_custom_event1");

// pass the needed data to it
event.setUserData(buf);

// dispatch the event!
_eventDispatcher->dispatchEvent(&event);

CC_SAFE_DELETE_ARRAY(buf);

The above example creates an EventCustom object and sets its UserData. It is then dispatched manually with _eventDispatcher->dispatchEvent(&event);. This triggers the event handler for your custom event.

Registering event with the dispatcher

It is easy to register an event with the Event Dispatcher. Taking the sample touch event listener from above:

// Add listener
_eventDispatcher->addEventListenerWithSceneGraphPriority(listener1,
sprite1);

It is important to note that a touch event can only be registered once per object. If you need to use the same listener for multiple objects you should use clone().

// Add listener
_eventDispatcher->addEventListenerWithSceneGraphPriority(listener1,
sprite1);

// Add the same listener to multiple objects.
_eventDispatcher->addEventListenerWithSceneGraphPriority(listener1->clone(),
 sprite2);

_eventDispatcher->addEventListenerWithSceneGraphPriority(listener1->clone(),
 sprite3);

Removing events from the dispatcher

An added listener can be removed with following method:

_eventDispatcher->removeEventListener(listener);

Although they may seem special, built-in Node objects use the event dispatcher in the same way we have talked out. Makes senese right? Take Menu for an example. When you have a Menu with MenuItems when you click them you are dispatching a event. You can also removeEventListener() on built-in Node objects.