What is the best way to reload/restart the current scene with composer and keep information?

I was trying to put the eventListeners where you said, and I got an eventListener cannot be nil error.

You need to foward declared(outside the scene:create) onColllison, touchScreen, and send enmities to put them inside scene destroy, hide, and show

So, addEventListeners in phase == did for show and hide, and remove them in destroy. Is this correct?

Would the same thing apply to the eventListeners for my background scrolling: 

local function scrollMountains(self,event) if self.x \< -self.width \* 0.6 then self.x = self.width \* 1.32 else self.x = self.x - self.value end end horizon.enterFrame = scrollMountains Runtime:addEventListener("enterFrame", horizon) horizon2.enterFrame = scrollMountains Runtime:addEventListener("enterFrame", horizon2) mountainback.enterFrame = scrollMountains Runtime:addEventListener("enterFrame", mountainback) mountainback2.enterFrame = scrollMountains Runtime:addEventListener("enterFrame", mountainback2) mountain.enterFrame = scrollMountains Runtime:addEventListener("enterFrame", mountain) mountain2.enterFrame = scrollMountains Runtime:addEventListener("enterFrame", mountain2) trees.enterFrame = scrollMountains Runtime:addEventListener("enterFrame", trees) trees2.enterFrame = scrollMountains Runtime:addEventListener("enterFrame", trees2)

Again, feel free to make changes to the documents I posted and send them back. Thank you.

Sincerely,

Alex

Only do add event listeners for collision, touch, and enter frame for send enemies in scene show and event.phase == did. do remove event listeners for collision, touch, and enter frame for send enemies in scene destroy and hide and event.phase == did.

function scene:show( event ) local sceneGroup = self.view local phase = event.phase if phase == "will" then -- Called when the scene is still off screen and is about to move on screen elseif phase == "did" then -- Called when the scene is now on screen -- -- INSERT code here to make the scene come alive -- e.g. start timers, begin animation, play audio, etc. Runtime:addEventListener("enterFrame", sendEnemies) Runtime:addEventListener("touch", touchScreen) Runtime:addEventListener("collision", onCollision) physics.start() end end function scene:hide( event ) local sceneGroup = self.view local phase = event.phase if phase == "will" then -- Called when the scene is on screen and is about to move off screen -- -- INSERT code here to pause the scene physics.pause() elseif phase == "did" then Runtime:removeEventListener("enterFrame", sendEnemies) Runtime:removeEventListener("touch", touchScreen) Runtime:removeEventListener("collision", onCollision) end end function scene:destroy( event ) Runtime:removeEventListener("enterFrame", sendEnemies) Runtime:removeEventListener("touch", touchScreen) Runtime:removeEventListener("collision", onCollision) -- Called prior to the removal of scene's "view" (sceneGroup) -- -- INSERT code here to cleanup the scene -- e.g. remove display objects, remove touch listeners, save state, etc. local sceneGroup = self.view end

I did this, but everytime the character hits an enemy they lose two lives, and occasionally there is still an enemy present after level1.lua has gone to menu.lua.

Similar to touch and scene you onColllison function has two phase began and ended. Because of this your player hit goes off twice

I don’t see anything about menu.lua in your code

For your first way

this is most likely physics or timer or event is still running when you go to another scene. You are suppose to remove or pause this when you got to another scene. 

For your 2nd question: i need some code to work with. This is just the reset scene. I need at least an idea on how your code is setup.

  1. you should move all your objects back (need code to help further)

  2. before you goto another scene you can do :

    local lifeCount = composer.getVariable( “lifeCount” ) if lifeCount == nil then lifeCount = 5 end composer.setVariable( lifeCount, lifeCount-1 )

Their are other way will modules or passing  between scenes, but this works

Dear Scott,

Thank you very much for your reply, I am extremely appreciative of your help. I am 12 years old and this can get frustrating at times for me. 

Here are the most important sections of my code:

local function sendEnemies()

        timeCounter = timeCounter + 1

        if((timeCounter%enemySendSpeed) == 0) then

            enemyCounter = enemyCounter + 1

            enemySendSpeed = enemySendSpeed - enemyIncrementSpeed

            if(enemySendSpeed <= enemyMaxSendSpeed) then

                enemySendSpeed = enemyMaxSendSpeed

            end

            local temp = math.random(1,3)

            if(temp == 1) then

                enemy[enemyCounter] = display.newSprite(beetle, beetleSequenceData)

            elseif (temp == 2) then

                enemy[enemyCounter] = display.newSprite(scorpion, scorpionSequenceData)

            else

                enemy[enemyCounter] = display.newSprite(vulture, vultureSequenceData)

            end

            

            enemy[enemyCounter].x = _R + 150

            enemy[enemyCounter].y = _CY - (_CH * 0.1)

            enemy[enemyCounter].xScale = -1

            enemy[enemyCounter].id = “enemy”

            enemy[enemyCounter].isFixedRotation = true

            

            transition.to(enemy[enemyCounter], {x=_L - 50, time=enemyTravelSpeed, onComplete= function(self)     

                if (self~= nil) then display.remove(self); end

            end})

            if(temp == 1) then

                physics.addBody(enemy[enemyCounter], “dynamic”, physicsData:get(“beetle”))

            elseif(temp == 2) then

                physics.addBody(enemy[enemyCounter], “dynamic”, physicsData:get(“scorpion”))

            elseif(temp == 3) then

                physics.addBody(enemy[enemyCounter], “dynamic”, physicsData:get(“vulture”))

                enemy[enemyCounter].gravityScale = 0

            end

            

            enemy[enemyCounter]:setSequence(“moving”)

            enemy[enemyCounter]:play()

        end

    end

for i=1, livesCount do

        lives[i] = display.newImageRect(“heart.png”, 50, 50)

        lives[i].x = _L + (i * 65) - 25

        lives[i].y = _T + 50

    end

    

    function scrollMountains(self,event)

        if self.x < -self.width * 0.6 then

            self.x = self.width * 1.32

        else

            self.x = self.x - self.value

        end

    end

    

    horizon.enterFrame = scrollMountains

    Runtime:addEventListener(“enterFrame”, horizon)

    

    horizon2.enterFrame = scrollMountains

    Runtime:addEventListener(“enterFrame”, horizon2)

    

    mountainback.enterFrame = scrollMountains

    Runtime:addEventListener(“enterFrame”, mountainback)

    

    mountainback2.enterFrame = scrollMountains

    Runtime:addEventListener(“enterFrame”, mountainback2)

    

    mountain.enterFrame = scrollMountains

    Runtime:addEventListener(“enterFrame”, mountain)

    

    mountain2.enterFrame = scrollMountains

    Runtime:addEventListener(“enterFrame”, mountain2)

    

    trees.enterFrame = scrollMountains

    Runtime:addEventListener(“enterFrame”, trees)

    

    trees2.enterFrame = scrollMountains

    Runtime:addEventListener(“enterFrame”, trees2)

    

    local characterOptions = 

    {

        width = 100,

        height = 126.5,

        numFrames = 10,

        sheetContentWidth = 500,

        sheetContentHeight = 253

    }

    

    local sheet_character = graphics.newImageSheet(“girl-running.png”, characterOptions)

    

    local sequence_character = {

        {name = “run”, start = 1, count = 10, time = 800, loopCount = 0, loopDirection = “forward”}

    }

    

    local character = display.newSprite(sheet_character, sequence_character)

    

    character.x = _L + 250

    character.y = _CY - (character.height * 0.3)

    character:play()

    character.id = “character”

    physics.addBody(character, “dynamic”, {bounce = 0})

    character.isFixedRotation = true

    

    function touchScreen(event)

        if (event.phase == “began”) then

            if (canJump == true) then

                character:applyForce(0, -60, self.x, self.y)

            end

            canJump = false

        end

        return true

    end

local function showPlayerHit()

            character.alpha = 0.5

            local tmr_onPlayerHit = timer.performWithDelay(1, playerHit, 1)

        end

        

            if(event.object1.id == “enemy” and event.object2.id == “character”) then

                showPlayerHit()

                removeOnPlayerHit(event.object1, nil)

            end

        

            if(event.object1.id == “character” and event.object2.id == “enemy”) then

                showPlayerHit()

                removeOnPlayerHit(nil, event.object2)

            end

        

            if(event.object1.id == “enemy” and event.object2.id == “enemy”) then

                event.contact.isEnabled = false    

            end

        

            if(event.object1.id == “character” and event.object2.id == “ground”) then

                canJump = true

            end

        

            if(event.object1.id == “ground” and event.object2.id == “character”) then

                canJump = true

            end

    end

    

    Runtime:addEventListener(“enterFrame”, sendEnemies)

    Runtime:addEventListener(“touch”, touchScreen)

    Runtime:addEventListener(“collision”, onCollision)

I apologize if it is a lot of code.

Thank you very much for your help.

Sincerely,

Alex

So a reset function would something like

local function resetLevel () character.x = \_L + 250 character.y = \_CY - (character.height \* 0.3) for i = 1, #enemy do display.remove(enemy[i]) enemy[i] = nil end end

i don’t see a go to reset scene but try composer.showOverlay

also i would add the scene events and on the hide (event.phase == “will”) do

physics.pause()

Runtime:removeEventListener(“enterFrame”, sendEnemies)

look at the composer docs and try to composerfy( made a new word :slight_smile: ) your code

Like add scene groups to you level or some kind of scene management. If you do use composer you can use things like showOverlay.

Thank you very much for your quick reply, I am working to implement it into my code. I just have one question: how would cause the resetLevel function to trigger and where would I put the composer.showOverlay? 

Sorry for the late reply.

Sincerely,

Alex

In first code you showed what looked like to be a gamover scene. when ever you want call a reset and go to let’s call it gameover.lua you should call resetLevel() and then composer.showOverlay(“gameover”). Of course when your done you would use hide overlay.

My playerHit() function will cause the level to restart, so overall:

  1. Call resetLevel function in playerHit

  2. Use performWithDelay to call composer.showOverlay (or just add it normally into the playerHit() function)

  3. Declare lifeCount in playerHit()

Also, where would I add hideOverlay, for now I am just hoping to restart one scene instead of going to another, then going back.

The easy way:

 

main.lua (not a real scene but starts the whole process) --> menu.lua  -->  level1.lua  --> tryagain.lua --> level1.lua

 

Now in tryagain.lua display a message for a few seconds and then while that’s displaying call storyboard.removeScene(“level1”); storyboard.gotoScene(“level1”)

 

Rob Miracle had posted this on a conversation related to mine, could the same thing work with composer? 

 

Thank you.

Yes composer can do this, I was just making a suggestion that if you remove the scene from code and go back it takes a lot of “processing power” to do this and could talk longer that is why thought show overlay would work. But will all that out the way you can is use composer.gotoScene and composer.removeScene just like storyboard. Note removeScene removes the scene from memory and could take longer load but it is up to you

Thanks, but could I still keep data? For example:

level1.lua: Current Score is 900, Lives is Lives - 1

Can I keep the current score at 900, and the correct number of lives after the game restarts and how?

Thank you.

Thier are lots of ways to do this
Get and set variables with https://docs.coronalabs.com/api/library/composer/setVariable.html
https://docs.coronalabs.com/api/library/composer/getVariable.html

Toss data back and forth with composer.gotoScene(“tryagain”,{params= {variable1 = 100, anotherVar= “hello”}})

Then on next scene in the event like create:scene(event)
print(event.params.anotherVar)

You can use modules
https://coronalabs.com/blog/2013/05/28/tutorial-goodbye-globals/

I tried using this function: 

function tryAgain() 

        composer.removeScene(“level1”)

        composer.gotoScene(“level1”, “fade”, 500)

    end

In my tryagain.lua, when I reach and try to go back to level1, I get this error: Attempt to perform arithmetic on field ‘width’ a nil value, that points to line 119 in my level1.lua.

function scrollMountains(self,event)

        if self.x < -self.width * 0.6 then --Line 119

            self.x = self.width * 1.32 

        else

            self.x = self.x - self.value

        end

end

The scrollMountains function is inside the scene:create function and makes my background scroll, do I have to move it elsewhere?

Sincerely,

Alex

If you are trying to wipe the scene with removeScene, you are deleting all on objects in sceneGroup. Composer does not auto remove runtime event listeners. You have to remove them your self. So it is still going even when you go tryagain.

Where would I remove the event Listeners?