Jump to content

[TOPIC: topicViewTemplate]
[GLOBAL: userSmallPhoto]
Photo

Creating Composer scenes programmatically.
Started by paulscottrobson Apr 22 2014 03:04 AM

20 replies to this topic
[TOPIC CONTROLS]
[/TOPIC CONTROLS]
[modOptionsDropdown]
[/modOptionsDropdown]
[reputationFilter]
[TOPIC: post.html]
#1

paulscottrobson

[GLOBAL: userInfoPane.html]
paulscottrobson
  • Pro
  • PipPipPipPipPipPip
  • 357 posts
  • Jedi

It would be nice to do this :) AFAICS the only way you can create a named scene is through gotoScene() which involves a separate code file for every scene.

 

So you could write something like this (would not be disposable I suppose !) :-

local scene = composer.newScene()
-- attach various bits and bobs to it
composer.addScene("demoscene",scene)
-- then, later
composer.gotoScene("demoscene")

 

or even if you could just provide a factory routine, something like :-

 

composer.gotoScene("scenename",function(composerInstance)
    -- code to programatically create scene 
end)

so the code would do the same as the code in the scene file does.



[TOPIC: post.html]
#2

davebollinger

[GLOBAL: userInfoPane.html]
davebollinger
  • Basic
  • PipPipPipPipPipPip
  • 268 posts
  • Jedi

Have you considered a DIY on the legacy storyboard?: https://github.com/coronalabs/framework-storyboard-legacy

[TOPIC: post.html]
#3

paulscottrobson

[GLOBAL: userInfoPane.html]
paulscottrobson
  • Pro
  • PipPipPipPipPipPip
  • 357 posts
  • Jedi

Seems a shame not to use the new system, especially if it Storyboard is going to be removed. I don't think this change would be difficult ; at the moment gotoScene() et al. access their scenes through the require mechanism, it is only an issue of providing an alternate.



[TOPIC: post.html]
#4

davebollinger

[GLOBAL: userInfoPane.html]
davebollinger
  • Basic
  • PipPipPipPipPipPip
  • 268 posts
  • Jedi

storyboard vs composer - your choice. but this doesn't sound like the type of feature they'd ever add.

fwiw, if you really think about it, you don't REALLY need more than two scenes (or maybe three, if you want one dedicated as an overlay). then just ping-pong between them.

in that scenario, your "factory" wouldn't be creating entire scenes, rather it'd be creating CONTENT for the two "template" ping-pong scenes. your scenes then are just "wrappers" for composer's/storyboard's use.

pseudo-code OTTOMH example:
-- load/precreate two empty "template" scenes:
local composer = require("composer")
composer.loadScene("ping")
local ping = composer.getScene("ping")
composer.loadScene("pong")
local pong = composer.getScene("pong")
--
contentFactory:createContentForScene(ping,"someModuleThatResemblesASceneButIsnt")
composer:gotoScene("ping")

-- inside your template scenes you'd do something like this:
local scene = composer.newScene()
scene.content = nil -- to be populated by content factory
function scene:show(event) -- and similarly for others..
  if (self.content) then
    self.content:show(event)
  end
end


[TOPIC: post.html]
#5

luisherranz

[GLOBAL: userInfoPane.html]
luisherranz
  • Starter
  • Pip
  • 8 posts
  • Newbie

We want this as well. If you want to use the Composer for business apps you are going to end up with the same scene (file) used to populate different things.

 

Consider for example a blog app. You may have a "posts-list.lua" and a "single-post.lua". If you want to move directly from a single-post to another single-post, you need to duplicate your single-post.lua. Not cool. Not DRY.

 

The implementation should be quite easy anyway. If you pass a string to the composer.gotoScene() function, it does a require. If you pass it a table, it uses that table instead. Something as simple as this:

 

function composer:gotoScene( scene, options )
local scene_table
if type(scene) == "string" then
scene_table = require( scene )
elseif type(scene) == "table" then
scene_table = scene
end
....
end

 

Of course you need to check if you have previously loaded the same table before (so you don't call create, etc) but I bet the composer is already doing that in some way.

 

Then you can do a composer.newScene() outside a .lua file, organise the code to be reusable and don't repeat yourself.

 

 

Today I have read this on the Daily Builds:

  • Composer: Fixing a error when defining multiple scenes inside the same lua file. Casenum #32231, #32168

I wonder if there is already a way to do this.

Could somebody from Corona explain it? I couldn't find any more information about it.



[TOPIC: post.html]
#6

alexf

[GLOBAL: userInfoPane.html]
alexf
  • Corona Staff
  • 231 posts
  • Jedi

Composer can create multiple scenes in one file.

 

local composer = require "composer"

local scene1 = composer.newScene( "scene1" )
local scene2 = composer.newScene( "scene2" )

function scene1:create( event )
     --do stuff
end
scene1:addEventListener( "create", scene1 )

function scene2:create( event )
    -- do stuff
end
scene2:addEventListener( "create", scene2 )

composer.gotoScene( "scene1" )

 

Does this help?

 

Thanks,

alex



[TOPIC: post.html]
#7

paulscottrobson

[GLOBAL: userInfoPane.html]
paulscottrobson
  • Pro
  • PipPipPipPipPipPip
  • 357 posts
  • Jedi

Thanks for that - missed that optional parameter. Will make my OOP Wrapper somewhat more coherent I think :)



[TOPIC: post.html]
#8

paulscottrobson

[GLOBAL: userInfoPane.html]
paulscottrobson
  • Pro
  • PipPipPipPipPipPip
  • 357 posts
  • Jedi

http://docs.coronalabs.com/api/library/composer/newScene.html has this useful API feature missing, which is why I missed it. Presumably if you create it programmatically you cannot have the scene recycling on, as when it recycles it the next gotoScene() will try to load it in as code.



[TOPIC: post.html]
#9

luisherranz

[GLOBAL: userInfoPane.html]
luisherranz
  • Starter
  • Pip
  • 8 posts
  • Newbie

Yes, @alexf, many thanks. As @paulscottrobson said, it would be great to add that to the documentation.



[TOPIC: post.html]
#10

vadiminc

[GLOBAL: userInfoPane.html]
vadiminc
  • Pro
  • PipPip
  • 13 posts
  • Member

Hi guys, please help to find solutions how to create and load dynamically scenes. So, for example. we have only one template .lua file with general scene code. And need to create 3 scenes with different names but same structure from template .lua file



[TOPIC: post.html]
#11

paulscottrobson

[GLOBAL: userInfoPane.html]
paulscottrobson
  • Pro
  • PipPipPipPipPipPip
  • 357 posts
  • Jedi

Well, you can use alexf's idea and pre-create it. Or my OOP Composer, which would allow you to declare it as an class and create instances for the various scenes. My Bitmap Font demo app does something like this, creating different scenes using the same basic template with small differences.

 

If this is a level scene you could have it data driven, so everything uses the same scene, but the things that go in the scene are (say) pulled from an XML file.



[TOPIC: post.html]
#12

vadiminc

[GLOBAL: userInfoPane.html]
vadiminc
  • Pro
  • PipPip
  • 13 posts
  • Member

Paul, thank you for answer. Where I can find your OOP Composer to check out?



[TOPIC: post.html]
#13

paulscottrobson

[GLOBAL: userInfoPane.html]
paulscottrobson
  • Pro
  • PipPipPipPipPipPip
  • 357 posts
  • Jedi

It's at https://github.com/autismuk/Scene-Manager - there's a simple demo showing a couple of scenes and an overlay. The Font Demo program at https://github.com/autismuk/Font-Demo does something similar to what you want to do and uses it.

 

It has one main scene (the menu) and six sub scenes, which are very similar but slightly different (the font effect changes) and it creates a scene class which can be modified via the constructor, and it then creates six instances of the scene to add to the composer equivalent.

 

It's a bit beta but I think it works :)



[TOPIC: post.html]
#14

vadiminc

[GLOBAL: userInfoPane.html]
vadiminc
  • Pro
  • PipPip
  • 13 posts
  • Member

Just found out yours code on github. Great idea! Thanks.

Corona Labs, when you will do your job better? Make your solutions flexible ;)

Last time I'm disappointed in your platform....



[TOPIC: post.html]
#15

luisherranz

[GLOBAL: userInfoPane.html]
luisherranz
  • Starter
  • Pip
  • 8 posts
  • Newbie

@vadimic, you can still use the scene template but use it inside a creator function.

 

Something like this:

local function dynamicSceneCreator( scene_name )

local composer = require( "composer" )
local scene = composer.newScene( scene_name )

-- -----------------------------------------------------------------------------------------------------------------
-- All code outside of the listener functions will only be executed ONCE unless "composer.removeScene()" is called.
-- -----------------------------------------------------------------------------------------------------------------

-- local forward references should go here

-- -------------------------------------------------------------------------------


-- "scene:create()"
function scene:create( event )

    local sceneGroup = self.view

    -- Initialize the scene here.
    -- Example: add display objects to "sceneGroup", add touch listeners, etc.
end


-- "scene:show()"
function scene:show( event )

    local sceneGroup = self.view
    local phase = event.phase

    if ( phase == "will" ) then
        -- Called when the scene is still off screen (but is about to come on screen).
    elseif ( phase == "did" ) then
        -- Called when the scene is now on screen.
        -- Insert code here to make the scene come alive.
        -- Example: start timers, begin animation, play audio, etc.
    end
end


-- "scene:hide()"
function scene:hide( event )

    local sceneGroup = self.view
    local phase = event.phase

    if ( phase == "will" ) then
        -- Called when the scene is on screen (but is about to go off screen).
        -- Insert code here to "pause" the scene.
        -- Example: stop timers, stop animation, stop audio, etc.
    elseif ( phase == "did" ) then
        -- Called immediately after scene goes off screen.
    end
end


-- "scene:destroy()"
function scene:destroy( event )

    local sceneGroup = self.view

    -- Called prior to the removal of scene's view ("sceneGroup").
    -- Insert code here to clean up the scene.
    -- Example: remove display objects, save state, etc.
end


-- -------------------------------------------------------------------------------

-- Listener setup
scene:addEventListener( "create", scene )
scene:addEventListener( "show", scene )
scene:addEventListener( "hide", scene )
scene:addEventListener( "destroy", scene )

-- -------------------------------------------------------------------------------

return scene

end

Then just call the creator before going to that scene:

local function gotoDynamicScene( scene_name )

  if composer.getScene( scene_name ) == nil then
    dynamicSceneCreator( scene_name)
  end

  composer.gotoScene( scene_name )

end


[TOPIC: post.html]
#16

vadiminc

[GLOBAL: userInfoPane.html]
vadiminc
  • Pro
  • PipPip
  • 13 posts
  • Member

luisherranz, amazing idea! Thanks a lot. Only one small thing, doesn't possible to use loadScene method that to build pre-loader things :(



[TOPIC: post.html]
#17

vadiminc

[GLOBAL: userInfoPane.html]
vadiminc
  • Pro
  • PipPip
  • 13 posts
  • Member

Also just checked now your solutions, and have got the WARNING when I try to use composer:gotoScene(....):

WARNING: Cannot create path for resource file 'Tab1.ccscene' b/c it does not exist.



[TOPIC: post.html]
#18

luisherranz

[GLOBAL: userInfoPane.html]
luisherranz
  • Starter
  • Pip
  • 8 posts
  • Newbie

I have not tested it but I don't see why it wouldn't work. Just make sure to create it first:

dynamicSceneCreator( "my_scene_1" )
dynamicSceneCreator( "my_scene_2" )

composer.loadScene( "my_scene_1" )

The cssscene warning comes from Corona, from their new Composer GUI. I would not worry about it.



[TOPIC: post.html]
#19

vadiminc

[GLOBAL: userInfoPane.html]
vadiminc
  • Pro
  • PipPip
  • 13 posts
  • Member

yes, it works now. First of all, need to make loadScene and after that use gotoScene.

btw, if you put true to second param, for example, composer.loadScene(scene_name, true, params). The create() event won't be called. Only when you use false, create() will be called.



[TOPIC: post.html]
#20

vadiminc

[GLOBAL: userInfoPane.html]
vadiminc
  • Pro
  • PipPip
  • 13 posts
  • Member

another issue:

composer.gotoScene( tabs[1].title )
composer.gotoScene( tabs[2].title )

result:

scene:show        will    Tab1
scene:hide        will    Tab1
scene:hide        did     Tab1
scene:show        will    Tab2
scene:show        did     Tab2
scene:show        did     Tab2

solution:

composer.gotoScene( tabs[1].title )
timer.performWithDelay( 0, function() composer.gotoScene( tabs[2].title ) end )


[TOPIC: post.html]
#21

kilopop

[GLOBAL: userInfoPane.html]
kilopop
  • Starter
  • PipPipPipPipPipPip
  • 113 posts
  • Jedi

Hi Luisherranz, your dynamic scene creation method seems really useful. We are currently using the ping pong method between 2 composer scenes, filling create scene with new data each time. But all on one page would be much tidier.

 

At this point we get a littany of errors but mainly "attempt to concatenate global 'sceneName' (a nil value)" which is coming from the original composer.gotoScene("scene_1", options) in main.lua..

 

Could you possibly post a working example of your one page composer solution? The first error we got was that composer didn't exist when running the gotoDynamicScene so we required composer before the dynamicSceneCreator function. But maybe this means we have set the page up in correctly against your example?

 

Anyway, a working example would be most appreciated and in fact should be the main method as how to use composer. Why bother repeating the same structure over 26 scenes in our case if one page populated with new data will work.

 

Thanks Luisherranz.




[topic_controls]
[/topic_controls]