cocos2d uses The pyglet Event Framework to handle events.
you have emitters (instances of pyglet.event.EventDispatcher)
each emitter registers as much events as desired, each one identified by a string (the event name)
to act over events, you register listeners with the emitter. Essentially, you provide the emitter with a (<event name>, callable) and the emitter will call the callable when <event name> happens. Any number of listeners can register for a (emitter, <event name>) pair. A listener can register to any number of emitters.
example registration emitter events:
class Bunker(pyglet.event.EventDispatcher): ... def building_update(self, dt): ... if self.elapsed_time>self.building_time: self.dispatch_event('on_building_complete', self) def take_damage(self, damage): self.dispatch_event('on_building_under_attack', self) self.life -= damage if self.life<0: self.dispatch_event('on_building_destroyed', self) # following lines register the events that Bunker instances can emit Bunker.register_event_type('on_building_complete') Bunker.register_event_type('on_building_under_attack') Bunker.register_event_type('on_building_destroyed')Note that an event can carry zero, one or more arguments; here we send the instance emitting the event.
example registration listeners:
class Commander(object): def __init__(self, ...): self.buildings = [] ... def invest_resources(self): ... bunker = Bunker(...) # register to receive all events from object bunker bunker.push_handlers(self) self.buildings.append(bunker) ... # handlers for the events def on_building_complete(self, building): ... def on_building_under_attack(self, building): ... def on_building_destroyed(self, building): ...Note that the handlers accepts the parameters that the event carries. The listener registration here works as:
- we pass a class instance to push_handlers
- pyglet will look at methods in this class instance whose name match <event name> for any <event name> which the emitter registered, and then register ( <event name>, obj.event_name ) for each match.
With this style of listener registration you should be careful when registering for two emitters: if both emitters can generate 'on_cuack' events and you register:
emitter1.push_handlers(obj) emitter2.push_handlers(obj)then obj.on_cuack will be called by both emitters.
Another listener registration style is pushing explicit handlers:
bunker.push_handlers( self.on_building_complete, self.on_building_under_attack, self.on_building_destroyed )When you want a listener to stop receiving events from an emitter, you de-register the listener:
emitter.remove_handlers(...) # params as in push_handlers
Event propagation : The event is propagated to all handlers from the top of the emitter stack until one returns EVENT_HANDLED.
Besides using events to get user input ('on_key_press', 'on_mouse_move',...) or window status change ( 'on_activate', ... ) , you can use events to decouple the model from the view in your app. The game Tetrico, to be found in samples/tetrico is an example of this usage.
Cocos in general will not automatically handle listeners registration/de-registration, except for one special case: the emitter is director.window and the listener is a layer or scene Thus, for the general case, you must handle the push_handlers - remove_handlers thing by yourself. When your listener lives in a CocosNode, a good scheme is pushing handlers in the on_enter method and remove handlers in the on_exit method. This way, you are sure your handlers will not be called when the node is not in the active scene. Also, for custom events, it is a good practice to not use event names that director.window uses: that prevents unexpected double calls to the handler(s).
For the special case that emitter is director.window and the listeners are layers or scenes, cocos can handle the registration / de-registration:
When a scene becomes active it would walk the scene tree to allow layers autoregistering for director.window events It would not attempt to register itself as a listener except if the scene.enable_handlers is called. The walk begins in the scene, and passes only to layer class childs.
When prompted from the above walk, a layer whose class member is_event_handler has value True will register itself as a director.window listener The registration will be on the form:
director.window.push_handler(layer)so any method whose name matchs a <event name> will be registered as a listener.
When the scene becomes inactive ( by director.pop by example ), the matching walk calling:
director.window.remove_handlers(layer)will be issued.
Events generated by cocos itself:
- ( director, 'on_push' )
- ( director, 'on_pop' )
- ( director, 'on_resize' )
- ( director, 'on_cocos_resize')
Cocos registers a default key listener that provides handy functionality, see Default Handlers