So i'm working on a little game using onCollision event's :
local function onCollision(event)
if event.phase == "began" and gameIsActive == true then
local obj1 = event.object1;
local obj2 = event.object2;
if obj1.name == "bill" then
if obj2.name == "rocks" then gameover()
elseif obj2.name == "" then
end
end
end
end
Runtime:addEventListener( "collision", onCollision )
But i have a bit of a problem the code works as expected the first time it runs but is you restart the game i'm getting this error :
File: game.lua
Line: 649
Attempt to call global 'gameover' (a table value)
stack traceback: game.lua:649: in function <game.lua:643> ?: in function <?:221>
649 = if obj2.name == "rocks" then gameover()
643 = local function onCollision(event)
Any idea what this could be?
gameover function :
function gameover()
rightTAP:removeEventListener("tap", movePLAYERr)
leftTAP:removeEventListener("tap", movePLAYERl)
timer.pause(spawnBits)
timer.pause(Rockspawner1)
timer.pause(tmrscore)
timer.pause(updateScoretimer)
timer.pause(spawnDinosControll)
audio.pause( drillingChannel )
drillbg.isVisible = false
scoreText.isVisible = false
Restartg = display.newText( "Restart", 0, 0,nil, 20)
Restartg:setFillColor(0, 0, 0)
screenGroup:insert(Restartg)
Restartg.x= display.contentWidth/2
Restartg.y= display.contentHeight/2 + 160
Restartg:addEventListener("tap", RestartGame1)
end
The error message is clear: you are trying to call gameover and it has a table value instead of a function value. You get the same error is you run the following: gameover = {}; gameover().
This means that either you are not correctly defining gameover function or overwriting gameover value somewhere in your script.
if you have an object of functions you can call whit the key that you use to declare.
example
el = {}
print(el)
table: 0x7f8fe9e003f0
el.f1 = function()
print('hola')
end
el.f1()
hola
On restart you have to remove all Runtime listeners. Otherwise they will be still listening and it may cause errors, because they lead to already removed methods.
Place it in RestartGame1:
Runtime:removeEventListener( "collision", onCollision )
fixed by adding "transition.pause( thisRock )" to the gameover function
Related
I started learning and developing games using Corona SDK recently and right now I am facing an issue to collect coins in the gameplay. When the player object collides with the coin object the coin should be removed/disappeared. I tried the below code which is not successful whenever the coin collides with the player it throws an error
Attempt to call method 'translate' (a nil value)
Below is the code I used,
------Create Coins------
function coin()
token = display.newImage(sceneContainer, "gold.png")
token.x = math.random(320, 720)
token.y = math.random(160, 260)
token.myName = "token"
physics.addBody( token, "dynamic", { bounce=0, friction=1, radius=20 })
local function muovi()
token:translate(-2, 0)
end
Runtime:addEventListener( "enterFrame", muovi )
end
tmr = timer.performWithDelay(5000, coin, 0)
------Collision Function------
function onCollision( event )
if ( event.phase == "began" ) then
if event.object1.myName == "player" and event.object2.myName == "token" then
event.object2:removeSelf()
print("hitting 1")
elseif event.object1.myName == "token" and event.object2.myName == "player" then
event.object1:removeSelf()
print("hitting 1")
end
end
end
Runtime:addEventListener( "collision", onCollision)
Look at the gotchas in the Corona Documentation.
As a best practice, you should set the associated variable(s) to nil
after calling object:removeSelf().
When an object is removed, the rendering-related resources of the
removed object are deleted immediately. What remains of the object is
simply a plain Lua table with all non-display object properties — the
metatable is set to nil and all properties relating to display object
are removed. Thus, if there are still references to object in Lua,
they will simply be references to a normal Lua table.
Try setting the object to nil after calling removeSelf()
if ( event.phase == "began" ) then
if event.object1.myName == "player" and event.object2.myName == "token" then
event.object2:removeSelf()
event.object2 = nil
print("hitting 1")
elseif event.object1.myName == "token" and event.object2.myName == "player" then
event.object1:removeSelf()
event.object1 = nil
print("hitting 1")
end
end
end
If you read the second point you'll notice that you'll still have a plain Lua table with all non-display object properties, so my guess is that it is calling removeSelf twice. The first time it is removing the object from display, but it's not removing the myName field. Therefore when it's called a second time, it's trying to removeSelf again.
Goal
I have a global variable made to store the table returned by timer.performWithDelay. My goal was, in the scene:show() function for the timer to be cancelled, and recreated with a new delay.
Problem
I'm getting a nil return value in the variable used to store the table when the timer is recreated.
Code:
local timerVar
local function update()
print("updating")
print(timerVar)
timer.cancel(timerVar)
timerVar = timer.performWithDelay(delay, timerFunction, 0)
print(timerVar)
end
function scene:create(event)
timerVar = timer.performWithDelay(delay, timerFunction, 0)
end
function scene:show(event)
if (phase == "will") then
update()
timer.resume(timerVar)
end
end
function scene:hide(event)
if (phase == "will") then
timer.pause(timerVar)
end
end
Console output:
updating
table: 095D9CA8
nil
What's happening here?
Is timer.cancel() removing the the timerVar variable altogether?
If I can't keep the timer, how can I get around this so that I can have the timer table stored under the same name and with the same scope, but born anew?
I try reproduce your problem but got
updating
15:28:47.324 table: 0091F958
15:28:47.324 table: 0772C590
15:28:47.324 WARNING: timer.resume( timerId ) ignored because timerId was not paused.
My code:
main.lua
local composer = require( 'composer' )
composer.gotoScene( 'test' )
test.lua
local composer = require( "composer" )
local scene = composer.newScene()
local timerVar
local delay = 1000
local function timerFunction()
end
local function update()
print("updating")
print(timerVar)
timer.cancel(timerVar)
timerVar = timer.performWithDelay(delay, timerFunction, 0)
print(timerVar)
end
function scene:create( event )
local sceneGroup = self.view
timerVar = timer.performWithDelay(delay, timerFunction, 0)
end
function scene:show( event )
local sceneGroup = self.view
local phase = event.phase
if ( phase == "will" ) then
update()
timer.resume(timerVar)
elseif ( phase == "did" ) then
end
end
function scene:hide( event )
local sceneGroup = self.view
local phase = event.phase
if ( phase == "will" ) then
timer.pause(timerVar)
elseif ( phase == "did" ) then
end
end
function scene:destroy( event )
local sceneGroup = self.view
end
scene:addEventListener( "create", scene )
scene:addEventListener( "show", scene )
scene:addEventListener( "hide", scene )
scene:addEventListener( "destroy", scene )
return scene
Try install latest stable version of Corona.
This is not technically a direct answer to my question, but it did solve my problem, so I'll put my workaround here for future users. I was able to avoid cancelling the timer altogether by simply changing the delay of the timer. this is accomplished as simply as:
timerVar._delay = newDelay
Note the underbar before the delay variable, it's easy to miss.
This allowed me to update the delay without making a new timer.
recently I was coding a new game when I ran across a problem of which I cannot seem to be able to fix.
This is the code :
function newPower()
rand = math.random( 100 )
if (rand < 80) then
powerup = display.newImage("power.png");
powerup.class = "powerup"
powerup.x = 60 + math.random( 160 )
powerup.y = -100
physics.addBody( powerup, { density=0.9, friction=0.3, bounce=0.3} )
powerup:addEventListener( "touch", handlePowerTouch )
end
end
local function handlePowerTouch( event )
if event.phase == "began" then
currentScore = currentScore * 2
currentScoreDisplay.text = string.format( "%06d", currentScore )
event.target:removeSelf()
return true
end
end
local function spawnpowers()
-- Spawn a new powerup every second until canceled.
spawnPower = timer.performWithDelay( 1000, newPower, -1 )
end
Any help fixing this issue would be greatly appreciated!
The issue I'm having is when I click "run" or "play" the game starts working then crashes and displays this message:
addEventListener: listener cannot be nil: nil stack traceback:
?: in function 'addeventListener'
game.lua63: in function'_listener' <-- i have given you game.lua:63 above.
Thanks
powerup:addEventListener( "touch", handlePowerTouch )
Here handlePowerTouch is nil as the function definition follows after this line.
Move your function definition in front of that line, then it should work.
Btw, is there any reason why you have so many global variables? You should use local variables wherever possible.
I have a listener for loading remote images, but I need to be able to pass an ID number to that listener, and I'm not really sure how to do it. My code to retrieve the remote image is:
display.loadRemoteImage("http://www.newyorker.com/online/blogs/photobooth/NASAEarth-01.jpg", "GET", networkListener, "banner.png",system.TemporaryDirectory, (globalData.contentX * rows2) + globalData.contentX/2, 20 + (i - 1) % 6 * 140
And the listener I have is:
local function networkListener( event )
if ( event.isError ) then
print ( "Network error - download failed" )
else
local target = event.target
target.alpha = 0
transition.to( target, { alpha = 1.0 } )
target.width = 590
target.height = 110
target:addEventListener( "touch", target )
scrollView:insert(target)
function target:touch(event)
if event.phase == "began" then
display.getCurrentStage():setFocus( self )
self.isFocus = true
elseif self.isFocus then
if event.phase == "moved" then
numMoved = numMoved + 1
if(numMoved > 10) then
display.getCurrentStage():setFocus( nil )
self.isFocus = false
scrollView:takeFocus( event )
end
elseif event.phase == "ended" or event.phase == "cancelled" then
globalData.selectedLocationID = target.id --This needs to be the ID that I pass to this listener
if(globalData.approvedToggle == 1) then
storyboard.gotoScene("businessScene")
else
storyboard.gotoScene("locationScene")
end
display.getCurrentStage():setFocus( nil )
self.isFocus = false
end
end
return true
end
end
Any help in this matter would be greatly appreciated, thanks!
I did this a while back on an Ecommerence app I was making. I'm not to familer with storyboard, but I do remember using this API. If I remember correctly, you need to use event.target as the event listener and pass everything through there. I also remember that you can embed a function inside of the display.loadRemoteImage API like this:
itemImage = display.loadRemoteImage(itemData.imageURL, "GET",
function(event)
event.target.xScale = 0.4
event.target.yScale = 0.4
function openSite(event)
if event.phase == "ended" then
system.openURL( itemData.itemURL )
end
end
event.target:addEventListener( "tap", openSite )
end)
My advice is remove all the story board stuff, and try making the API work in a different document. I think that you need to simplify it so you aren't confused.
Hopefully this helps.
I am doing a game in corona SDK, but I have this little problem.
I have a menu with a button. If I press it, it sends me to the first level of my game.
When I pass the final level, the game return me to the menu. Bur, if I start playing the first again, my images doesn´t appear.
The images are balls, and to pass the level, you have to eliminate all the balls. To do this, I use:
ball:removeSlef()
ball = nil
But, I don´t think that this is the problem, because I eliminate this lines, and it doesn´t work.
The images are create in scene:createScene function, and insert in the Group.
I short the code of the first level to be understood.
local storyboard = require( "storyboard" )
local scene = storyboard.newScene()
local physics = require "physics"
physics.start(); physics.pause()
physics.setGravity( 0, 0 )
local cont = 0
local bur = {}
function eliminar1( event )
if (cont == 0) and (event.phase == "began") then
event.target:removeSelf()
bur[1] = nil
cont = cont + 1
end
end
function eliminar2( event )
if (cont == 1) and (event.phase == "began") then
bur[2]:removeSelf()
bur[2] = nil
cont = cont + 1
end
end
function eliminar3( event )
if (cont == 2) and (event.phase == "began") then
bur[3]:removeSelf()
bur[3] = nil
storyboard.gotoScene( "levels.1.level2" )
end
end
function scene:createScene ( event )
local screenGroup = self.view
for i = 1,3 do
bur[i] = display.newImage("irudiak/"..i..".png")
bur[i]:translate(math.random(0,280), math.random(0,400) )
physics.addBody( bur[i], {bounce = 0.3 } )
bur[i]:setLinearVelocity(math.random(-50,50), math.random(-50,50) )
screenGroup:insert(bur[i])
end
bur[1]:addEventListener("touch", eliminar1)
bur[2]:addEventListener("touch", eliminar2)
bur[3]:addEventListener("touch", eliminar3)
end
function scene:enterScene( event )
local screenGroup = self.view
physics.start()
end
function scene:exitScene( event )
local screenGroup = self.view
physics.stop()
end
function scene:destroyScene( event )
local screenGroup = self.view
package.loaded[physics] = nil
physics = nil
end
return scene
createScene is ran only first time when you gotoScene. Every next time only willEnterScene and enterScene are played. To play createScene again you have to remove it (storyboard.removeScene() I guess). Or you can move some stuff you need to willEnterScene. For more detailed info you can watch this: http://www.coronalabs.com/blog/2013/08/20/tutorial-reloading-storyboard-scenes/