How to handle common UI elements in my game

Hi All,

More of a “how does everyone else handle this” type question…

I have a game that has many composer scenes, etc and a lot of the graphics are styled the same across all scenes. for example, they all have

  • A header with a title for the current scene, a back button and a cancel button
  • Various buttons images with relevant text overlaid depending on what they will do.  The button image is exactly the same except for maybe a different ‘fill’ colour which is done pragmatically.
  • graphics to group sections within a page e.g. in my setting page i have a ‘user’ section, then a ‘sounds’ section, etc… 
  • A footer with some common info in it

etc etc…

I was finding I was copying and pasting the exact same code for a button plus text all over the place (and just changing the text value) and it was getting messy and hard to manage.  I just wanted to see how other devs approach this problem?

I currently have a gfx.lua where I have all the code to generate the various bits of my ui which I then call from whatever scene Im in… Obviously I have various parameter to determine the text, etc as this will change.

Can you see any problems doing it using a gfx.lua module?  

Keen to hear how other (more experienced) devs do this

Thanks

If the scenes have similar graphical content, I usually just group all the code into one scene. This one scene will basically contain all the different scenes that I need

Whenever i want to change the scene, i will call a method like:

[lua]

function scene:changeMode(newMode)

    --Appropriate changes made here for example:

   if (newMode == “mainMenu”) then

       titleText.text = “Main Menu”

   end

end

[/lua]

What I do is take the composer scene template and drop it in my project folder named template.lua.  I then add all of the UI elements needed that will be common to every scene, such as bars, titles, backgrounds, the “Continue” button.  Once I have that the way I want it, I will copy the template to my other scenes (help.lua, gamecredits.lua, settings.lua, etc.).  Then I go in and add in the scene specifics.

Now the drawback to this that if I need to make a change then I have to touch every scene.  I can see where your idea about having a gui.lua that has the core frame work in it that you would require.  It would need an .init() or .new() function that would create a whole batch of display objects and you could pass in your scene’s view group for it to insert the objects into.

That seems like a reasonably valid approach.

Rob

Thanks for the replies guys…

Rob - yes that is what I was thinking… just for the common elements…  good point on the init! 

With regard to other controls like buttons, etc I know I could use the widgets framework… However, I am using the ultimate config.lua you spoke about in another post (which is awesome btw) but… I remember you saying in a comment that there are problems with the widget controls scaling properly .

Is this still the case? if so, from the comments the work around is to use a content size of  320×480?

Config post: http://coronalabs.com/blog/2013/09/10/modernizing-the-config-lua/

Your comment:  http://coronalabs.com/blog/2013/09/10/modernizing-the-config-lua/#comment-46245

I can work around widgets except for the scroll view.

Sorry if this is going slightly off topic…

cheers

The main purpose of the ultimate config.lua was to make it fit the screen regardless of the device.  The idea about going to 800x1200 was around breaking the notion that your config.lua should think in pixels and with  fewer low res devices around, it simplifies having to have 3 image resolutions, you could get away with two.

There is absolutely nothing wrong with maintaining a 320x480 content area and if you need widgets, you probably should stay at that resolution.  The rest of the config.lua will work like a champ.

Rob

That’s exactly the approach I ended up with in one of my apps for very much the same reason.  I had multiple scenes with common UI elements and making minor changes to position, size, etc. was becoming tedious.  I created a userInterface.lua module to handle drawing the components and now I only have to change the appearance in one place and it automatically propagates throughout.

Another option to consider is including the listener function in the UI module too.  Then you can use the getSceneName function to determine the listener’s behavior.  Or if it’s a common behavior across all modules it’s already said and done.  Hope that helps!

Josh

Rob,

Already sent this to Dave privately but thought I’d go ahead and share it here too.  To implement a common UI interface in my scenes I include the following lines in each scene:

local ui = require( "uiExample" ) local uiGroup = display.newGroup() ui.drawUI( uiGroup ) sceneGroup:insert( uiGroup )

And here’s a stripped down version of the module I used.  I included an example of using the current scene to customize the behavior in the Back button listener.

Josh

----------------------------------------------------------------------------------------- -- -- uiExample.lua -- -- Functions used for drawing the user interface buttons such as back, help, etc. -- ----------------------------------------------------------------------------------------- local \_ = {} local composer = require( "composer" ) local widget = require( "widget" ) local options ={ effect = "fade", time = 400, } -- 'onRelease' event listener for backBtn local function onBackBtnRelease(event) local currScene = composer.getSceneName( "current" ) -- go to selected scene if currScene == "Play" then composer.gotoScene( "Menu", options ) elseif currScene == "Puzzle" then composer.gotoScene( "Play", options ) end return true -- indicates successful touch end -- 'onRelease' event listener for optionsBtn local function onOptionsBtnRelease(event) composer.gotoScene( "Options", options ) return true -- indicates successful touch end -- 'onRelease' event listener for storeBtn local function onStoreBtnRelease(event) composer.gotoScene( "Store", options ) return true -- indicates successful touch end function \_.drawUI(uiGroup) local options = { labelColor = { default={1}, over={0.25} }, font = native.systemFont, fontSize = 12, width=0.1\*display.contentWidth, height=0.1\*display.contentWidth, } options.label = "Options" options.x = 0.25\*display.contentWidth options.y = 0.5\*display.contentWidth options.onRelease = onOptionsBtnRelease local optionsBtn = widget.newButton( options ) uiGroup.optionsBtn = optionsBtn uiGroup:insert(optionsBtn) options.label = "Back" options.x = 0.5\*display.contentWidth options.y = 0.5\*display.contentWidth options.onRelease = onBackBtnRelease local backBtn = widget.newButton( options ) uiGroup.backBtn = backBtn uiGroup:insert(backBtn) options.label = "Store" options.x = 0.75\*display.contentWidth options.y = 0.5\*display.contentWidth options.onRelease = onStoreBtnRelease local storeBtn = widget.newButton( options ) uiGroup.storeBtn = storeBtn uiGroup:insert(storeBtn) return end return \_

If the scenes have similar graphical content, I usually just group all the code into one scene. This one scene will basically contain all the different scenes that I need

Whenever i want to change the scene, i will call a method like:

[lua]

function scene:changeMode(newMode)

    --Appropriate changes made here for example:

   if (newMode == “mainMenu”) then

       titleText.text = “Main Menu”

   end

end

[/lua]

What I do is take the composer scene template and drop it in my project folder named template.lua.  I then add all of the UI elements needed that will be common to every scene, such as bars, titles, backgrounds, the “Continue” button.  Once I have that the way I want it, I will copy the template to my other scenes (help.lua, gamecredits.lua, settings.lua, etc.).  Then I go in and add in the scene specifics.

Now the drawback to this that if I need to make a change then I have to touch every scene.  I can see where your idea about having a gui.lua that has the core frame work in it that you would require.  It would need an .init() or .new() function that would create a whole batch of display objects and you could pass in your scene’s view group for it to insert the objects into.

That seems like a reasonably valid approach.

Rob

Thanks for the replies guys…

Rob - yes that is what I was thinking… just for the common elements…  good point on the init! 

With regard to other controls like buttons, etc I know I could use the widgets framework… However, I am using the ultimate config.lua you spoke about in another post (which is awesome btw) but… I remember you saying in a comment that there are problems with the widget controls scaling properly .

Is this still the case? if so, from the comments the work around is to use a content size of  320×480?

Config post: http://coronalabs.com/blog/2013/09/10/modernizing-the-config-lua/

Your comment:  http://coronalabs.com/blog/2013/09/10/modernizing-the-config-lua/#comment-46245

I can work around widgets except for the scroll view.

Sorry if this is going slightly off topic…

cheers

The main purpose of the ultimate config.lua was to make it fit the screen regardless of the device.  The idea about going to 800x1200 was around breaking the notion that your config.lua should think in pixels and with  fewer low res devices around, it simplifies having to have 3 image resolutions, you could get away with two.

There is absolutely nothing wrong with maintaining a 320x480 content area and if you need widgets, you probably should stay at that resolution.  The rest of the config.lua will work like a champ.

Rob

That’s exactly the approach I ended up with in one of my apps for very much the same reason.  I had multiple scenes with common UI elements and making minor changes to position, size, etc. was becoming tedious.  I created a userInterface.lua module to handle drawing the components and now I only have to change the appearance in one place and it automatically propagates throughout.

Another option to consider is including the listener function in the UI module too.  Then you can use the getSceneName function to determine the listener’s behavior.  Or if it’s a common behavior across all modules it’s already said and done.  Hope that helps!

Josh

Rob,

Already sent this to Dave privately but thought I’d go ahead and share it here too.  To implement a common UI interface in my scenes I include the following lines in each scene:

local ui = require( "uiExample" ) local uiGroup = display.newGroup() ui.drawUI( uiGroup ) sceneGroup:insert( uiGroup )

And here’s a stripped down version of the module I used.  I included an example of using the current scene to customize the behavior in the Back button listener.

Josh

----------------------------------------------------------------------------------------- -- -- uiExample.lua -- -- Functions used for drawing the user interface buttons such as back, help, etc. -- ----------------------------------------------------------------------------------------- local \_ = {} local composer = require( "composer" ) local widget = require( "widget" ) local options ={ effect = "fade", time = 400, } -- 'onRelease' event listener for backBtn local function onBackBtnRelease(event) local currScene = composer.getSceneName( "current" ) -- go to selected scene if currScene == "Play" then composer.gotoScene( "Menu", options ) elseif currScene == "Puzzle" then composer.gotoScene( "Play", options ) end return true -- indicates successful touch end -- 'onRelease' event listener for optionsBtn local function onOptionsBtnRelease(event) composer.gotoScene( "Options", options ) return true -- indicates successful touch end -- 'onRelease' event listener for storeBtn local function onStoreBtnRelease(event) composer.gotoScene( "Store", options ) return true -- indicates successful touch end function \_.drawUI(uiGroup) local options = { labelColor = { default={1}, over={0.25} }, font = native.systemFont, fontSize = 12, width=0.1\*display.contentWidth, height=0.1\*display.contentWidth, } options.label = "Options" options.x = 0.25\*display.contentWidth options.y = 0.5\*display.contentWidth options.onRelease = onOptionsBtnRelease local optionsBtn = widget.newButton( options ) uiGroup.optionsBtn = optionsBtn uiGroup:insert(optionsBtn) options.label = "Back" options.x = 0.5\*display.contentWidth options.y = 0.5\*display.contentWidth options.onRelease = onBackBtnRelease local backBtn = widget.newButton( options ) uiGroup.backBtn = backBtn uiGroup:insert(backBtn) options.label = "Store" options.x = 0.75\*display.contentWidth options.y = 0.5\*display.contentWidth options.onRelease = onStoreBtnRelease local storeBtn = widget.newButton( options ) uiGroup.storeBtn = storeBtn uiGroup:insert(storeBtn) return end return \_