Summary of all cocos2dx3. X events

Source: Internet
Author: User
An event system is a core component of software. From an elementary point of view, it is the design mode for interaction between modules in an application. From a general point of view, it is a component module of the software architecture. In modern software development, the operating system usually informs the application of some things, such as user input and insufficient memory, through some predefined events. However, we usually do not directly use system events. For example, elements in different areas of an interface may have different understandings of touch events. In some cases, we need to prioritize some logics, this requires repackaging of system events to cope with complex elements and logic on the interface. On the other hand, we need an event system to send messages in part of the application. For example, when an enemy enters the attack range, the system notifies the hero of shooting, and plays a death animation when the enemy's blood volume is less than 0. These require the game engine to provide a flexible Event System, which can manage distribution system events and use its simple management of custom events.

Cocos2d-x 3.0 centralizes all events into eventdispatcher, which not only improves the management and usage of system events such as touch, but also enables us to use it to process custom events. This chapter describes the relevant content. 5.1 to process an event, you must first define an event type. The event system always processes event subscription and distribution by type rather than instance, so that the same event can have multiple subscribers. An event is the base class of all events. It uses a string to represent the event type. Instead of using event directly, we should inherit from it to implement custom events. The event type is usually not a variable to ensure that the event instances of the same type have the same type, except eventcustom, which can specify different types during initialization, this is to simplify the compilation of event types. The following is the definition of the event class:

Class event
{
Protected:
Event (const STD: string & type );

Public:
Virtual ~ Event ();
Inline const STD: string & GetType () const {return _ type ;};
Inline void stoppropagation () {_ isstopped = true ;};
Inline bool isstopped () const {return _ isstopped ;};
Inline node * getcurrenttarget () {return _ currenttarget;}; protected:
Inline void setcurrenttarget (node * Target) {_ currenttarget = target ;}; STD: String _ type;
Bool _ isstopped;
Node * _ currenttarget; friend class eventdispatcher;}; in fact, the event member should only contain a string of the type, however, the distribution of some events in the Cocos2d-x, such as touch, may be related to the node hierarchy, so it also contains a way to get the associated element: getcurrenttarget (); and it is also the friend of eventdispatcher, this is to facilitate the distribution of touch and other events, which will be analyzed later in this chapter. An event instance is actually data in the event transfer process. It is constructed by the event trigger and passed to the event distributor, the event distributor notifies all subscribers that subscribe to this type of event based on its type, and passes it as a parameter to the subscriber. Because an event is an asynchronous communication mechanism, it usually does not have a callback, or even a type of event may not contain any subscriber, this requires the event trigger to pass the relevant context data to the receiver, the receiver can process the request correctly. For example, the eventtouch object contains the information of the touch point, so that the subscriber can process the logic. The event types that come with the Cocos2d-x engine include: eventtouch, eventkeyboard, eventacceleration, and eventcustom that makes it easier for developers to customize events. 5.2 The subscriber of an event is responsible for processing the event. Its members include the type of the subscribed event (this type should be the same as the type of the corresponding event), and a callback method is used to process the event. These two members should only be used by the event distributor (eventdispatcher), so they are defined as protected members and eventlistener is defined as the Friends of eventdispatcher:

Class eventlistener: Public object
{
Protected:
Eventlistener ();
Bool Init (const STD: string & T, STD: function <void (event *)> callback );

Public:
Virtual ~ Eventlistener ();
Virtual bool checkavaiable () = 0;
Virtual eventlistener * clone () = 0; protected:
STD: function <void (event *)> _ onevent; STD: String _ type;
Bool _ isregistered; friend class eventdispatcher;
Friend class node;}; in earlier versions of the Cocos2d-x, the subscriber was defined in an inherited manner, and the subscriber and the object dealing with the logic were the same entity, for example, cclayer implemented cctouchdelegate. In 3.0, eventlistener is defined as a variable. The advantage is that the processing method can be defined as a Lambda expression, which is an important aspect of C ++ 11 3.0 support, it changes the programming habits of using events, but brings the benefits of lambda expressions. programming is more flexible. You can even define an eventlistener variable in an eventlistener processing program. As opposed to the event type, subscribers in the Cocos2d-x include: eventlistenertouch, eventlistenerkeyboard, eventlisteneracceleration, and eventlistenercustom. 5.3 after the event workflow defines the event and subscriber, the application only needs to register a subscriber instance with the event distributor to be notified when the event occurs. In cocosd-X, it is responsible for event subscription, cancellation, and distribution of eventdispatcher. It is a singleton. Applications can obtain its instances through the eventdispatcher: getinstance () method. The following uses an example to demonstrate how events work. In this example, when collisionsystem detects a collision between two nodes, a collision event is triggered, hitsystem is one of the subscriber of a collision event. It will respond to the collision event and modify the enemy's life value:

Class collisionevent: public event
{
Public:
Static const char * collision_event_type;

Collisionevent (entity * l, entity * R );

Entity * getleft () {return _ left ;}
Entity * getright () {return _ Right ;}
PRIVATE:
Entity * _ left;
Entity * _ right;

}; The above code first adds a collision event class collisionevent, which inherits from the event and defines its type with a constant collision_event_type. As the data transmitted by events, collisionevent should pass the relevant context to the subscriber. Here, we need to pass two entity instances that are subject to a collision. The entity component system will be described in later sections of this book.

Class collisionlistener: Public eventlistener
{
Public:
Static collisionlistener * Create (STD: function <void (collisionevent *)> callback );
Virtual bool checkavaiable () override;
Virtual collisionlistener * clone () override;

Protected:
Collisionlistener ();
Bool Init (STD: function <void (collisionevent *)> callback );

STD: function <void (collisionevent *)> _ oncollisionevent;

}; Next, we need to define the subscriber. In the init () method of collisionlistener, we declare the type of the subscribe event. by viewing the implementation code of collisionlistener, we can see that it references the collision_event_type defined by collisionevent above.

Void hitsystem: Configure ()
{
Auto listener = collisionlistener: Create (
[This] (collisionevent * event ){
This-> hit (event );
});
Eventdispatcher: getinstance ()-> addeventlistenerwithfixedpriority (listener, 1 );
}

Then we need to register a subscriber with eventdispatcher. Hitsystem will respond to collision events, so we register with eventdispatcher during hitsystem initialization and pass a Lambda expression as the handler.

Void collisionsystem: Update (float DT)
{

If (Collide ()){
Collisionevent * event = new collisionevent (entity, collisionentity );
Eventdispatcher: getinstance ()-> dispatchevent (event );
} Finally, it is the program that triggers the event. Because collisionsystem is responsible for collision detection, it will notify eventdispatcher to distribute this collision event when detecting a collision between two nodes, and save the two entity that are in conflict as data in the event parameter. When eventdispatcher receives an event notification, it first searches for the subscribers that match the event parameter type. In this example, the type of collisionlistener is the same as that of collisionevent, therefore, the callback method in collisionlistener will be executed. Therefore, with eventdispatcher, We Can Customize various events and flexibly communicate with each module of the application, greatly simplifying event processing and reducing inter-module coupling. Of course, in general, you do not need to define every event like this. You can directly use eventcustom. Its constructor accepts a type parameter, so that the same eventcustom instance can distribute different types of events. Similarly, eventlistenercustom accepts a type parameter so that it can process different event types. 5.4 deep analysis of eventdispatcher through the previous study, we should initially learn how to use general events in Cocos2d-x. However, to use events more flexibly and skillfully, you also need to learn more. before further analyzing the eventdispatcher mechanism, let's summarize the special requirements for using events in games:
  1. Set the subscriber priority. A type of event may have multiple subscriber, so it is necessary to set the processing order. For example, when a collision event is completed, one subscriber is responsible for damage calculation, another subscriber may perform some UI operations, such as playing sound or particle effects. The priority of the former must be higher, because the latter's processing may depend on the calculation of the life value.
  2. Modify the priority of a subscriber.
  3. Stop the event distribution, so that subsequent subscribers do not have to process the event.
  4. Processing Event distribution based on the hierarchy of elements on the screen, rather than manually set priorities, is particularly important in the distribution of touch events.
With these goals, we will analyze how eventdispatcher achieves them and how we should use them in applications. First, eventdispatcher provides two methods to register a subscriber:

Void addeventlistenerwithscenegraphpriority (eventlistener * listener, node * node );

Void addeventlistenerwithfixedpriority (eventlistener * listener, int fixedpriority); the first method is to provide an associated node, so that event processing will determine the priority of distribution based on the node's draw order. The second option is to manually set a priority so that eventdispatcher directly determines the distribution order based on the priority. At the same time, the subscriber registered through the second method can also modify the priority by calling the setpriority () method. Secondly, how does eventdispatcher calculate the subscriber's priority based on the element's draw order? Inside the Cocos2d-x engine, each eventlistener is encapsulated as an eventlisteneritem struct:

Struct eventlisteneritem
{
Int fixedpriority;
Node * node;
Eventlistener * listener;
~ Eventlisteneritem ();

}; If the subscriber is associated with a node, the node member is assigned a value and the fixedpriority is set to 0. The listener variable is added to the subscriber list of the node. This setting will affect the subscriber's sorting. Find the sortalleventlisteneritemsfortype () method and summarize the sorting rules as follows:
  1. Subscriber whose fixedpriority is less than 0 is distributed. Smaller fixedpriority is, priority is given to subscriber.
  2. Distribute all Subscribers whose fixedpriority value is 0 and are not associated with the node.
  3. Distribute all subscriber associated with the node. The higher the eventpriority of the associated node (the more on the screen), the higher the priority.
  4. Distribute all Subscribers whose fixedpriority is greater than 0. Similarly, smaller fixedpriority gives priority to distribute.
There are two points to note: First, it doesn't make much sense to compare the priority of the two subscription methods. Generally, we should avoid mixing the two registration methods for subscribers of the same event type. Second, the eventpriority variable of node does not need to be concerned. If you set it to calculate the priority based on node, the engine will ensure that it is consistent with the draw sequence. If you are interested in this, you can see that the node class has a static variable _ globaleventpriorityindex. At the beginning of each frame, director will reset it to 0, and then in the visit () of the node () every time the draw () method is called, The updateeventpriorityindex () method is called. The method is as follows:

Inline void updateeventpriorityindex (){
_ Oldeventpriority = _ eventpriority;
_ Eventpriority = ++ _ globaleventpriorityindex;
If (_ oldeventpriority! = _ Eventpriority)
{
Setdirtyforalleventlisteners ();
}

}; Therefore, the value of the _ eventpriority variable of the first node to be drawn is 1, and the value of _ eventpriority of the second node is 2 ..., Once a new node is added or the old node is removed, these values are recalculated, but they can always be consistent with the draw sequence. Once the sequence number of a node is changed, the order of all subscriber associated with the node in eventdispatcher is recalculated to ensure that the processing order is consistent with the drawing order. When each event is processed, if the subscriber registers through a node Association, eventdispatcher temporarily saves the node associated with the current subscriber in the event, in this way, we can also get the node in the event handler, which is the getcurrenttarget () method we saw earlier. This is useful in some cases, because eventlistener can be associated with any node instance, and the object where the event processing code is located may not retain its reference. Finally, it is about the prohibition of event distribution. Because an event will be processed by multiple subscribers, the same event instance will be passed to multiple handlers, in this way, each handler can modify this common event instance. If one of the handlers calls the stoppropagation () method of event and sets _ isstopped to true, eventdispatcher stops distributing the event and subsequent subscribers will not receive Event Notifications. In addition, eventdispatcher also contains some code to ensure security, as well as methods such as sorting subscribers from new ones as needed, dispatchevent () the method also contains a parameter forcesortlisteners with the default value of false. It can be re-ordered to subscriber before the event is distributed, but it does not seem useful, because generally, factors that affect the priority, such as adding or removing nodes, directly modifying fixedpriority will lead to subscribers re-sorting, and there may be some special cases. 5.5 system events in Cocos2d-x for system events, some Cocos2d-x take special treatment, and some we can learn from some of the advanced techniques of using events, of course, the most important thing in actual use is to know how to use them and when they are triggered. 5.5.1 eventtouch touch is the most complex event in the Coco2d-x. to simplify the most commonly used single-point operations, it needs to distinguish between multiple points and single-point touch, for a single point, it also records some States throughout the touch operation. These complex situations make the distribution mechanism of the above learning not to be handled directly, so the Cocos2d-x has made special processing for the touch event, we can find a private dispatchtouchevent () method in eventdispatcher, it is specially used to handle touch events. Eventtouch events can be divided into two types: mouse clicks on a PC or Mac, and touch on a mobile device. The former has only one touch point. But the Cocos2d-x does not process right-click, but directly to the parent window or the system for processing. These events are captured by the implementer of the lower-level eglviewprotocol from the system and then encapsulated into the information format in the Cocos2d-x and passed to eventdispatcher. It is worth noting that in this encapsulation process, eglviewprotocol will adjust the position of the touch point according to the resolution scheme set by the program. At the same time in iOS platform by default, Cocos2d-x does not enable multi-point support, readers need to appcontroller. set in CPP: [_ glview setmultipletouchenabled: Yes]; first, find the eventtouch definition, which contains only two members. _ eventcode is used to display the status of the current touch event, which is in eventtouch: eventcode; _ touches ensures the information of the touch points in the current touch state:

Class eventtouch: public event
{
Public:
Enum class eventcode {
Began,
Moved,
Ended,
Cancelled
};

Eventcode geteventcode () {return _ eventcode ;};
STD: vector <touch *> gettouches () {return _ touches ;};

# If touch_perf_debug
Void seteventcode (eventcode) {_ eventcode = eventcode ;};
Void settouches (const STD: vector <touch *> & touches) {_ touches = touches ;};
# Endif
};

From the definition, we can also directly set the touch information to simulate system events for testing. Touch encapsulates the information of a touch point, from which the application can obtain a lot of useful information:

Class cc_dll touch: Public object
{
Public:
/** Position of the touch point in the OpenGL Coordinate System */
Point getlocation () const;
/** The touch point is located at the previous position in the OpenGL Coordinate System */
Point getpreviuslocation () const;
/** Start position of the touch point in the OpenGL Coordinate System */
Point getstartlocation () const;
/** Difference between the current position and the previous position in the OpenGL Coordinate System */
Point getdelta () const;
/** Position of the touch point in the screen Coordinate System */
Point getlocationinview () const;
/** The touch point is located at the previous position in the screen Coordinate System */
Point getpreviuslocationinview () const;
/** Start position of the touch point in the screen Coordinate System */
Point getstartlocationinview () const;

Int GETID () const {return _ id ;}

}; With this information, we can perform precise touch judgment in the program, for example, determining whether a certain area is located and whether a certain area is left at the end of the touch event, you can also restore the positions of some elements based on the displacement of the touch point when the canceled event occurs. Some practical examples will be analyzed later. In the dispatchtouchevent () method, we no longer need to worry about the subscriber's priority, because in this respect, the processing of touch events and other events is consistent. What needs special processing here is that the touch event must call different response methods of the subscriber according to different touch states. There is only one processing method different from that of other subscriber, the subscriber of a touch event must provide methods in each touch state:

Class eventlistenertouch: Public eventlistener
{
Public:
STD: function <bool (touch *, event *)> ontouchbegan;
STD: function <void (touch *, event *)> ontouchmoved;
STD: function <void (touch *, event *)> ontouchended;
STD: function <void (touch *, event *)> ontouchcancelled;

STD: function <void (const STD: vector <touch *> &, event *)> ontouchesbegan;
STD: function <void (const STD: vector <touch *> &, event *)> ontouchesmoved;
STD: function <void (const STD: vector <touch *> &, event *)> ontouchesended;
STD: function <void (const STD: vector <touch *> &, event *)> ontouchescancelled;

Void setswallowtouches (bool needswallow );

PRIVATE:

Bool _ needswallow;


Touch: dispatchmode _ dispatchmode;

}; First, the subscriber is divided into single-point and multi-point touch subscribers through touch: dispatchmode, you can also set setswallowtouches to determine whether to disable subsequent subscribers from processing a certain touch point. The association between eventlistener and node only leads to the opposite order of event distribution and rendering. For a touch event, it generally only needs to be processed once, in this case, eventdispatcher determines whether to continue distributing Based on the _ needswallow attribute. Based on the needs of processing these touch events, the logic of the dispatchtouchevent () method is clearer: first, find all single-touch subscribers, then, use each touch point to ask whether ontouchbegan needs to be processed. If necessary, save the touch point to the subscriber for subsequent methods such as move, end, And cencelled. If _ needswallow of the subscriber is set to true, the touch point will not be processed by any subscriber. Next, find all the remaining touch points after the above execution, find the subscribers of all multi-touch, and call each multi-touch method respectively. It is worth noting that if there are more than one touch point at the same time, the single-touch subscriber will execute multiple times, so if the player simultaneously clicks two fingers on one button, the button will be triggered twice. eventdispatcher does not guarantee that the subscriber of a single point event will only be clicked once. The program logic needs to implement state record. We can refer to the menu later to analyze the processing of this event, it uses menu: State to record the current status of the button. Finally, let's analyze the examples of using touch in the two engines, so that readers can understand the common Processing Methods of touch. 5.5.1.1 the layer supports system events. The layer is often used to organize elements based on the UI hierarchy, just like its name. In fact, its main purpose is to facilitate the use of system events, touch, buttons, gravity acceleration and other events. By providing subscribers to construct these events, register and unregister these subscribers with eventdispatcher to simplify our use of system events. In addition, it also added support for physical Engine Integration in 3.0, which will be learned in the following sections. Layer registers a subscriber to eventdispatcher in an associated manner for all events, that is, the priority of the subscriber to be processed depends on its UI level. To use a system event, you only need to call the setxxxenabled () method, and then rewrite the corresponding event processing method. Of course, all system events are disabled by default, and the touch events are set to multi-touch by default. The following uses a touch event as an example to describe how to use a touch event in a layer:

Bool helloworld: Init ()
{
If (! Cclayer: Init ()){
Return false;
}

Settouchmode (touch: dispatchmode: one_by_one );
Settouchenabled (true );

Return true;

}

Class helloworld: Public cocos2d: Layer
{
Public:
Virtual bool ontouchbegan (touch * touch, event * event );
Virtual void ontouchmoved (touch * touch, event * event );
Virtual void ontouchended (touch * touch, event * event );

Virtual void ontouchcancelled (touch * touch, event * event);} in the preceding example, we first call the settouchenabled () method declaration after the init of the layer to process single-point touch events, then, we overwrite the four processing methods required for single-point touch. In fact, after each system event processing is enabled, layer registers a subscriber with eventdispatcher and declares the processing method as a virtual method for the subclass to override. When the event is disabled or the element is removed, the user is logged out of eventdispatcher. This greatly simplifies the use of system events. 5.5.1.2 touch processing in the menu is commonly used in some controls, such as Button clicking and table dragging. The most important part in its development is click determination. In addition, you need to pay attention to the status determination mentioned above for the button to avoid multiple clicks. The Cocos2d-x provides us with a commonly used GUI control-menu for explicit buttons. By analyzing the menu, we can fully understand the processing of touch. Menu inherits from layerrgb, which makes it easy to use touch events. By viewing the source code of the menu, you can know that the menu registers a single touch event. Of course, Menu also implements management of multiple menuitems. Here we are concerned about how to use the location information of touch points.

Bool menu: ontouchbegan (touch * touch, event * event)
{
If (_ state! = Menu: State: Waiting |! _ Visible |! _ Enabled ){
Return false;
}

For (node * c = This-> _ parent; C! = NULL; C = C-> getparent ()){
If (c-> isvisible () = false ){
Return false;
}
}

_ Selecteditem = This-> itemfortouch (touch );
If (_ selecteditem ){
_ State = menu: State: tracking_touch;
_ Selecteditem-> selected ();

Return true;
}

Return false;

}

In this example, we can see three interesting information:

Www.sumdabke.com

First, menu uses a menu: state variable to prevent multiple clicks at the same time. If menu starts to process a touch point, it sets _ state to menu: State :: tracking_touch. Second, it looks up on the UI tree until the root node and checks whether the node is being drawn. This is because although you can set the visibility of a node by setting visible, its subnodes do not directly know whether they are hidden or explicit. Therefore, you need to traverse up to the root node to make a judgment. At the same time, although eventdispatcher can determine the distribution order based on the node UI level, it is not responsible for checking the node visibility, because the element here is only used to calculate the distribution order, in addition, not all events calculate the priority based on the element level. Therefore, a common problem during development is that a node is hidden through its parent level, but it still receives touch events. Finally, the menu uses the itemfortouch () method to determine the click:

Menuitem * menu: itemfortouch (touch * Touch)
{
Point touchlocation = touch-> getlocation ();

If (_ Children & _ Children-> count ()> 0)
{
Object * pobject = NULL;
Ccarray_foreach_reverse (_ children, pobject)
{
Menuitem * child = dynamic_cast <menuitem *> (pobject );
If (Child & Child-> isvisible () & Child-> isenabled ())
{
Point local = Child-> converttonodespace (touchlocation );
Rect r = Child-> rect ();
R. Origin = point: zero;

If (R. containspoint (local )){
Return child;
}
}
}
}

Return NULL;

} Here is the general method for clicking and determining. First, we use the getlocation () method to retrieve the world coordinates of the touch point in the OpenGL coordinate system, and then convert it to the local coordinate system of the node, finally, check whether the node falls into the region based on the node size. So far, we have learned all about touch.

5.5.2 eventkeyboard

Www.sumdabke.com

The keyboard input event is relatively simple. It captures a key action. Its parameters include the pressed key _ keycode and the two States _ ispressed:

Class eventkeyboard: public event
{
Eventkeyboard (keycode, bool ispressed)
: Event (event_type)
, _ Keycode (keycode)
, _ Ispressed (ispressed)
{};

PRIVATE:
Keycode _ keycode;
Bool _ ispressed;

Friend class eventlistenerkeyboard;

};

Class eventlistenerkeyboard: Public eventlistener
{
Public:
STD: function <void (eventkeyboard: keycode, event * event)> onkeypressed;
STD: function <void (eventkeyboard: keycode, event * event)> onkeyreleased;

}; What's interesting is the call method of the two processing methods. Generally, an event has only one processing method. We still remember the _ onevent variable defined by eventlistener, which is directly called by eventdispatcher, here, eventlistenerkeyboard performs special processing:

Bool eventlistenerkeyboard: Init ()
{
Auto listener = [this] (event * event ){
Auto keyboardevent = static_cast <eventkeyboard *> (event );
If (keyboardevent-> _ ispressed ){
If (onkeypressed! = Nullptr)
Onkeypressed (keyboardevent-> _ keycode, event );
}
Else {
If (onkeyreleased! = Nullptr)
Onkeyreleased (keyboardevent-> _ keycode, event );
}
};

Www.sumdabke.com

If (eventlistener: Init (eventkeyboard: event_type, listener )){
Return true;
}

Return false;

}

We can see that the eventlistenerkeyboard repacks the listener. It can be seen that the subscriber instance defined in our program is not necessarily the instance referenced in the final eventdispatcher. What's more interesting here is that the subscriber contains the subscriber. This is the use of method pointers for event distribution, rather than inheriting the benefits of implementing a certain delegate.


Add www.sundabke.com



Summary of all cocos2dx3. X events

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.