Here is an example with some of the ingredients you want.
Events are used for drawing various objects, with collisions, delays.
[EDIT] i’ve added a couple buttons and a quiet state to show how to trigger a global status (like your tuto / not tuto). You can see that all the state impacts are resolved locall by each object.
[EDIT] i’ve changed the shout mechanism so teach shout generates next (you wanted an sequence of events in a given order).
[EDIT] modified again: you can tap the ball, so you see the touched events too.
--# EventMngr
-- ############## START of EVENT MANAGER ##################
-- @tnlogy & @JMV38 & @Briarfox
-- example of usage:
-- EventMngr:extend(evMngr) -- extend an existing table with event manager funcs
-- evMngr:on("touch",func) -- register func() to fire on "touch" event
-- evMngr:on("touch", obj.func, obj) -- register obj:func() to fire on "touch" event
-- evMngr:trigger("touch",10,50) -- fires func(10,50) and obj:func(10,50)
-- evMngr:off("touch", func) -- unregister func()
-- evMngr:off("touch", obj.func, obj) -- unregister obj:func()
-- evMngr:off("touch") -- unregister all "touch" listeners
-- evMngr:off(obj.func) -- unregister all listeners with obj.func
-- evMngr:off(obj) -- unregister events with obj listening
-- "all" captures all events and passes the event name as the first param:
-- evMngr:on("all", func)
EventMngr = {}
local fifo = true -- first in (to register) first out (to be triggered)
function EventMngr:on(eventName, fn, obj)
if not self.events then self.events = {} end -- init event table if does not exist
-- if not self.events[eventName] then self.events[eventName] = {} end -- init this event name
if not self.events[eventName] then self.events[eventName] = {} end -- init this event name
local new = true -- confirm it is a new request
for i,fa in ipairs(self.events[eventName]) do
if fa.func == fn and fa.obj == obj then new = false end
end
local p -- insertion point in the table
if new then
if fifo then p = #self.events[eventName] +1 else p = 1 ; fifo=true end
local listener = {func = fn, obj = obj }
table.insert(self.events[eventName], p, listener)
end
return self
end
function EventMngr:executeNextCallBeforeOthers()
fifo = false
end
function EventMngr:off(nameOrFnOrObj, fn, obj)
local name
local fn,obj = fn,obj -- manage the case when they are nil
local firstType = type(nameOrFnOrObj)
local request
if firstType == "string" or firstType == "number" then
name = nameOrFnOrObj
if name == "all" then request = "remove all events"
elseif fn == nil then request = "remove all instances of this event"
else request = "remove this event" end
elseif firstType == "function" then
fn = nameOrFnOrObj
request = "remove all events with this function"
else
obj = nameOrFnOrObj
request = "remove all events with this object"
end
if request == "remove all instances of this event" then
self.events[name] = nil
elseif request == "remove all events" then
self.events = {}
else
local evs = self.events -- go through all events ...
if name then evs = {evs[name]} end -- ... or through 1 event only
for eventName,fns in pairs(evs) do
local n = #fns
for i=0,n-1 do
local j = n-i -- go backward because of remove, ipairs not suitable
local f = fns[j]
local match
if request == "remove this event"
then match=(f.func==fn and f.obj==obj)
elseif request == "remove all events with this function"
then match=(f.func==fn)
elseif request == "remove all events with this object"
then match=(f.obj==obj)
end
if match then
table.remove(fns,j)
end
end
end
end
return self
end
function EventMngr:trigger(name, ...)
self.lastTrigger = name
local evs = (self.events and self.events[name]) or {}
for i,fa in ipairs(evs) do
local func,obj = fa.func, fa.obj
if obj then func(obj,...)
else func(...) end
end
--trigger all
local evs = (self.events and self.events["all"]) or {}
for i,fa in ipairs(evs) do
local func,obj = fa.func, fa.obj
if obj then func(obj,name,...)
else func(name,...) end
end
end
-- to transform a table into an event manager
function EventMngr:extend(target)
for k, v in pairs(self) do
if type(v) == "function" and v ~= EventMngr.extend
then target[k] = v
end
end
return target
end
-- ############## END of EVENT MANAGER ##################
--# Noise
Noise = class()
EventMngr:extend(Noise)
function Noise:init(b)
self:setSilence(b)
end
function Noise:setSilence(b)
self.silence = b
self:trigger("silence",self.silence)
end
Noise:init(false)
--# World
World = class()
EventMngr:extend(World)
function World:init()
end
function World:draw()
self:trigger("draw")
end
function World:touched(touch)
self:trigger("touched", touch)
end
--# Message
Message = class()
local enabled = true
Noise:on("silence",function(b) enabled = not b end)
function Message:init(txt,x,y,t,size)
if not enabled then return end
self.x, self.y, self.t, self.txt, self.size = x,y,t,txt, (size or 50)
World:on("draw",self.draw,self)
tween.delay(self.t,function() World:off(self) end)
end
function Message:draw()
fill(255, 0, 0, 255)
fontSize(self.size )
text(self.txt, self.x, self.y)
end
--# Edge
Edge = class()
EventMngr:extend(Edge)
function Edge:init(data)
if data.x then
local x = data.x
self.pos0 = vec2(x,0)
self.pos1 = vec2(x,HEIGHT)
elseif data.y then
local y = data.y
self.pos0 = vec2(0, y)
self.pos1 = vec2(WIDTH, y)
else
error("please define x or y")
end
self.body = physics.body(EDGE, self.pos0, self.pos1)
self.body.info = self
-- events
World:on("draw",self.draw,self)
World:executeNextCallBeforeOthers()
World:on("touched",self.touched,self)
end
function Edge:draw()
fill(223, 223, 223, 255)
line(self.pos0.x, self.pos0.y, self.pos1.x, self.pos1.y)
end
function Edge:touched(touch)
end
--# Ball
Ball = class()
function Ball:init(x,y,r)
self.x = x
self.y = y
self.r = r
self.body = physics.body(CIRCLE, self.r)
self.body.x = self.x
self.body.y = self.y
self.body.sleepingAllowed = false
self.body.gravityScale = 0
self.body.linearVelocity = vec2(400,200)
self.body.linearDamping = 0
self.body.angularDamping = 0
self.body.friction = 0
self.body.restitution = 1
self.body.info = self
-- events
World:on("draw",self.draw,self)
World:executeNextCallBeforeOthers()
World:on("touched",self.touched,self)
print ("try to tap the ball")
end
function Ball:draw()
local pos = self.body.position
self.x, self.y = pos.x, pos.y
fill(223, 223, 223, 255)
ellipse(self.x,self.y,self.r*2)
end
function Ball:shout(size)
self.soundSize = size or self.soundSize or 50
-- this one
Message("A",self.x,self.y,1,size)
-- next one
local newSize = self.soundSize * 0.9
if newSize > 20 then
tween.delay(0.05,function()self:shout(newSize)end)
else
collectgarbage()
end
end
function Ball:collide(c)
self:shout(50)
end
local abs = math.abs
function Ball:touched(t)
if abs(self.x-t.x)<self.r and abs(self.x-t.x)<self.r then
if t.state == BEGAN then
Message("Outch!",self.x,self.y,1,50)
end
end
end
--# Main
-- eventsExample
-- Use this function to perform your initial setup
function setup()
e1 = Edge({x=0})
e2 = Edge({x=WIDTH})
e3 = Edge({y=0})
e4 = Edge({y=HEIGHT})
b = Ball(WIDTH/2, HEIGHT/2, 50)
parameter.action("silence",function() Noise:setSilence(true) end)
parameter.action("loud",function() Noise:setSilence(false) end)
end
-- This function gets called once every frame
function draw()
background(40, 40, 50)
strokeWidth(5)
World:draw()
end
function touched(t)
World:touched(t)
end
function collide(c)
if c.state == BEGAN then
fA = c.bodyA.info.collide
if fA then fA(c.bodyA.info, c) end
fB = c.bodyB.info.collide
if fB then fB(c.bodyB.info, c) end
end
end