Having trouble changing scenes

I have a simple app that I am making where asteroids fall from the sky, and when they collide with the character (flying frog…) the scene changes to a try again screen. I have everything working, however when the asteroid collides with the character and the code tries to change to tryAgain() scene function, I get an error “attempt to call global tryAgain (a nil value)”.

tryAgain() is my try again scene.

Not sure what I’m doing wrong… here is the code, the tryAgain() scene function is at the end of the code, and the error is happening on line 14 when the collision happens. I have  bolded the problem code.

Any help would be greatly appreciated!

Thanks in advance!


display.setStatusBar(display.HiddenStatusBar)

local physics = require("physics")  
physics.start()  
physics.setDrawMode("hybrid")  
physics.setGravity(0,0)

local W = display.contentWidth  
local H = display.contentHeight

local asteroid  
local frog  
local background  
local button  
local text

local mRandom=math.random

**local function astCollision(self, event)  
 if event.phase == "began" then  
  if event.target.type == "asteroid" and event.other.type == "frog" then  
 tryAgain()  
 end  
 end  
end**

local function asteroids()  
 asteroid = display.newImage("Images/ast.png")  
 asteroid.width = 256  
 asteroid.height = 256  
 asteroid.x = mRandom(128,W-128)  
 asteroid.y = - 256  
 physics.addBody(asteroid, {0,0,0})  
 asteroid:setLinearVelocity(0,600)  
 asteroid.collision = astCollision  
 asteroid:addEventListener("collision", asteroid)  
 asteroid.type = "asteroid"  
end

local function frog()  
 frog = display.newImage("Images/Frog6.png")  
 frog.x = W/2  
 frog.y = H/1.2  
 frog.width = 96  
 frog.height = 128  
 physics.addBody(frog, "static", {0,0,0})  
 frog.type = "frog"  
end

local function background()  
 background = display.newImage("Images/Background.png")  
 background.x = W/2  
 background.y = H/2  
 background.width = W  
 background.height = H  
end

local function button()  
 button = display.newImage("Images/bluebutton.png")  
 button.x = W/2  
 button.y = H/2  
end

local function mainMenu()  
 background()  
 button()  
 text = display.newText("Play Game",W/2,H/2,nativesystemFont,64)  
end

local function mainGame()  
 background()  
 frog()  
 timer.performWithDelay(1000,asteroids,0)  
end

local function tryAgain()  
 background()  
 button()  
 text = display.newText("Try Again",W/2,H/2,nativesystemFont,64)  
end

mainGame()

hi @crumble619

The code starts from top to bottom.

Your tryAgain function is at the bottom. So it does not recognise your tryAgain functions yet.

put at the top with other declarations

local tryAgain — forward declaration

then your function instead of local function tryAgain() change to

tryAgain = function( )

Good Luck!

burhan

That fixes the problem, I appreciate the help!

I am now running into the problem of the mainGame() continuing to run when it switches to the tryAgain() screen. Is there a way to stop the timed spawned in mainGame() from spawning more asteroids and deleting the physics bodies that are already created when the first collision happens and the game switches to the tryAgain() scene?

This is the new code:

display.setStatusBar(display.HiddenStatusBar)

local physics = require(“physics”)
physics.start()
physics.setDrawMode(“hybrid”)
physics.setGravity(0,0)

local W = display.contentWidth
local H = display.contentHeight

local asteroid
local frog
local background
local button
local text

local mainMenu
local mainGame
local tryAgain

local mRandom=math.random

local function astCollision(self, event)
  if event.phase == “began” then
   if event.target.type == “asteroid” and event.other.type == “frog” then
  tryAgain()
  return true
  end
  end
 end

local function asteroidSpawn()
  asteroid = display.newImage(“Images/ast.png”)
  asteroid.width = 256
  asteroid.height = 256
  asteroid.x = mRandom(128,W-128)
  asteroid.y = - 256
  physics.addBody(asteroid, {0,0,0})
  asteroid:setLinearVelocity(0,600)
  asteroid.collision = astCollision
  asteroid:addEventListener(“collision”, asteroid)
  asteroid.type = “asteroid”
 end

local function frogSpawn()
  frog = display.newImage(“Images/Frog6.png”)
  frog.x = W/2
  frog.y = H/1.2
  frog.width = 96
  frog.height = 128
  physics.addBody(frog, “static”, {0,0,0})
  frog.type = “frog”
 end

local function backgroundSpawn()
  background = display.newImage(“Images/Background.png”)
  background.x = W/2
  background.y = H/2
  background.width = W
  background.height = H
 end

local function myTapListener(event)
  if (event.target) then
  mainGame()
  return true
  end
 end

local function buttonSpawn()
  button = display.newImage(“Images/bluebutton.png”)
  button.x = W/2
  button.y = H/2
  button:addEventListener(“tap”,myTapListener)
 end

mainMenu = function()
  backgroundSpawn()
  buttonSpawn()
  text = display.newText(“Play Game”,W/2,H/2,nativesystemFont,64)
 end

mainGame = function()
  backgroundSpawn()
  frogSpawn()
  timer.performWithDelay(1000,asteroidSpawn,0)
 end

tryAgain = function()
  backgroundSpawn()
  buttonSpawn()
  text = display.newText(“Try Again”,W/2,H/2,nativesystemFont,64)
 end

mainMenu()

You can name your timer then later you may cancel, pause or resume your timer as needed.

local mySpawnTimer  

mySpawnTimer =  timer.performWithDelay(1000,asteroidSpawn,0) 

then later use which one you want.

timer.cancel (mySpawnTimer )

timer.pause (mySpawnTimer )

timer.resume (mySpawnTimer )

Good Luck!

burhan

Thanks for the help!

If I use composer for having each scene in a different tab and destroying the scenes when they change, will that also automatically stop all running functions in the previous scene? I’m thinking that I should learn to use composer to make things easier on myself down the line.

Do I still have to stop timers and stuff after changing scenes with composer?

Appreciate the tips!

hi @crumble619

The code starts from top to bottom.

Your tryAgain function is at the bottom. So it does not recognise your tryAgain functions yet.

put at the top with other declarations

local tryAgain — forward declaration

then your function instead of local function tryAgain() change to

tryAgain = function( )

Good Luck!

burhan

That fixes the problem, I appreciate the help!

I am now running into the problem of the mainGame() continuing to run when it switches to the tryAgain() screen. Is there a way to stop the timed spawned in mainGame() from spawning more asteroids and deleting the physics bodies that are already created when the first collision happens and the game switches to the tryAgain() scene?

This is the new code:

display.setStatusBar(display.HiddenStatusBar)

local physics = require(“physics”)
physics.start()
physics.setDrawMode(“hybrid”)
physics.setGravity(0,0)

local W = display.contentWidth
local H = display.contentHeight

local asteroid
local frog
local background
local button
local text

local mainMenu
local mainGame
local tryAgain

local mRandom=math.random

local function astCollision(self, event)
  if event.phase == “began” then
   if event.target.type == “asteroid” and event.other.type == “frog” then
  tryAgain()
  return true
  end
  end
 end

local function asteroidSpawn()
  asteroid = display.newImage(“Images/ast.png”)
  asteroid.width = 256
  asteroid.height = 256
  asteroid.x = mRandom(128,W-128)
  asteroid.y = - 256
  physics.addBody(asteroid, {0,0,0})
  asteroid:setLinearVelocity(0,600)
  asteroid.collision = astCollision
  asteroid:addEventListener(“collision”, asteroid)
  asteroid.type = “asteroid”
 end

local function frogSpawn()
  frog = display.newImage(“Images/Frog6.png”)
  frog.x = W/2
  frog.y = H/1.2
  frog.width = 96
  frog.height = 128
  physics.addBody(frog, “static”, {0,0,0})
  frog.type = “frog”
 end

local function backgroundSpawn()
  background = display.newImage(“Images/Background.png”)
  background.x = W/2
  background.y = H/2
  background.width = W
  background.height = H
 end

local function myTapListener(event)
  if (event.target) then
  mainGame()
  return true
  end
 end

local function buttonSpawn()
  button = display.newImage(“Images/bluebutton.png”)
  button.x = W/2
  button.y = H/2
  button:addEventListener(“tap”,myTapListener)
 end

mainMenu = function()
  backgroundSpawn()
  buttonSpawn()
  text = display.newText(“Play Game”,W/2,H/2,nativesystemFont,64)
 end

mainGame = function()
  backgroundSpawn()
  frogSpawn()
  timer.performWithDelay(1000,asteroidSpawn,0)
 end

tryAgain = function()
  backgroundSpawn()
  buttonSpawn()
  text = display.newText(“Try Again”,W/2,H/2,nativesystemFont,64)
 end

mainMenu()

You can name your timer then later you may cancel, pause or resume your timer as needed.

local mySpawnTimer  

mySpawnTimer =  timer.performWithDelay(1000,asteroidSpawn,0) 

then later use which one you want.

timer.cancel (mySpawnTimer )

timer.pause (mySpawnTimer )

timer.resume (mySpawnTimer )

Good Luck!

burhan

Thanks for the help!

If I use composer for having each scene in a different tab and destroying the scenes when they change, will that also automatically stop all running functions in the previous scene? I’m thinking that I should learn to use composer to make things easier on myself down the line.

Do I still have to stop timers and stuff after changing scenes with composer?

Appreciate the tips!