Some help needed creating external module

Hi all,

In the game I’m currently writing I’d like to break some functions out into a separate external module (mainly so that I can re-use it in other projects), but I now realise that I have no clue how to go about writing an external module.

The module is to display messages on the screen - which in itself is really easy.  However I’d like to be able to queue the messages up so that they appear in order, and to be able to specify how long they appear for using something like 

local message="This is a test" messageBoard (message, 4000)

so that once any current messages in the queue have been displayed and removed, this message would then appear for 4 seconds.

I hope this makes some kind of sense (it does in my head).

many thanks in advance for any help.

Here’s an example using one of Roaming Gamers games he produced for the hangout…

In your programs, you can use just a few lines to call the function.

You can then ‘require’ the module in other apps to reuse the same routine. Eventually you can build yourself a library. (A bit like Ed did with SSK)

Don’t forget you can also return values from the functions if needed.

In the main line, it uses this code…

---------------------------------------------------------------------- -- Sound ---------------------------------------------------------------------- local soundMgr = require "scripts.soundMgr" --soundMgr.init() --soundMgr.enableSFX( true ) --soundMgr.playSoundTrack( "sounds/music/8bit Dungeon Level.mp3" )  

In a module (lua program) called soundMgr.lua, it holds this code (verbatim)

-- ============================================================= -- Copyright Roaming Gamer, LLC. 2009-2015 -- ============================================================= -- This content produced for Corona Geek Hangouts audience. -- You may use any and all contents in this example to make a game or app. -- ============================================================= local audio = require "audio" local common = require "scripts.common" local public = {} local getTimer = system.getTimer -- Local storage for handles to sound files -- local effects = {} local minTime = {} minTime.zombie1 = 2500 minTime.zombie2 = 5000 minTime.zombie3 = 5000 minTime.archer = 2000 minTime.bowfire = 150 local lastTime = {} local altVolume = {} altVolume.zombie2 = 0.25 altVolume.zombie3 = 0.25 altVolume.died = 0.5 altVolume.nextlevel = 0.5 -- Sound Effects Enable -- local sfxEn = false public.enableSFX = function( enable ) sfxEn = enable end -- Sound Effect EVENT listener -- local function onSFX( event ) local sfx = effects[event.sfx] if( not sfx ) then return end if( not sfxEn ) then return end local curTime = getTimer() print(curTime, minTime[event.sfx], lastTime[event.sfx] ) if( minTime[event.sfx] and lastTime[event.sfx] ) then if( curTime - lastTime[event.sfx] \< minTime[event.sfx] ) then return end end lastTime[event.sfx] = curTime local channel = audio.findFreeChannel( 2 ) --table.dump(event) if( channel ) then audio.setVolume( altVolume[event.sfx] or 1, { channel = channel } ) audio.play( sfx, { channel = channel } ) end end; listen( "onSFX", onSFX ) function public.init() effects["coin1"] = audio.loadSound("sounds/sfx/Pickup\_Coin.wav") effects["coin2"] = audio.loadSound("sounds/sfx/Pickup\_Coin2.wav") effects["coin3"] = audio.loadSound("sounds/sfx/Pickup\_Coin3.wav") effects["zombie1"] = audio.loadSound("sounds/sfx/Zombie Kill You.wav") effects["zombie2"] = audio.loadSound("sounds/sfx/Zombie Attack Walk.wav") effects["zombie3"] = audio.loadSound("sounds/sfx/Zombie Moan.wav") effects["archer"] = audio.loadSound("sounds/sfx/Archer.wav") effects["bowfire"] = audio.loadSound("sounds/sfx/Bow Fire.wav") effects["died"] = audio.loadSound("sounds/sfx/died.wav") effects["gem"] = audio.loadSound("sounds/sfx/gem.wav") effects["nextlevel"] = audio.loadSound("sounds/sfx/nextlevel.wav") end -- Set the sound track file -- local firstPlay = true public.playSoundTrack = function( path ) if( firstPlay ) then firstPlay = false local soundTrack = audio.loadStream( path ) audio.play( soundTrack, { channel=1, loops=-1, fadein = 3000 } ) else audio.resume( 1 ) end end public.pauseSoundTrack = function( ) audio.pause( 1 ) end return public

This will be easy to do if you create a message queue based on a table.  

In a module declare a function such as addMessage(msg, time).  This function will add a new row to an internal table holding the messages.

Also in the module add an enterFrame() event that keeps track of the current message and the time it was displayed.  If the queue is empty it does nothing.  If the #messages > current message then it has a new message to display and it should show it.  You then have 2 choices, set a timer to remove the message after time has passed or keep track of the current time and hide the current message if its time has expired.  increment current message and check for any new messages.

If you are going to have many thousands of messages you might need to clear down the message queue occasionally.

Return a reference to the module and require whenever it’s needed.

Here is the basic structure and you just add the logic

local messageQueue = {} function&nbsp;messageQueue:addMessage(msg, time) &nbsp; --add to queue end function messageQueue:processQueue(event) &nbsp; --process the message queue here end Runtime:addEventListener("onFrame", processQueue) return&nbsp;messageQueue

then in another module

local messageQueue = require("messageQueue") --show message for 10s messageQueue:addMessage("this is a message", 10)

Hi guys,

Sorry for the very late reply to this.

I took on board your advise and started to write the module but then got stuck and the mundane world took over and left me with no time to work on it.

Anyway…

This is what I have so far:

messages.lua

local messageQueue = {} local queue={} messageQueue.queue = queue local centerX = display.actualContentWidth\*0.5 local centerY = display.actualContentHeight\*0.5 local w = display.actualContentWidth local working = false local messageFont = native.systemFont local messageDisplay = display.newText ( " ", centerX, centerY, messageFont, 64 ) messageDisplay:setFillColor(1) messageDisplay.alpha=0 function messageQueue.hideMessage() print("HIDING MESSAGE") local m=queue[1] if m.fadeOut then local fadeOut = transition.to(messageDisplay, {alpha=0, time=500}) else messageDisplay.alpha=0 end end function messageQueue.showMessage() print("SHOWING MESSAGE") local m=queue[1] messageDisplay.text = m.message if m.fadeIn then local fadeIn = transition.to(messageDisplay, {alpha=1, time=500}) else messageDisplay.alpha=1 end timer.performWithDelay(m.displayTime, messageQueue.hideMessage() ) end function messageQueue.processQueue() isWorking = true if #queue \< 1 then isWorking = false return false else messageQueue.showMessage() end end function messageQueue:addMessage(msg, time, fadeIn, fadeOut) local newMessage={} newMessage.message. = msg newMessage.displayTime = time newMessage.fadeIn. = fadeIn newMessage.fadeOut. = fadeOut table.insert( queue, newMessage ) if not working then self.processQueue() end return end return messageQueue

and it is accessed like so:

main.lua

local messageQueue = require ( "messages" ) local f = 1 local tmpMsg = "This is message "..tostring(f) messageQueue:addMessage(tmpMsg, 8000, true, true) -- addMessage( message to be display, time to be displayed for, fadeIn? (boolean), fadeOut? (boolean)

The issue I’m having is that in the showMessage function if I comment out the timer then the message shows up as expected (either with our without fading in as specified).

But, if I uncomment the timer so that it should then go to the hideMessage function after the given time, what actually happens is that the message never appears and the console shows both the ‘show’ and ‘hide’ print statements.

I’m probably missing something obvious but I can’t for the life of me figure it out.

(apologies if the code isn’t brilliant, this is my first try at writing an external module).

Many thanks

Chris

Change line 34 from

timer.performWithDelay(m.displayTime, messageQueue.hideMessage() )

to

timer.performWithDelay(m.displayTime, function() messageQueue.hideMessage() end)

what is the result?

Perfect.  That is spot on.  many many thanks.

Just out of interest though, why would wrapping it in an anonymous function make a difference?

I don’t really know the inner workings of this but…  if you were simply calling a local function hideMessage it would of been fine but when the function resides in another class/module or you are passing parameters to it, i.e. using () then the function needs to be anonymous to stop it being executed immediately.

the former calls the function, and passes its return value (nil) to timer.performWithDelay, and no function = no timer execution

the latter passes a function definition to timer.performWithDelay, “wrapping” your original function call for later execution

given that you don’t need a “self” parameter, you could also just do

timer.performWithDelay(m.displayTime, messageQueue.hideMessage )

note the lack of parenthesis - this isn’t a call, it’s passing the reference to the function.  that’ll work, even though it’s in another module - the function’s closure environment will “contain” all of the module-local variables that it needs.

[ADDED] at the risk of getting too esoteric…  if you did need a “self” call (on a specific instance, for example:  aMessenger:hideMessage()) then you’d have to use the anonymous function style (or equivalent, strictly speaking it doesn’t have to be anonymous, but you do need an on-the-fly function def) to capture the particular instance in the definition for later execution.

Hi guys,

Still plugging away with this one.  The basic functionality is all done and working and I’m really happy with it butI’m now wanting to expand it somewhat and eventually add it to the community code repository as I reckon it’ll be quite a useful module.

Anyway, what I’m here to ask today is this…

If for example in my game.lua file I have a function called fooFunction and I want to add an onComplete so that when the message has finished being displayed it will call the function, I would add the message like thus:

messageQueue:addMessage("test message", {time=2000, onComplete=fooFunction})

How do I then call the function (which is in game.lua) from the messageQueue module?

I hope I’ve worded this so that it makes sense.

Cheers

Chris

you would do something like this

if onComplete then onComplete() end

DOH!  Of course it would be that easy :slight_smile:

Thank you

Here’s an example using one of Roaming Gamers games he produced for the hangout…

In your programs, you can use just a few lines to call the function.

You can then ‘require’ the module in other apps to reuse the same routine. Eventually you can build yourself a library. (A bit like Ed did with SSK)

Don’t forget you can also return values from the functions if needed.

In the main line, it uses this code…

---------------------------------------------------------------------- -- Sound ---------------------------------------------------------------------- local soundMgr = require "scripts.soundMgr" --soundMgr.init() --soundMgr.enableSFX( true ) --soundMgr.playSoundTrack( "sounds/music/8bit Dungeon Level.mp3" ) &nbsp;

In a module (lua program) called soundMgr.lua, it holds this code (verbatim)

-- ============================================================= -- Copyright Roaming Gamer, LLC. 2009-2015 -- ============================================================= -- This content produced for Corona Geek Hangouts audience. -- You may use any and all contents in this example to make a game or app. -- ============================================================= local audio = require "audio" local common = require "scripts.common" local public = {} local getTimer = system.getTimer -- Local storage for handles to sound files -- local effects = {} local minTime = {} minTime.zombie1 = 2500 minTime.zombie2 = 5000 minTime.zombie3 = 5000 minTime.archer = 2000 minTime.bowfire = 150 local lastTime = {} local altVolume = {} altVolume.zombie2 = 0.25 altVolume.zombie3 = 0.25 altVolume.died = 0.5 altVolume.nextlevel = 0.5 -- Sound Effects Enable -- local sfxEn = false public.enableSFX = function( enable ) sfxEn = enable end -- Sound Effect EVENT listener -- local function onSFX( event ) local sfx = effects[event.sfx] if( not sfx ) then return end if( not sfxEn ) then return end local curTime = getTimer() print(curTime, minTime[event.sfx], lastTime[event.sfx] ) if( minTime[event.sfx] and lastTime[event.sfx] ) then if( curTime - lastTime[event.sfx] \< minTime[event.sfx] ) then return end end lastTime[event.sfx] = curTime local channel = audio.findFreeChannel( 2 ) --table.dump(event) if( channel ) then audio.setVolume( altVolume[event.sfx] or 1, { channel = channel } ) audio.play( sfx, { channel = channel } ) end end; listen( "onSFX", onSFX ) function public.init() effects["coin1"] = audio.loadSound("sounds/sfx/Pickup\_Coin.wav") effects["coin2"] = audio.loadSound("sounds/sfx/Pickup\_Coin2.wav") effects["coin3"] = audio.loadSound("sounds/sfx/Pickup\_Coin3.wav") effects["zombie1"] = audio.loadSound("sounds/sfx/Zombie Kill You.wav") effects["zombie2"] = audio.loadSound("sounds/sfx/Zombie Attack Walk.wav") effects["zombie3"] = audio.loadSound("sounds/sfx/Zombie Moan.wav") effects["archer"] = audio.loadSound("sounds/sfx/Archer.wav") effects["bowfire"] = audio.loadSound("sounds/sfx/Bow Fire.wav") effects["died"] = audio.loadSound("sounds/sfx/died.wav") effects["gem"] = audio.loadSound("sounds/sfx/gem.wav") effects["nextlevel"] = audio.loadSound("sounds/sfx/nextlevel.wav") end -- Set the sound track file -- local firstPlay = true public.playSoundTrack = function( path ) if( firstPlay ) then firstPlay = false local soundTrack = audio.loadStream( path ) audio.play( soundTrack, { channel=1, loops=-1, fadein = 3000 } ) else audio.resume( 1 ) end end public.pauseSoundTrack = function( ) audio.pause( 1 ) end return public

This will be easy to do if you create a message queue based on a table.  

In a module declare a function such as addMessage(msg, time).  This function will add a new row to an internal table holding the messages.

Also in the module add an enterFrame() event that keeps track of the current message and the time it was displayed.  If the queue is empty it does nothing.  If the #messages > current message then it has a new message to display and it should show it.  You then have 2 choices, set a timer to remove the message after time has passed or keep track of the current time and hide the current message if its time has expired.  increment current message and check for any new messages.

If you are going to have many thousands of messages you might need to clear down the message queue occasionally.

Return a reference to the module and require whenever it’s needed.

Here is the basic structure and you just add the logic

local messageQueue = {} function&nbsp;messageQueue:addMessage(msg, time) &nbsp; --add to queue end function messageQueue:processQueue(event) &nbsp; --process the message queue here end Runtime:addEventListener("onFrame", processQueue) return&nbsp;messageQueue

then in another module

local messageQueue = require("messageQueue") --show message for 10s messageQueue:addMessage("this is a message", 10)

Hi guys,

Sorry for the very late reply to this.

I took on board your advise and started to write the module but then got stuck and the mundane world took over and left me with no time to work on it.

Anyway…

This is what I have so far:

messages.lua

local messageQueue = {} local queue={} messageQueue.queue = queue local centerX = display.actualContentWidth\*0.5 local centerY = display.actualContentHeight\*0.5 local w = display.actualContentWidth local working = false local messageFont = native.systemFont local messageDisplay = display.newText ( " ", centerX, centerY, messageFont, 64 ) messageDisplay:setFillColor(1) messageDisplay.alpha=0 function messageQueue.hideMessage() print("HIDING MESSAGE") local m=queue[1] if m.fadeOut then local fadeOut = transition.to(messageDisplay, {alpha=0, time=500}) else messageDisplay.alpha=0 end end function messageQueue.showMessage() print("SHOWING MESSAGE") local m=queue[1] messageDisplay.text = m.message if m.fadeIn then local fadeIn = transition.to(messageDisplay, {alpha=1, time=500}) else messageDisplay.alpha=1 end timer.performWithDelay(m.displayTime, messageQueue.hideMessage() ) end function messageQueue.processQueue() isWorking = true if #queue \< 1 then isWorking = false return false else messageQueue.showMessage() end end function messageQueue:addMessage(msg, time, fadeIn, fadeOut) local newMessage={} newMessage.message. = msg newMessage.displayTime = time newMessage.fadeIn. = fadeIn newMessage.fadeOut. = fadeOut table.insert( queue, newMessage ) if not working then self.processQueue() end return end return messageQueue

and it is accessed like so:

main.lua

local messageQueue = require ( "messages" ) local f = 1 local tmpMsg = "This is message "..tostring(f) messageQueue:addMessage(tmpMsg, 8000, true, true) -- addMessage( message to be display, time to be displayed for, fadeIn? (boolean), fadeOut? (boolean)

The issue I’m having is that in the showMessage function if I comment out the timer then the message shows up as expected (either with our without fading in as specified).

But, if I uncomment the timer so that it should then go to the hideMessage function after the given time, what actually happens is that the message never appears and the console shows both the ‘show’ and ‘hide’ print statements.

I’m probably missing something obvious but I can’t for the life of me figure it out.

(apologies if the code isn’t brilliant, this is my first try at writing an external module).

Many thanks

Chris

Change line 34 from

timer.performWithDelay(m.displayTime, messageQueue.hideMessage() )

to

timer.performWithDelay(m.displayTime, function() messageQueue.hideMessage() end)

what is the result?

Perfect.  That is spot on.  many many thanks.

Just out of interest though, why would wrapping it in an anonymous function make a difference?

I don’t really know the inner workings of this but…  if you were simply calling a local function hideMessage it would of been fine but when the function resides in another class/module or you are passing parameters to it, i.e. using () then the function needs to be anonymous to stop it being executed immediately.

the former calls the function, and passes its return value (nil) to timer.performWithDelay, and no function = no timer execution

the latter passes a function definition to timer.performWithDelay, “wrapping” your original function call for later execution

given that you don’t need a “self” parameter, you could also just do

timer.performWithDelay(m.displayTime, messageQueue.hideMessage )

note the lack of parenthesis - this isn’t a call, it’s passing the reference to the function.  that’ll work, even though it’s in another module - the function’s closure environment will “contain” all of the module-local variables that it needs.

[ADDED] at the risk of getting too esoteric…  if you did need a “self” call (on a specific instance, for example:  aMessenger:hideMessage()) then you’d have to use the anonymous function style (or equivalent, strictly speaking it doesn’t have to be anonymous, but you do need an on-the-fly function def) to capture the particular instance in the definition for later execution.

Hi guys,

Still plugging away with this one.  The basic functionality is all done and working and I’m really happy with it butI’m now wanting to expand it somewhat and eventually add it to the community code repository as I reckon it’ll be quite a useful module.

Anyway, what I’m here to ask today is this…

If for example in my game.lua file I have a function called fooFunction and I want to add an onComplete so that when the message has finished being displayed it will call the function, I would add the message like thus:

messageQueue:addMessage("test message", {time=2000, onComplete=fooFunction})

How do I then call the function (which is in game.lua) from the messageQueue module?

I hope I’ve worded this so that it makes sense.

Cheers

Chris

you would do something like this

if onComplete then onComplete() end