Corona SDK: Remove display object from table - coronasdk

I have been working on a tower defense game. So basically I have a loop that makes a table of display objects that are tanks and adds physics bodies to them. Then it "gives the tanks" to a function called moveTank to actually move them.
However to conserve memory I want to remove the physics body and display obejct itself once it is offscreen. The code for that stars at line 234. In the simulator, if you test just removing the physics body everything works fine. However, if I remove the display object (maybe I'm doing it wrong?) it removes the image, but some other functions that use it give an error saying that I'm trying to compare a number with a nil value# line 229. The nil value is the object is removed and I set its property isMoving and isAlive to false so why is it even trying to perform the operation??
Can anyone help me finish this part of trying to remove the display object with no errors?
local storyboard = require( "storyboard" )
local scene = storyboard.newScene()
local physics = require "physics"
physics.start()
physics.setGravity( 0, 0)
physics.setDrawMode("hybrid")
local screenW, screenH, halfW, halfH = display.contentWidth, display.contentHeight, display.contentWidth*0.5, display.contentHeight*0.5
local tanks = {}
local tickCnt = 0
local TOTAL_TANKS = 2
local tankCnt = 1
function AddCommas( number, maxPos )
local s = tostring( number )
local len = string.len( s )
if len > maxPos then
-- Add comma to the string
local s2 = string.sub( s, -maxPos )
local s1 = string.sub( s, 1, len - maxPos )
s = (s1 .. "," .. s2)
end
maxPos = maxPos - 3 -- next comma position
if maxPos > 0 then
return AddCommas( s, maxPos )
else
return s
end
end
function tickCntFunct()
if tickCnt <= 2000 then
tickCnt = tickCnt + 25
print("tick count", tickCnt)
else
tickCnt = 0
end
end
timer.performWithDelay(10, tickCntFunct, 0)
function moveTank(aTank)
local mSpeed = 150
rSpeed = 6
if aTank.isAlive == true and aTank.isKilled == false then
print("aTank.x = ", aTank.x)
print("aTank.y = ", aTank.y)
print("tank rotation = ", aTank.rotation)
local disP1 = math.sqrt((math.abs(aTank.x - pointer1.x))^2 + (math.abs(aTank.y - pointer1.y))^2)
local disP2 = math.sqrt((math.abs(aTank.x - pointer2.x))^2 + (math.abs(aTank.y - pointer2.y))^2)
local disP3 = math.sqrt((math.abs(aTank.x - pointer3.x))^2 + (math.abs(aTank.y - pointer3.y))^2)
local disP4 = math.sqrt((math.abs(aTank.x - pointer4.x))^2 + (math.abs(aTank.y - pointer4.y))^2)
removeTanknumber = 0
if aTank.x < 0 then
aTank.rotation = 90
aTank:setLinearVelocity( mSpeed, 0)
end
if aTank.rotation < 180 and disP1 < 1 then
aTank:setLinearVelocity( 0, 0)
aTank.rotation = aTank.rotation + rSpeed
end
if aTank.rotation >= 180 and disP1 < 1 then
aTank.rotation = 180
aTank:setLinearVelocity( 0, mSpeed)
end
if aTank.rotation > 90 and disP2 < 1 then
aTank:setLinearVelocity( 0, 0)
aTank.rotation = aTank.rotation - rSpeed
end
if aTank.rotation <= 90 and disP2 < 1 then
aTank.rotation = 90
aTank:setLinearVelocity( mSpeed, 0)
end
if aTank.rotation > 0 and disP3 < 1 then
aTank:setLinearVelocity( 0, 0)
aTank.rotation = aTank.rotation - rSpeed
end
if aTank.rotation <= 0 and disP3 < 1 then
aTank.rotation = 0
aTank:setLinearVelocity( 0, -1*mSpeed)
end
if aTank.rotation < 90 and disP4 < 1 then
aTank:setLinearVelocity( 0, 0)
aTank.rotation = aTank.rotation + rSpeed
end
if aTank.rotation >= 90 and disP4 < 1 then
aTank.rotation = 90
aTank:setLinearVelocity( mSpeed, 0)
end
end
end
timer.performWithDelay(1, moveTank, 0)
function scene:createScene( event )
local group = self.view
pointersGroup = display.newGroup()
mapGroup = display.newGroup()
-- create a grey rectangle as the backdrop
map = display.newImage( "map1.png" )
map:setReferencePoint(display.TopLeftReferencePoint)
map.x = 0
map.y = 0
spawner = display.newImage("pointer.png")
spawner:setReferencePoint(display.CenterReferencePoint)
spawner.y = 210
spawner.x = -40
pointer1 = display.newImage("pointer.png")
pointer1:setReferencePoint(display.CenterReferencePoint)
pointer1.x = 210
pointer1.y = 210
pointer2 = display.newImage("pointer.png")
pointer2:setReferencePoint(display.CenterReferencePoint)
pointer2.x = 210
pointer2.y = 390
pointer3 = display.newImage("pointer.png")
pointer3:setReferencePoint(display.CenterReferencePoint)
pointer3.x = 510
pointer3.y = 390
pointer4 = display.newImage("pointer.png")
pointer4:setReferencePoint(display.CenterReferencePoint)
pointer4.x = 510
pointer4.y = 90
sideBlock = display.newImage("side_block.png")
physics.addBody( sideBlock, "static", { friction=0.5, bounce=0.3 } )
sideBlock:setReferencePoint(display.CenterReferencePoint)
sideBlock.x = screenW - 100
sideBlock.y = screenH/2
-- all display objects must be inserted into group
pointersGroup:insert( spawner )
pointersGroup:insert( pointer1 )
pointersGroup:insert( pointer2 )
pointersGroup:insert( pointer3 )
pointersGroup:insert( pointer4 )
pointersGroup:insert( sideBlock )
mapGroup:insert( map )
group:insert( pointersGroup )
group:insert( mapGroup )
end
function scene:enterScene( event )
local group = self.view
for i = 1, TOTAL_TANKS do
-- create 5 tanks, place them off screen, set their status to isAlive and not isMoving
table.insert(tanks, display.newImage("tank.png"))
print("memory (all spawned): ", AddCommas( system.getInfo("textureMemoryUsed"), 9 ) .. " bytes" )
tanks[i]:setReferencePoint(display.CenterReferencePoint)
tanks[i]:scale(0.75, 0.75)
tanks[i].x = spawner.x
tanks[i].y = spawner.y
tanks[i].isAlive = true
tanks[i].isMoving = false
tanks[i].isKilled = false
end
local function gameLoop(event)
-- normally should not have loops inside the game loop, because
-- if there is too much going on during each frame call it can cause issues
-- but for moving only 5 tanks, this is how you can do it.
-- each tank will call the same function above (moveTank)
-- have a variable that you would check here, to see if 'so many ticks or seconds'
-- has passed, and if so, set the isMoving to true for the next tank .. will just use
-- a simple incremented variable 'tickCnt'
if tickCnt > 2000 and tankCnt <= TOTAL_TANKS then
physics.addBody( tanks[tankCnt], { density=3.0, friction=0.5, bounce=0.3 } )
tanks[tankCnt].isMoving = true
print("tankCnt= ", tankCnt)
tankCnt = tankCnt + 1
print("memory (moving): ", AddCommas( system.getInfo("textureMemoryUsed"), 9 ) .. " bytes" )
end
for i=1, TOTAL_TANKS do
if tanks[i].isMoving == true and tanks[i].isAlive == true and tanks[i].isKilled == false then
moveTank(tanks[i])
end
end
for i = 1, TOTAL_TANKS do
if tanks[i].x >= 40 and tanks[i].isKilled == false then
tanks[i].isKilled = true
tanks[i].isMoving = false
tanks[i].isAlive = false
physics.removeBody( tanks[i] )
display.remove(tanks[i])
tanks[i] = nil
end
end
end
Runtime:addEventListener("enterFrame", gameLoop)
physics.start()
end
function scene:exitScene( event )
local group = self.view
physics.stop()
end
function scene:destroyScene( event )
local group = self.view
package.loaded[physics] = nil
physics = nil
end
--------------------------------------------------------------------------------------- --
-- END OF YOUR IMPLEMENTATION
-----------------------------------------------------------------------------------------
-- "createScene" event is dispatched if scene's view does not exist
scene:addEventListener( "createScene", scene )
-- "enterScene" event is dispatched whenever scene transition has finished
scene:addEventListener( "enterScene", scene )
-- "exitScene" event is dispatched whenever before next scene's transition begins
scene:addEventListener( "exitScene", scene )
-- "destroyScene" event is dispatched before view is unloaded, which can be
-- automatically unloaded in low memory situations, or explicitly via a call to
-- storyboard.purgeScene() or storyboard.removeScene().
scene:addEventListener( "destroyScene", scene )
-----------------------------------------------------------------------------------------
return scene

Try to loop from last tank to first.

Related

Corona SDK / Lua : An table's property is nil, when accessed via event.other during collision event. But why?

So I have this module, where all of its activity during the game is in. In t.physics I add a collision event listener (differentiating if target is a group or a single object). When the concerning objects detect a collision though, the property col of the other object (event.other) seems to be nil, although I initially set it to a string representing a color in t.create. I just can't find the cause for that, can anyone?
Thanks for your help.
Greetings, Nils
local fence = require("lib.fence")
local physics = require("physics")
local t = {}
local stages = {yellow = 1, lila = 1, red = 1}
local sizes = {1, 3.625, 7.25}
t.colors = {"yellow", "lila", "red"}
t.growing = false
t.setSize = function(fill)
local tHeight = fill.contentHeight * sizes[stages[fill.col]]
local tScale = tHeight / fill.contentHeight
fill.yScale = tScale
end
t.grow = function(group, color, hero)
local counter = 0
stages[color] = stages[color] + 1
for i = 1, group.numChildren, 1 do
if group[i].col == color then
counter = counter + 1
local function newPhysics() t.physics(group) end
if counter == 1 then
local function reset() t.growing = false if stages[color] == 3 then stages[color] = 1; newPhysics(); end end
local function start() t.growing = true end
transition.to(group[i], {time = 260, yScale = sizes[stages[color]], onStart = start, onComplete = reset})
else
transition.to(group[i], {time = 250, yScale = sizes[stages[color]], onStart = start})
end
end
end
end
t.physics = function(target)
if target.numChildren == nil then
physics.removeBody(target)
local function add()
physics.addBody( target, "static", {isSensor = true} )
target.collision = function(self, event)
if event.phase == "began" then
target.count = target.count + 1
if target.count == 1 then
t.grow(target.parent, self.col, event.other)
end
elseif event.phase == "ended" then
target.count = 0
end
end
end
timer.performWithDelay(1, add, 1)
else
for i = 1, target.numChildren, 1 do
physics.removeBody( target[i] )
physics.addBody( target[i], "static", {isSensor = true} )
target[i].name = "fill"
local fill = target[i]
fill.count = 0
fill.collision = function(self, event)
if event.phase == "began" then
self.count = self.count + 1
if self.count == 1 and event.other.x ~= nil then
t.grow(target, self.col, event.other)
end
else
fill.count = 0
end
end
fill:addEventListener("collision")
end
end
end
t.setColor = function(fill)
local colors = {
{238 / 255, 228 / 255, 28 / 255},
{38 / 255, 33 / 255, 77 / 255},
{175 / 255, 24 / 255, 52 / 255},
}
local names = {"yellow", "lila", "red"}
local r = math.random(3)
fill.fill = colors[r]
fill.col = names[r]
end
t.create = function(fences, group, colors)
local fills = {}
for i = 1, #fences, 1 do
local rCol = math.random(3)
local col
if rCol == 1 then
col = colors.yellow
elseif rCol == 2 then
col = colors.lila
else
col = colors.red
end
fills[i] = display.newRect(
group, fences[i].x + fences[i].contentWidth * 0.125, fences[i].y,
fences[i].contentWidth * 0.9, (fences[i].contentHeight * 0.5 / 3)
)
fills[i].dPosX = fills[i].x
fills[i].y = display.contentHeight- fills[i].contentHeight / 2
fills[i].fill = col
fills[i].col = t.colors[rCol]
fills[i].increased = false
end
return fills
end
t.move = function(fills, fences, group)
for i = 1, #fills, 1 do
local fill = fills[i]
function fill:enterFrame()
self:translate(fence.speed, 0)
if t.growing == false then
t.setSize(self)
end
if self.x > display.contentWidth + 0.55 * fences[i].contentWidth then
local xT = {}
for i = 1, group.numChildren, 1 do
xT[i] = group[i].x
end
local function compare(a, b) return a < b end
table.sort(xT, compare)
self.x = xT[1] - fences[i].contentWidth * 0.98
t.setColor(self)
local function newPhysics() t.physics(self) end
timer.performWithDelay( 25, newPhysics, 1 )
self:toBack()
end
end
Runtime:addEventListener("enterFrame", fill)
end
end
return t
Solved. Ugh, sorry, I forgot to define the property on the other object involved (hero), that has its own module. What a stupid slip.
Thanks for your answers anyways!
You don't seem to have any dynamic bodies here. What is colliding with what? Could it be that the other object involved in the collision (the value of event.other) is not something initialized in t.create() and so doesn't have the col property?
From the Corona documentation on Collision Detection:
Some body types will — or will not — collide with other body types. In a collision between two physical objects, at least one of the objects must be dynamic, since this is the only body type which collides with any other type.
Also, in your fill.collision(), I think you want to pass event.target as the first argument to t.grow() rather than target. If you try things, please update the question with more information.

AddEventListener: Listener cannot be nil

Can someone please explain what is going wrong here? It went wrong as soon as I added the AddEventListener
newBalloon:addEventListener( "tap", pushBalloon )
Complete Code:
local composer = require( "composer" )
local scene = composer.newScene()
local physics = require( "physics" )
physics.start()
-- Configure image sheet
local positioninsheetOptions = 144.1
local sheetOptions =
{
frames =
{
{
x = 0,
y = 0,
width = 112,
height = 142
},
{
x = 0,
y = positioninsheetOptions,
width = 112,
height = 142
},
{
x = 0,
y = positioninsheetOptions*2,
width = 112,
height = 142
},
{
x = 0,
y = positioninsheetOptions*3,
width = 112,
height = 142
},
{
x = 0,
y = positioninsheetOptions*4,
width = 112,
height = 142
},
{
x = 0,
y = positioninsheetOptions*5,
width = 112,
height = 142
},
{
x = 0,
y = positioninsheetOptions*6,
width = 112,
height = 142
},
{
x = 0,
y = positioninsheetOptions*7,
width = 112,
height = 142
},
{
x = 0,
y = positioninsheetOptions*8,
width = 112,
height = 142
},
{
x = 0,
y = positioninsheetOptions*9,
width = 112,
height = 142
},
{
x = 0,
y = positioninsheetOptions*10,
width = 112,
height = 142
},
},
}
local objectSheet = graphics.newImageSheet( "gameObjects.png", sheetOptions )
local tapCount = 0
local platform
local tapText
local balloonsTable = {}
local leftBorder
local rightBorder
local backGroup
local mainGroup
local uiGroup
local platform
local platform2
local function createBalloon()
local randomBalloon = math.random( 10 )
local newBalloon = display.newImageRect( objectSheet, randomBalloon, 112, 142 )
if newBalloon then
table.insert( balloonsTable, newBalloon )
physics.addBody( newBalloon, "dynamic", { radius=70, bounce=0 } )
newBalloon.myName = "bigBalloon"
newBalloon.alpha = 0.75
newBalloon.gravityScale = randomBalloon/-150
newBalloon:addEventListener( "tap", pushBalloon )
end
local whereFrom = math.random( 3 )
if ( whereFrom == 1 ) then
-- From the left
newBalloon.x = 100
newBalloon.y = display.contentHeight+300
elseif ( whereFrom == 2 ) then
-- From the top
newBalloon.x = 160
newBalloon.y = display.contentHeight+300
elseif ( whereFrom == 3 ) then
-- From the right
newBalloon.x = 220
newBalloon.y = display.contentHeight+300
end
end
local function gameLoop()
-- Create new balloon
createBalloon()
-- Remove balloons which have drifted off screen
for i = #balloonsTable, 1, -1 do
local thisBalloon = balloonsTable[i]
if ( thisBalloon.x < -100 or
thisBalloon.x > display.contentWidth + 100 or
thisBalloon.y < -100 )
then
display.remove( thisBalloon )
table.remove( balloonsTable, i )
end
end
end
local function pushBalloon( event )
local tappedBalloon = event.target
-- balloon:applyLinearImpulse( 0.2, -2, balloon.x, balloon.y )
-- tapCount = tapCount + 1
-- tapText.text = tapCount
if event.phase == "began" then
tappedBalloon.gravityScale = 10
end
end
local function pushBalloon2()
-- balloon:applyLinearImpulse( 0.2, -2, balloon.x, balloon.y )
-- tapCount = tapCount + 1
-- tapText.text = tapCount
balloon2.gravityScale = 10
balloon2:applyLinearImpulse( 0.1, 0, balloon2.x, balloon2.y )
end
local function pushBalloon3()
-- balloon:applyLinearImpulse( 0.2, -2, balloon.x, balloon.y )
-- tapCount = tapCount + 1
-- tapText.text = tapCount
balloon3.gravityScale = 10
balloon3:applyLinearImpulse( -0.1, 0, balloon3.x, balloon3.y )
end
-- -----------------------------------------------------------------------------------
-- Scene event functions
-- -----------------------------------------------------------------------------------
-- create()
function scene:create( event )
local sceneGroup = self.view
-- Code here runs when the scene is first created but has not yet appeared on screen
physics.pause()
local background = display.newImageRect( "background.png", 360, 570 )
background.x = display.contentCenterX
background.y = display.contentCenterY
platform = display.newImageRect( "platform.png", 10, display.contentHeight*5 )
platform.x = -5
platform.y = 0
physics.addBody( platform, "static", { friction=0.5, bounce=0.3 } )
platform2 = display.newImageRect( "platform.png", 10, display.contentHeight*5 )
platform2.x = display.contentWidth+5
platform2.y = 0
physics.addBody( platform2, "static", { friction=0.5, bounce=0.3 } )
createBalloon()
end
-- show()
function scene:show( event )
local sceneGroup = self.view
local phase = event.phase
if ( phase == "will" ) then
-- Code here runs when the scene is still off screen (but is about to come on screen)
elseif ( phase == "did" ) then
gameLoopTimer = timer.performWithDelay( 1250, gameLoop, 0 )
-- Code here runs when the scene is entirely on screen
physics.start()
end
end
-- hide()
function scene:hide( event )
local sceneGroup = self.view
local phase = event.phase
if ( phase == "will" ) then
-- Code here runs when the scene is on screen (but is about to go off screen)
elseif ( phase == "did" ) then
-- Code here runs immediately after the scene goes entirely off screen
physics.pause()
end
end
-- destroy()
function scene:destroy( event )
local sceneGroup = self.view
-- Code here runs prior to the removal of scene's view
end
-- -----------------------------------------------------------------------------------
-- Scene event function listeners
-- -----------------------------------------------------------------------------------
scene:addEventListener( "create", scene )
scene:addEventListener( "show", scene )
scene:addEventListener( "hide", scene )
scene:addEventListener( "destroy", scene )
-- -----------------------------------------------------------------------------------
return scene
Thank you.
You refer to function (pushBalloon) do not exist yet when add event listener. So put definition of function above line you add event listener like that
local function pushBalloon( event )
local tappedBalloon = event.target
-- balloon:applyLinearImpulse( 0.2, -2, balloon.x, balloon.y )
-- tapCount = tapCount + 1
-- tapText.text = tapCount
if event.phase == "began" then
tappedBalloon.gravityScale = 10
end
end
...
newBalloon:addEventListener( "tap", pushBalloon )
At the point in the file where you are adding the listener, pushBalloon() is not defined. You should add a forward declaration earlier in the file:
local scene = ...
local pushBalloon
...
-- createBalloon() and other existing code
...
pushBalloon = function ( event )
-- your function code
end

Failed to back to Menu from Game Over . Score unable to display on game over screen

I'm very new to corona. I learned all from the web and this is what i produced. The game seems fine but when game over screen is displayed, the button i put to back to menu scene doesn't work and the score that player get failed to be displayed to..
Here is my code...someone kindly please help me out.. what code should i change or add to it??
Thank you. Any help is much appreciated.
local composer = require( "composer" )
local scene = composer.newScene()
local physics = require("physics")
local widget = require "widget"
physics.start()
rand = math.random( 20 )
local slap_sound = audio.loadSound("Sound/slap2.mp3")
local ow = audio.loadSound("Sound/ow.mp3")
local buttonSound = audio.loadSound("Sound/sound2.mp3")
local background
local back
local count={total1=0,total=0,touch=0,life=3}
local total
local time_remain = 5
local mossie
local bee
local shade
local gameOverScreen
local winScreen
local countdown
local life
local pauseBtn
local resumeBtn
local gametmr
---------------------------------------------------------------------------------
-- All code outside of the listener functions will only be executed ONCE
-- unless "composer.removeScene()" is called.
---------------------------------------------------------------------------------
local gameOver = function()
composer.removeScene("easy")
physics.pause()
--audio.play(gameOverSound)
background = display.newImageRect( "Images/bg.jpg", display.contentWidth, display.contentHeight )
background.anchorX = 0
background.anchorY = 0
background.x, background.y = 0, 0
gameOverScreen = display.newImage("Images/gameover.png",400,300)
gameOverScreen.x = 160
gameOverScreen.y = 240
gameOverScreen.alpha = 0
transition.to(gameOverScreen,{time=500,alpha=1})
--total.isVisible = true
total.text="Score : "..count.touch
total.x = 160
total.y = 400
--total:setTextColor(000000)
botwall.isVisible = false
mossie.isVisible = false
bee.isVisible = false
life.isVisible = false
countdown.isVisible = false
pauseBtn.isVisible = false
resumeBtn.isVisible = false
local myButton = widget.newButton
{
left = 100,
top = 100,
id = "myButton",
label = "Menu",
onEvent = handleButtonEvent
}
myButton.isVisible = true
end
local function handleButtonEvent( event )
if ( "ended" == event.phase ) then
composer.gotoScene ("menu")
end
end
local function countDown(e)
time_remain = time_remain-1
countdown.text = time_remain
if time_remain == 0 then
gameOver()
end
end
local pauseGame = function(e)
if(e.phase=="ended") then
audio.play(buttonSound)
physics.pause()
timer.pause(gametmr)
pauseBtn.isVisible = false
resumeBtn.isVisible = true
return true
end
end
local resumeGame = function(e)
if(e.phase=="ended") then
audio.play(buttonSound)
physics.start()
timer.resume(gametmr)
pauseBtn.isVisible = true
resumeBtn.isVisible = false
return true
end
end
local collisionListener=function(self,event)
if(event.phase=="began")then
if(event.other.type=="mossie")then
audio.play(ow)
count.life=count.life-1
if(count.life==0) then
gameOver()
end
event.other:removeSelf()
event.other=nil
else
event.other:removeSelf()
event.other=nil
end
end
end
function onTouch(mossie)
audio.play(slap_sound)
count.touch=count.touch+1
total.text="Score : "..count.touch
mossie.target:removeSelf()
end
function killIt(e)
if(e.phase == "ended") then
gameOver()
end
end
local bottomWall = function()
botwall=display.newImage("Images/tangan.png")
botwall.x = 160
botwall.y = 500
botwall:setFillColor(22,125,185,255)
botwall.type="botwall"
botwall.collision=collisionListener
physics.addBody(botwall,"static",{ density=100.0, friction=0.0, bounce=0.0} )
botwall:addEventListener("collision",botwall)
end
local function newMossie(event)
total.text="Score : "..count.touch
life.text="Life : "..count.life
mossie = display.newImage("Images/biasa.png")
mossie.x = 60 + math.random( 160 )
mossie.y = -100
mossie.type="mossie"
mossie:setFillColor(255,0,0)
physics.addBody( mossie, { density=0.3, friction=0.2, bounce=0.5} )
mossie.name = "mossie"
mossie:addEventListener("touch",onTouch)
end
local function newBee(event)
bee = display.newImage("Images/lebah.png")
bee.x = 60 + math.random( 160 )
bee.y = -100
bee.type="other"
physics.addBody( bee, { density=1.4, friction=0.3, bounce=0.2} )
bee:addEventListener("touch",killIt)
end
-- local forward references should go here
---------------------------------------------------------------------------------
-- "scene:create()"
function scene:create( event )
local sceneGroup = self.view
background = display.newImageRect( "Images/bg.jpg", display.contentWidth, display.contentHeight )
background.anchorX = 0
background.anchorY = 0
background.x, background.y = 0, 0
total=display.newText("Score : 0",display.contentWidth * 0.5, 20, "Arial", 26)
total:setTextColor(000000)
countdown=display.newText(time_remain ,display.contentWidth * 0.9, 20, "Arial", 26)
countdown:setTextColor(000000)
life = display.newText("Life : 3 " ,display.contentWidth * 0.5, 50, "Arial", 26)
life:setTextColor(000000)
pauseBtn = display.newImage("Images/pause.png")
pauseBtn.x = display.contentWidth * 0.1
pauseBtn.y = display.contentHeight - 450
resumeBtn = display.newImage("Images/playb.png")
resumeBtn.x = display.contentWidth * 0.1
resumeBtn.y = display.contentHeight - 450
botwall=display.newImage("Images/tangan.png")
botwall.x = 160
botwall.y = 500
botwall:setFillColor(22,125,185,255)
botwall.type="botwall"
botwall.collision=collisionListener
physics.addBody(botwall,"static",{ density=100.0, friction=0.0, bounce=0.0} )
sceneGroup:insert(background)
sceneGroup:insert(total)
sceneGroup:insert(countdown)
sceneGroup:insert(life)
sceneGroup:insert(pauseBtn)
sceneGroup:insert(resumeBtn)
sceneGroup:insert(botwall)
resumeBtn.isVisible = false
pauseBtn:addEventListener("touch", pauseGame)
resumeBtn:addEventListener("touch", resumeGame)
botwall:addEventListener("collision",botwall)
dropMossie = timer.performWithDelay( 2000 , newMossie, -1 )
dropBee = timer.performWithDelay( 1800 , newBee, -1)
gametmr = timer.performWithDelay(1000, countDown, -1)
-- 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
--physics.stop()
--timer.cancel(gametmr)
--pauseBtn:removeEventListener("touch", pauseGame)
--resumeBtn:removeEventListener("touch", resumeGame)
--botwall:removeEventListener("collision",botwall)
--bee:removeEventListener("touch",killIt)
--mossie:removeEventListener("touch",onTouch)
-- 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
I suggest to make another composer scene for gameOverScreen (just like you did with these code block you post). Then do a composer.gotoScene(gameOver) read more here. And for the score I suggest that you make something like this.
The funtion handleButtonEvent is declared after you are creating a button widget. You can get the desired result by moving the function about the widget code.
local function handleButtonEvent( event )
if ( "ended" == event.phase ) then
composer.gotoScene ("menu")
end
end
local myButton = widget.newButton
{
left = 100,
top = 100,
id = "myButton",
label = "Menu",
onEvent = handleButtonEvent
}
myButton.isVisible = true
end

Saving values to another document in Lua with Corona SDK

I'm trying to save highscores to a table whenever a gameOver function is called.
When I go back in the app and try to read the highscores(They're displayed as newText where text is set to the proper highscore of the level.).
But it's not doing what it's supposed to. I can read the the levels highscores but when I try to change the values in the table, the highscore text doesn't change.
I have my main.lua and a myData.lua - The highscore table should be placed in the myData.lua in order to reach it from all the levels in the game.
this is my table
highScores = {
1,
2,
3,
4,
5,
6,
7
}
and I was trying to save/change the value with
highScores[1] = score
Where score is the score count in-game.
I've realized this is not the way to go about it, and what I can find on google seems to overly complicated for what I see as a simple task.
What am I doing wrong?
This is my entire level1.lua - The actual level that's running and trying to save it's score to the highScore table level1.lua):
local composer = require( "composer" )
local scene = composer.newScene()
local myData = require( "myData" )
local physics = require("physics")
physics.setDrawMode( "hybrid" )
-- forward references
local w = display.actualContentWidth
local h = display.actualContentHeight
local dropCount = 0
local spawnShit = 0
local allowDrop = 1
local spawnTime = 17
local countdownTimer
local score
local gX = 0
local gY = 0
score = 0
local countDownNumber = 10
local gameOver
local scoreT = display.newText( {text="Score: "..score, font=system.nativeSystemFont, fontSize=14,} )
scoreT.x = w * 0.5
scoreT.y = h * 0.1
local countDownText = display.newText( {text="", font=system.nativeSystemFont, fontSize=14} )
countDownText.x = w * 0.5
countDownText.y = h * 0.2
local drop01 = display.newImage("drop01.png")
drop01.x = -100
local drop02 = display.newImage("drop02.png")
drop02.x = -100
local drop03 = display.newImage("drop03.png")
drop03.x = -100
local drop04 = display.newImage("drop04.png")
drop04.x = -100
local timerSpawn
local timer2
-- Display objects
local background = display.newImage( "bluebg.png" )
background.x = w*0.5
background.y = h*0.5
background.width = w
background.height = h
local bckBtn = display.newText({text="<--BACK", font=system.nativeSystemFont, fontSize=14})
bckBtn.x = 50
bckBtn.y = 20
local egon = display.newImage( "Egon.png" )
egon.x = w*0.5
egon.y = h*0.85
egon.width = 100
egon.height = 97
local destroyAll = display.newRect( 0, h, w, 10 )
destroyAll.width = w*2
destroyAll.alpha = 0
local overlayBg = display.newRect( -500, -500, w, h )
overlayBg:setFillColor( 0, 0, 0 )
overlayBg.alpha = 0.4
--functions
function gameOver ()
if timerSpawn == nil then
else
timer.cancel(timerSpawn)
timerSpawn = nil
spawnShit = 0
end
if countdownTimer == nil then
else
timer.cancel(countdownTimer)
countdownTimer = nil
end
highScores[1] = score
transition.to( overlayBg, {x=w/2, y=h/2, time=500 } )
end
function goBack (event)
if "began" == event.phase then
gameOver()
if timerSpawn == nil then
else
timer.cancel(timerSpawn)
end
if countdownTimer == nil then
else
timer.cancel(countdownTimer)
end
elseif event.phase == "ended" then
timer2 = timer.performWithDelay(1000, function()
composer.gotoScene("select", "fade", 500)
end)
if overlayBg == nil then
else
overlayBg:removeSelf( )
end
return true
end
return true
end
function moveEgon (event)
if "moved" == event.phase then
egon.x = event.x
end
end
------------------------------------------------vvv---------------------------------------------------
------------------------------------------------vvv---------------------------------------------------
------------------------------------------------vvv---------------------------------------------------
function spawnObjects (event)
dropCount = math.random(1,4)
--if stopTimer == 1 then
-- timerSpawn = nil
--spawnShit = nil
--end
if spawnShit == 1 then
print( 'spawnShit' )
if dropCount == 1 then
-- Drop01 function and settings
drop01 = display.newImage( "drop01.png" )
drop01.x = math.random(10, 470)
drop01.y = -40
drop01.width = 50
drop01.height = 50
drop01.myName = "01"
physics.addBody( drop01, "dynamic", {density=0.9, friction=0.1, bounce=0.8 } )
elseif dropCount == 2 then
--Do shit for drop02
drop02 = display.newImage( "drop02.png" )
drop02.x = math.random(10, 470)
drop02.y = -40
drop02.width = 50
drop02.height = 50
drop02.myName = "02"
physics.addBody( drop02, "dynamic", {density=0.9, friction=0.1, bounce=0.8 } )
elseif dropCount == 3 then
drop03 = display.newImage( "drop03.png" )
drop03.x = math.random(10, 470)
drop03.y = -40
drop03.width = 50
drop03.height = 50
drop03.myName = "03"
physics.addBody( drop03, "dynamic", {density=0.9, friction=0.1, bounce=0.8 } )
elseif dropCount == 4 then
drop04 = display.newImage( "drop04.png" )
drop04.x = math.random(10, 470)
drop04.y = -40
drop04.width = 50
drop04.height = 50
drop04.myName = "04"
physics.addBody( drop04, "dynamic", {density=0.9, friction=0.1, bounce=0.8 } )
end
end
return true
end
---------------------------------------------------------------------------------------------------
---------------------------------------------------------------------------------------------------
---------------------------------------------------------------------------------------------------
function onCollision (event)
if "began" == event.phase then
--v--do shit when touching surface
if event.other.myName == "01" then
-- Do shit for drop01 --
-- Change score, powersups etc
event.other:removeSelf( )
score = score+1
countDownNumber = countDownNumber + 10
scoreT.text = "Score: "..score
end
if event.other.myName == "02" then
-- Do shit for drop02 --
-- Change score, powersups etc
event.other:removeSelf( )
score = score+1
scoreT.text = "Score: "..score
end
if event.other.myName == "03" then
-- Do shit for drop03 --
-- Change score, powersups etc
event.other:removeSelf( )
score = score-1
scoreT.text = "Score: "..score
end
if event.other.myName == "04" then
-- Do shit for drop04 --
-- Change score, powersups etc
event.other:removeSelf( )
score = score-1
scoreT.text = "Score: "..score
end
elseif "ended" == event.phase then
-- Do shit when leaving surfaces
end
return true
end
------------------------------------------------vvv---------------------------------------------------
------------------------------------------------vvv---------------------------------------------------
------------------------------------------------vvv---------------------------------------------------
function showCountDown (event)
-- Condition to show and hide countdown
if countDownNumber <= 1 or score == -1 then
spawnShit = 0
countDownNumber = 0
timer.cancel(timerSpawn)
timer.cancel(countdownTimer)
countdownTimer = nil
highScores[1] = score
print( 'NO MORE SPAAAAAAAAAAAAAAAWWNS' )
end
if countDownNumber >= 1 then
countDownNumber = countDownNumber -1
countDownText.text = countDownNumber
spawnShit = 1
end
return true
end
---------------------------------------------------------------------------------------------------
---------------------------------------------------------------------------------------------------
---------------------------------------------------------------------------------------------------
function destroy (event)
if "began" == event.phase then
event.other:removeSelf( )
else if "ended" == event.phase then
end
return true
end
end
--function scene:create( event )
function scene:create( event )
local sceneGroup = self.view
-- Initialize the scene here.
-- Example: add display objects to "sceneGroup", add touch listeners, etc
--Listeners
background:addEventListener( "touch", moveEgon )
bckBtn:addEventListener( "touch", goBack )
egon:addEventListener( "collision", onCollision )
destroyAll:addEventListener( "collision", destroy )
--SceneGroup insert
sceneGroup:insert( background )
sceneGroup:insert(egon)
sceneGroup:insert(bckBtn)
sceneGroup:insert(drop01)
sceneGroup:insert(drop02)
sceneGroup:insert(drop03)
sceneGroup:insert(drop04)
sceneGroup:insert(scoreT)
sceneGroup:insert(countDownText)
sceneGroup:insert(overlayBg)
sceneGroup:insert(destroyAll)
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.
physics.start( )
gX = 0
gY = 10
physics.setGravity( gX, gY )
timercount = 10
spawnShit = 1
score = 0
scoreT.text = "Score: "..score
-- ADD physic bodies ----
physics.addBody( egon, "static", {density=0.1, friction=0.1, bounce=0.8 } )
physics.addBody( destroyAll, "static", {density=0.1, friction=0.1, bounce=0.1 } )
countDownNumber = 10
if countdownTimer == nil then
countdownTimer = timer.performWithDelay( 1000, showCountDown, 0 )
else
end
----------- Timers ------------
if timerSpawn == nil then
timerSpawn = timer.performWithDelay(500, spawnObjects, 0 )
else
end
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,
--timer.pause( timerSpawn )
physics.stop()
spawnShit = nil
score = nil
timerSpawn = nil
countdownTimer = nil
overlayBg = nil
--timer.cancel(timerSpawn)
physics.removeBody( egon )
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.
bckBtn:removeEventListener("touch", goBack )
egon:removeEventListener("touch", moveEgon )
end
-- -------------------------------------------------------------------------------
-- Listener setup
scene:addEventListener( "create", scene )
scene:addEventListener( "show", scene )
scene:addEventListener( "hide", scene )
scene:addEventListener( "destroy", scene )
-- -------------------------------------------------------------------------------
return scene
Allright. The scores are being saved. The problem was the way i displayed the score text, because it wasn't reloading, it was just re-using the old value.
FIX:
i added an
score01.text = highScore[1]
everytime the scene shows and it automatically updates the score when the select screen shows.

Assignment confusion in Lua

I am analysing Fishies projest from sample codes of Corona and i couldn't understand that assignment.
background = ( backgroundLandscape == background and backgroundPortrait ) or backgroundLandscape
Here is the full code:
-- Seed randomizer
local seed = os.time();
math.randomseed( seed )
display.setStatusBar( display.HiddenStatusBar )
-- Preload the sound file (theoretically, we should also dispose of it when we are completely done with it)
local soundID = audio.loadSound( "bubble_strong_wav.wav" )
-- Background
local halfW = display.viewableContentWidth / 2
local halfH = display.viewableContentHeight / 2
-- Create a table to store all the fish and register this table as the
-- "enterFrame" listener to animate all the fish.
local bounceAnimation = {
container = display.newRect( 0, 0, display.viewableContentWidth, display.viewableContentHeight ),
reflectX = true,
}
local backgroundPortrait = display.newImage( "aquariumbackgroundIPhone.jpg", 0, 0 )
local backgroundLandscape = display.newImage( "aquariumbackgroundIPhoneLandscape.jpg", -80, 80 )
backgroundLandscape.isVisible = false
local background = backgroundPortrait
-- Handle changes in orientation for the background images
local backgroundOrientation = function( event )
-- TODO: This requires some setup, i.e. the landscape needs to be centered
-- Need to add a centering operation. For now, the position is hard coded
local delta = event.delta
if ( delta ~= 0 ) then
local rotateParams = { rotation=-delta, time=500, delta=true }
if ( delta == 90 or delta == -90 ) then
local src = background
-- toggle background to refer to correct dst
background = ( backgroundLandscape == background and backgroundPortrait ) or backgroundLandscape
background.rotation = src.rotation
transition.dissolve( src, background )
transition.to( src, rotateParams )
else
assert( 180 == delta or -180 == delta )
end
transition.to( background, rotateParams )
audio.play( soundID ) -- play preloaded sound file
end
end
-- Add a global listener
Runtime:addEventListener( "orientation", backgroundOrientation )
--
-- Fishies
local numFish = 10
local file1 = "fish.small.red.png"
local file2 = "fish.small.blue.png"
--
-- Define touch listener for fish so that fish can behave like buttons.
-- The listener will receive an 'event' argument containing a "target" property
-- corresponding to the object that was the target of the interaction.
-- This eliminates closure overhead (i.e. the need to reference non-local variables )
local buttonListener = function( event )
if "ended" == event.phase then
local group = event.target
-- tap only triggers change from original to different color
local topObject = group[1]
if ( topObject.isVisible ) then
local bottomObject = group[2]
-- Dissolve to bottomObject (different color)
transition.dissolve( topObject, bottomObject, 500 )
-- Restore after some random delay
transition.dissolve( bottomObject, topObject, 500, math.random( 3000, 10000 ) )
end
-- we handled it so return true to stop propagation
return true
end
end
--
--
--
-- Add fish to the screen
for i=1,numFish do
-- create group which will represent our fish, storing both images (file1 and file2)
local group = display.newGroup()
local fishOriginal = display.newImage( file1 )
group:insert( fishOriginal, true ) -- accessed in buttonListener as group[1]
local fishDifferent = display.newImage( file2 )
group:insert( fishDifferent, true ) -- accessed in buttonListener as group[2]
fishDifferent.isVisible = false -- make file2 invisible
-- move to random position in a 200x200 region in the middle of the screen
group:translate( halfW + math.random( -100, 100 ), halfH + math.random( -100, 100 ) )
-- connect buttonListener. touching the fish will cause it to change to file2's image
group:addEventListener( "touch", buttonListener )
-- assign each fish a random velocity
group.vx = math.random( 1, 5 )
group.vy = math.random( -2, 2 )
-- add fish to animation group so that it will bounce
bounceAnimation[ #bounceAnimation + 1 ] = group
end
--
-- Function to animate all the fish
function bounceAnimation:enterFrame( event )
local container = self.container
container:setFillColor( 0, 0, 0, 0) -- make invisible
local containerBounds = container.contentBounds
local xMin = containerBounds.xMin
local xMax = containerBounds.xMax
local yMin = containerBounds.yMin
local yMax = containerBounds.yMax
local orientation = self.currentOrientation
local isLandscape = "landscapeLeft" == orientation or "landscapeRight" == orientation
local reflectX = nil ~= self.reflectX
local reflectY = nil ~= self.reflectY
-- the fish groups are stored in integer arrays, so iterate through all the
-- integer arrays
for i,v in ipairs( self ) do
local object = v -- the display object to animate, e.g. the fish group
local vx = object.vx
local vy = object.vy
if ( isLandscape ) then
if ( "landscapeLeft" == orientation ) then
local vxOld = vx
vx = -vy
vy = -vxOld
elseif ( "landscapeRight" == orientation ) then
local vxOld = vx
vx = vy
vy = vxOld
end
elseif ( "portraitUpsideDown" == orientation ) then
vx = -vx
vy = -vy
end
-- TODO: for now, time is measured in frames instead of seconds...
local dx = vx
local dy = vy
local bounds = object.contentBounds
local flipX = false
local flipY = false
if (bounds.xMax + dx) > xMax then
flipX = true
dx = xMax - bounds.xMax
elseif (bounds.xMin + dx) < xMin then
flipX = true
dx = xMin - bounds.xMin
end
if (bounds.yMax + dy) > yMax then
flipY = true
dy = yMax - bounds.yMax
elseif (bounds.yMin + dy) < yMin then
flipY = true
dy = yMin - bounds.yMin
end
if ( isLandscape ) then flipX,flipY = flipY,flipX end
if ( flipX ) then
object.vx = -object.vx
if ( reflectX ) then object:scale( -1, 1 ) end
end
if ( flipY ) then
object.vy = -object.vy
if ( reflectY ) then object:scale( 1, -1 ) end
end
object:translate( dx, dy )
end
end
-- Handle orientation of the fish
function bounceAnimation:orientation( event )
print( "bounceAnimation" )
for k,v in pairs( event ) do
print( " " .. tostring( k ) .. "(" .. tostring( v ) .. ")" )
end
if ( event.delta ~= 0 ) then
local rotateParameters = { rotation = -event.delta, time=500, delta=true }
Runtime:removeEventListener( "enterFrame", self )
self.currentOrientation = event.type
for i,object in ipairs( self ) do
transition.to( object, rotateParameters )
end
local function resume(event)
Runtime:addEventListener( "enterFrame", self )
end
timer.performWithDelay( 500, resume )
end
end
Runtime:addEventListener( "enterFrame", bounceAnimation );
Runtime:addEventListener( "orientation", bounceAnimation )
-- This function is never called,
-- but shows how we would unload the sound if we wanted to
function unloadSound()
audio.dispose(soundID)
soundID = nil
end
Lua has a slightly strange behaviour when it comes to ands and ors.
The expression a and b evaluates to a if a is considered false (only nil and false is considered false, all other values including 0 are considered true) and if a is considered true the expression evaluates to b.
The expression a or b evaluates to a if a is considered true and b if a is considered false.
Note: in both cases if the expression evaluates to a that value of b isn't even evaluated. This is called short circuit logic. In and if a is false, the and can't possibly be true, so there is no point in wasting computation time to evaluate b. Similarly, if a is true in a or b there is no point to evaluate b as the or can't possibly be false.
The construct cond and valiftrue or valiffalse (or the equivalent, due to operator precedence, (cond and valiftrue) or valiffalse) is equivalent to other language's ternary if statement, with one caveat: valiftrue must not evaluate to false. If that is the case, the whole expression will always evaluate to valiffalse. (Try and reason it out, that's the best way I find to get a grip on this construct.)
was about to post in detail but #JPvdMerwe got it spot on.
To be precise,
background = ( backgroundLandscape == background and backgroundPortrait ) or backgroundLandscape
translates to
if backgroundLandscape == background then
background = backgroundPortrait
else
background = backgroundLandscape
end
EDIT
As #JPvdMerwe pointed out, in this case, if backgroundPortrait is false then
background = backgroundLandscape
will be executed all the time.

Resources