"enterFrame" Runtime listeners to update game state?

The majority of my gameplay code resides in game.lua and this is a Composer scene.  Then I also have a good amount of code in player_maker.lua which relates more specifically to the player( ex:  lives remaining,  movement speed, etc).

My question:   What’s the best way to make my game.lua module aware that something happened to the player?  

For example, if the player lost a life and I updated that value in player_maker.lua, or if the player collided with some particular object and I want the game.lua module to know about, how should I be doing that?

Right now I am doing something like this in my game.lua:

-- INSIDE game.lua local player = player\_maker.create() local function updatePlayerLives() lives = player.lives if(player.lives == 0) then --do some stuff end end Runtime:addEventListener("enterFrame", updatePlayerLives)

Is this the best way to make game.lua aware that something changed in player_maker.lua?  Or is there a better alternative I should be using?   

Since the events I’m checking for do not happen often, it feels like a waste to be using a listener on every frame, but I’m not sure of another easy way to do it.

there are several patterns commonly used…

one is to give “player” a reference to game, (player.game=game, or some-such), then player can directly call into self.game.onPlayerDeath() (for example, where “self” in this context is “player”) if/as needed.  works fine though some don’t like such tight bonding between supposedly separate modules, so…

another is messaging (aka “events” in corona-speak), such that game “listens” to what player is “sending”: self:dispatchEvent({ name=“onPlayerDeath” }) for example.  if “game” is a scene instance, then this is easy, just add a listener for your own custom event; or if a lua table/class, then either wrap in a system.newEventListener() or use your own messaging system, in which case…

yet another is some higher-level “controller” class (which could also implement a messaging system) that is the “parent” of BOTH “game” and “player” as siblings, each child can call/message their parent/controller, but can’t call/message the other “sibling” directly, parent/controller would be responsible for inter-sibling relaying as appropriate.

Thanks for the quick reply, Dave.  

I’m trying to wrap my head around the 2nd method you mentioned, regarding self:dispatchEvent…  

I’m not real familiar with custom listeners, but do I add the listener in the game.lua and then call the dispatchEvent from the player module?   

Would you be able to include a code snippet from game.lua and player.lua just showing how that interaction happens?

imagine that this is in game.lua (doesn’t actually matter), set up some listeners:

local game = { onPlayerInjure = function(self,event) print("game knows player was injured", event.health) end, onPlayerDeath = function(self,event) print("game knows player died", event.health) end } Runtime:addEventListener("onPlayerInjure", game) Runtime:addEventListener("onPlayerDeath", game)

imagine this is in player.lua (doesn’t actually matter), causing those above events:

local player = { health=3, takeDamage = function(self,damage) self.health = self.health - damage Runtime:dispatchEvent({name="onPlayerInjure", health=self.health}) if (self.health \<= 0) then Runtime:dispatchEvent({name="onPlayerDeath", health=self.health}) end end }

and then “somewhere” (as long as “player” is in scope) demo it with:

player:takeDamage(1) --\> 2 injure player:takeDamage(1) --\> 1 injure player:takeDamage(1) --\> 0 injure AND death

if you “buy into” the event approach, just be aware that it will probably lead you all over the place…

-- just food for thought: in player:takeDamage() Runtime:dispatchEvent({name="onPlaySound", sound="ouch"}) -- assuming there's an audio sub-system somewhere listening for this -- just food for thought: in game:onPlayerInjure() Runtime:dispatchEvent({name="onUpdateHUD", health=event.health}) -- assuming there's a hud sub-system somewhere listening for this

etc.  once you start “decoupling” modules via events like this, it tends to go nuts.  initially that’s probably a GOOD THING, just be aware of crossing the line into absurdity.

consider the mythical audio sub-system:  you don’t even have to implement your audio sub-system yet, but you can sketch out the code in player anyway - the dispatched message will just be ignored until such time as “someone” bothers to listen for it.

it’s usually much more responsive than player.lua doing stuff like this:

-- typical hard-wired non-event approach: -- AudioSubSystem.lua might not even exist yet! local AudioSubSystem = require("framework.AudioSubSystem") -- and even if the module exists, the playSound method might not exist! -- or might have some other name, or exist with that name but used to hold a string (not fn), etc AudioSubSystem:playSound("ouch")

Thanks much for the great explanation!

Was able to get it working using your example.