Event systems

It seems like there are a bunch of different examples that everyone has created for how event systems should be implemented. What’s the general format?

Every time the draw function gets called, a for() loop is triggered which runs thru a table of registered events and sends them to the event targets? I’m contemplating writing my own, for the practice of it, but I know there is a standard architecture expectation for these things. So, what’s the basic design for how event messages get sent to objects?

Maybe the next update should include a generic event model, like how touch() works. since you’ve got touch.BEGAN, touch.MOVING, and touch.ENDED, you could do something similar with generic events. Event.REGISTER, Event.UNREGISTER, Event.FIRE.

Me and @Jmv38 compare our event handlers here:

http://codea.io/talk/discussion/6149/organising-larger-projects-a-simple-game-state-engine-ed-with-event-handler

I’ll post an updated version of mine when I get back to my iPad.

It works really well for some applications, but it isn’t a silver bullet. Lately I’ve been stripping certain event calls out of my programmes, so that they just have a few core events (ie draw3D draw2D singleTap reorient). There is a sense in which it can just become a glorified draw loop. Even that can be useful though.

Here’s my updated version. I borrowed prioritisation and event “consume” (high priority listeners prevent lower priority callbacks from being called) from @Jmv38 's version

Event = class()
--[[
v 1.2 Events now indexed in an array, not a hash table, allowing them to have an order, priorities, and for high priority listeners to "consume" the dispatched event, preventing lower priority callbacks from being called. To do this, have the callback of a high priority event return true.

very simple event handler, adapted from Cargobot. Event can be made a superclass of the scenes in the game, so that events have a scope,like they do in Corona.
You could just make it a global events manager though.

syntax:
-------
scene:subscribe(event, listener, callback, priority) 
      priority is optional. Either omit it (for default #events+1 insertion) or set it to 1 for high priority events.
      if callback function returns true then it will "consume" the dispatch. Use together with priority 1 events
scene:unsubscribe(event, listener) --listener optional
scene:dispatch(event, arguments)

examples:
---------
scene:subscribe("catMoves", self, self.chase, 1) 
    --a bug subscribes to event "catMoves", with optional first priority
scene:dispatch("catMoves", self.pos) 
    --broadcast cat position from hero class to current scene
self:subscribe("tap", self, self.touched) 
    --a scene subscribes itself to a system event, note repetition of self

notes:
------
the callback is defined in the class in the usual way, ie with a colon
"Bug:chase(arg)", but is given as a callback argument with a period, 
and without brackets or arguments "self.chase"

]]

function Event:init()
    self.events={}
    print("Scene "..self.id.." created\
"..string.rep("=",34)) 
end

function Event:subscribe(event, listener, callback, pos)
    if not self.events[event] then
        self.events[event]={} --create event
        --print ("Event "..event.." created in "..self.id.."\
"..string.rep("-",34))
    end
    local pos=pos or #self.events[event]+1
    table.insert(self.events[event], pos, {li=listener, cb=callback}) --add listener. 
    --print (listener.id.." is subscribed to "..event) 
end

function Event:unsubscribe(event, listener) --listener optional
    if listener then
        for i=1,#self.events[event] do
            if self.events[event][i].li==listener then
                -- print (listener.id.." #"..i..", unsubscribed from "..event) 
                table.remove(self.events[event], i) --unsubscribe one listener           
                return
            end     
        end
    else
        self.events[event]=nil --unsubscribe all listeners
    end
end

function Event:dispatch(event,...)
    if not self.events[event] then
      --  print ("no subscribers to "..event) --error message for debug purposes
        return
    end
    for i,v in ipairs(self.events[event]) do --has to be ipairs because objs can get removed within the draw loop. if count backwards, can no longer set priority 1
       -- local v=self.events[event][i]
        if v.cb(v.li, unpack(arg)) then return end --return true to halt the dispatch
    end
end