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.