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.
-
you should move all your objects back (need code to help further)
-
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
) 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:
-
Call resetLevel function in playerHit
-
Use performWithDelay to call composer.showOverlay (or just add it normally into the playerHit() function)
-
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?