corona sdk pinch to zoom - lua

i am using the function contained in corona sample codes for the pinch to zoom. But since i am developing an app, i need to make every single place of interest zoommable. Do i have to copy and paste the horribly long function for every single image or can i redirect every image on just one function? I am new to lua, here is the pinch to zoom function included in the sample code:
function background:touch( event )
local result = true
local phase = event.phase
local previousTouches = self.previousTouches
local numTotalTouches = 1
if ( previousTouches ) then
-- add in total from previousTouches, subtract one if event is already in the array
numTotalTouches = numTotalTouches + self.numPreviousTouches
if previousTouches[event.id] then
numTotalTouches = numTotalTouches - 1
end
end
if "began" == phase then
-- Very first "began" event
if ( not self.isFocus ) then
-- Subsequent touch events will target button even if they are outside the contentBounds of button
display.getCurrentStage():setFocus( self )
self.isFocus = true
previousTouches = {}
self.previousTouches = previousTouches
self.numPreviousTouches = 0
elseif ( not self.distance ) then
local dx,dy
if previousTouches and ( numTotalTouches ) >= 2 then
dx,dy = calculateDelta( previousTouches, event )
end
-- initialize to distance between two touches
if ( dx and dy ) then
local d = math.sqrt( dx*dx + dy*dy )
if ( d > 0 ) then
self.distance = d
self.xScaleOriginal = self.xScale
self.yScaleOriginal = self.yScale
print( "distance = " .. self.distance )
end
end
end
if not previousTouches[event.id] then
self.numPreviousTouches = self.numPreviousTouches + 1
end
previousTouches[event.id] = event
elseif self.isFocus then
if "moved" == phase then
if ( self.distance ) then
local dx,dy
if previousTouches and ( numTotalTouches ) >= 2 then
dx,dy = calculateDelta( previousTouches, event )
end
if ( dx and dy ) then
local newDistance = math.sqrt( dx*dx + dy*dy )
local scale = newDistance / self.distance
print( "newDistance(" ..newDistance .. ") / distance(" .. self.distance .. ") = scale(".. scale ..")" )
if ( scale > 0 ) then
self.xScale = self.xScaleOriginal * scale
self.yScale = self.yScaleOriginal * scale
end
end
end
if not previousTouches[event.id] then
self.numPreviousTouches = self.numPreviousTouches + 1
end
previousTouches[event.id] = event
elseif "ended" == phase or "cancelled" == phase then
if previousTouches[event.id] then
self.numPreviousTouches = self.numPreviousTouches - 1
previousTouches[event.id] = nil
end
if ( #previousTouches > 0 ) then
-- must be at least 2 touches remaining to pinch/zoom
self.distance = nil
else
-- previousTouches is empty so no more fingers are touching the screen
-- Allow touch events to be sent normally to the objects they "hit"
display.getCurrentStage():setFocus( nil )
self.isFocus = false
self.distance = nil
self.xScaleOriginal = nil
self.yScaleOriginal = nil
-- reset array
self.previousTouches = nil
self.numPreviousTouches = nil
end
end
end
return result
end

You could probably use a closure to accomplish this, assuming all of your points of interest are alike.
Example:
local function createPOI(properties)
local pointOfInterest = createPointOfInterest() -- code to create your object
--set properties based on argument
function pointOfInterest:touch(e)
--do stuff
end
pointOfInterest:addEventListner("touch")
return pointOfInterest
end
Corona has some code here that might be useful. Check the 'Multiple Touches' section.

Related

Attempt to call global (a nil value)

I'm trying to spawn asteroids in this game every few seconds. But whenever I run the game, I get an error that says
stack traceback:
main.lua:277: in function '_listener'
?: in function '?'
?: in function <?:172>
I've tried moving some of the code around but it's not helping. My professor looked at my code and doesn't know exactly what the issue is, but that the loop that I'm using is missing a something. Here is all the code that includes "createAsteroid"
EDIT: I added my entire main.lua file to this question. The separate files that I do have in this project are the character and the background image, everything else besides the config file is in here. I did try deleting an extra end In the gameLoop function but when I did, I got an error that basically said it expected an end there.
display.setStatusBar( display.HiddenStatusBar )
------------------------------
-- RENDER THE SAMPLE CODE UI
------------------------------
local sampleUI = require( "sampleUI.sampleUI" )
local physics = require( "physics" )
physics.start()
math.randomseed( os.time() )
------------------------------
-- CONFIGURE STAGE
------------------------------
local composer = require( "composer" )
local mainScene = display.newGroup()
display.getCurrentStage():insert( mainScene )
----------------------
-- BEGIN SAMPLE CODE
----------------------
-- Frequently used variables
local centerX = display.contentCenterX
local centerY = display.contentCenterY
local originX = display.screenOriginX
local originY = display.screenOriginY
local width = display.actualContentWidth
local height = display.actualContentHeight
local score = 0
local died = false
local asteroidsTable = {}
local player
local gameLoopTimer
local scoreText
local uiGroup = display.newGroup() -- Display group for UI objects like the score
-- Load background and character from "background.lua" and "character.lua" respectively
local background = require( "background" )
mainScene:insert( background )
local character = require( "character" )
mainScene:insert( character )
local asteroid = display.newImage( "asteroid.png", 180, -50 )
asteroid.rotation = 5
physics.addBody( asteroid, { density=3.0, friction=0.5, bounce=0.3, radius=25 })
-------------------------
-- BEGIN MOVEMENT LOGIC
-------------------------
-- Movement logic variables
local movementSpeed = 1.5
local moving = false
local moveDirection = "down"
-- Sets if the character should move and updates sprite animation
local function setMoving( state )
moving = state
if ( moving ) then
character:play()
else
character:pause()
character:setFrame(2)
end
end
-- Sets the direction that the player should move and updates the character's sprite facing
local function setMovementDirection( direction )
-- Don't change anything if we haven't altered direction
if ( moveDirection == direction ) then
return
end
-- Update the sprite playback
moveDirection = direction
character:setSequence( "walk-" .. direction )
-- Refresh animation playback, which can pause after changing the sprite sequence
setMoving( moving )
end
-- Set movement magnitudes
local movementX = 0
local movementY = 0
local function setMovement( x, y )
local updatedMovement = false
-- Horizontal movement checks
if ( movementX ~= x and nil ~= x ) then
movementX = x
updatedMovement = true
end
-- Abort if nothing is updating
-- We do this since axis/key events can fire multiple times with the same values
if ( not updatedMovement ) then
return
end
-- Determine movement direction
if ( 0 ~= movementX or 0 ~= movementY ) then
-- Favor horizontal animations over vertical ones
if ( math.abs( movementX ) >= math.abs( movementY ) ) then
if ( 0 < movementX ) then
setMovementDirection( "right" )
else
setMovementDirection( "left" )
end
else
if ( 0 < movementY ) then
setMovementDirection( "down" )
else
setMovementDirection( "up" )
end
end
end
-- Update moving animation/variable
if ( 0 == movementX and 0 == movementY ) then
setMoving( false )
else
setMoving( true )
end
end
-- Handle character translation on the screen per frame
local function onFrameEnter()
if ( 0 ~= movementX ) then
character.x = character.x + ( movementSpeed * movementX )
setMoving( true )
end
if ( 0 ~= movementY ) then
character.y = character.y + ( movementSpeed * movementY )
setMoving( true )
end
end
Runtime:addEventListener( "enterFrame", onFrameEnter )
---------------------------
-- BEGIN INPUT CODE: TOUCH
---------------------------
local padGraphic, padButtonUp, padButtonDown, padButtonLeft, padButtonRight
-- Determine if we have a joystick connected or not
local inputDevices = system.getInputDevices()
local function getHasJoystick()
for i = 1, #inputDevices do
if ( "joystick" == inputDevices[i].type ) then
return true
end
end
return false
end
local hasJoystick = getHasJoystick()
-- If we don't have any controllers found, create a virtual D-pad controller
if ( not hasJoystick ) then
-- Called when one of the virtual D-pad buttons are used
local function onTouchEvent( event )
local phase = event.phase
local targetID = event.target.id
if ( "began" == phase or "moved" == phase ) then
if ( "up" == targetID ) then
setMovement( 0, -1 )
elseif ( "down" == targetID ) then
setMovement( 0, 1 )
elseif ( "left" == targetID ) then
setMovement( -1, 0 )
elseif ( "right" == targetID ) then
setMovement( 1, 0 )
elseif ( "padGraphic" == targetID ) then
setMovement( 0, 0 )
end
elseif ( "ended" == phase or "cancelled" == phase ) then
-- An alternative to checking for "cancelled" is to set focus on the control
-- However, we don't want an incoming phone call to bug out input
if ( "up" == targetID or "down" == targetID ) then
setMovement( nil, 0 )
elseif ( "left" == targetID or "right" == targetID ) then
setMovement( 0, nil )
end
end
return true
end
-- Display score
scoreText = display.newText( uiGroup, "Score: " .. score, 400, 40, native.systemFont, 36 )
-- Hide the status bar
display.setStatusBar( display.HiddenStatusBar )
local function updateText()
scoreText.text = "Score: " .. score
end
local function createAsteroid()
local newAsteroid = display.newImageRect( mainGroup, objectSheet, 1, 102, 85 )
table.insert( asteroidsTable, newAsteroid )
physics.addBody( newAsteroid, "dynamic", { radius=25, bounce=0.8 } )
newAsteroid.myName = "asteroid"
local whereFrom = math.random( 3 )
if ( whereFrom == 1 ) then
-- From the left
newAsteroid.x = -60
newAsteroid.y = math.random( 500 )
newAsteroid:setLinearVelocity( math.random( 40,120 ), math.random( 20,60 ) )
elseif ( whereFrom == 2 ) then
-- From the top
newAsteroid.x = math.random( display.contentWidth )
newAsteroid.y = -60
newAsteroid:setLinearVelocity( math.random( -40,40 ), math.random( 40,120 ) )
elseif ( whereFrom == 3 ) then
-- From the right
newAsteroid.x = display.contentWidth + 60
newAsteroid.y = math.random( 500 )
newAsteroid:setLinearVelocity( math.random( -120,-40 ), math.random( 20,60 ) )
end
newAsteroid:applyTorque( math.random( -6,6 ) )
end
-- Create the visuals for the on-screen D-pad
local padSize = 200
padGraphic = display.newImageRect( mainScene, "pad.png", padSize, padSize )
padGraphic.x = originX + padSize/2 - 40
padGraphic.y = height + originY - padSize/2 + 40
padGraphic.alpha = 0.35
padGraphic.id = "padGraphic"
padGraphic:addEventListener( "touch", onTouchEvent )
-- Creates one of the invisible virtual D-pad buttons
local function createPadButton( buttonID, offsetX, offsetY )
local btn = display.newRect( mainScene, padGraphic.x+offsetX, padGraphic.y+offsetY, padSize/5, padSize/5 )
btn:addEventListener( "touch", onTouchEvent )
btn.id = buttonID
btn.isVisible = false
btn.isHitTestable = true
return btn
end
-- Create buttons for handling the D-pad input
padButtonUp = createPadButton( "up", 0, padSize/-5 )
padButtonDown = createPadButton( "down", 0, padSize/5 )
padButtonLeft = createPadButton( "left", padSize/-5, 0 )
padButtonRight = createPadButton( "right", padSize/5, 0 )
end
-----------------
-- GAME LOOP --
-----------------
local function gameLoop()
-- Create new asteroid
createAsteroid()
-- Remove asteroids which have drifted off screen
for i = #asteroidsTable, 1, -1 do
local thisAsteroid = asteroidsTable[i]
if ( thisAsteroid.x < -100 or
thisAsteroid.x > display.contentWidth + 100 or
thisAsteroid.y < -100 or
thisAsteroid.y > display.contentHeight + 100 )
then
display.remove( thisAsteroid )
table.remove( asteroidsTable, i )
end
end
end
timer.performWithDelay(1000, gameLoop, 0)
--------------------------------------------------
-- BEGIN INPUT CODE: KEYBOARD & BASIC CONTROLLER
--------------------------------------------------
-- Detect if a joystick axis is being used
local joystickInUse = false
-- Keyboard input configuration
local keyUp = "up"
local keyDown = "down"
local keyLeft = "left"
local keyRight = "right"
-- Called when a key event has been received
local function onKeyEvent( event )
local keyName = event.keyName
local phase = event.phase
-- Handle movement keys events; update movement logic variables
if ( not joystickInUse ) then
if ( "down" == phase ) then
if ( keyUp == keyName ) then
setMovement( nil, -1 )
elseif ( keyDown == keyName ) then
setMovement( nil, 1 )
elseif ( keyLeft == keyName ) then
setMovement( -1, nil )
elseif ( keyRight == keyName ) then
setMovement( 1, nil )
end
elseif ( "up" == phase ) then
if ( keyUp == keyName ) then
setMovement( nil, 0 )
elseif ( keyDown == keyName ) then
setMovement( nil, 0 )
elseif ( keyLeft == keyName ) then
setMovement( 0, nil )
elseif ( keyRight == keyName ) then
setMovement( 0, nil )
end
end
end
return false
end
Runtime:addEventListener( "key", onKeyEvent )
------------------------------------------
-- BEGIN INPUT CODE: ADVANCED CONTROLLER
------------------------------------------
-- We only support advanced controllers when one is detected
if ( getHasJoystick ) then
-- Detect axis event updates
local function onAxisEvent( event )
local value = event.normalizedValue
local axis = event.axis.type
local descriptor = event.axis.descriptor
-- We only care about "x" and "y" input events
-- However, touch-screen events can fire these as well so we filter them out
if ( ( "x" ~= axis and "y" ~= axis ) or ( string.find( descriptor, "Joystick" ) == nil ) ) then
return
end
-- Detect zero movement at a certain cutoff so we don't get the character moving very, very slowly
if ( math.abs(value) < 0.15 ) then
value = 0
end
-- Based on which axis type we are dealing with, set movement variables
if ( "x" == axis ) then
setMovement( value, nil )
elseif ( "y" == axis ) then
setMovement( nil, value )
end
-- Some devices will send both up/down/left/right keys and the axis value
-- We let our code know that we are using a joystick value so they do not conflict
if ( 0 ~= value ) then
joystickInUse = true
else
joystickInUse = false
end
end
Runtime:addEventListener( "axis", onAxisEvent )
end
local function onCollision( event )
if ( ( obj1.myName == "player" and obj2.myName == "asteroid" ) or
( obj1.myName == "asteroid" and obj2.myName == "player" ) )
then
display.remove( player )
else
ship.alpha = 0
timer.performWithDelay( 1000, restoreShip )
end
end
Runtime:addEventListener( "collision", onCollision )
Go to line 277 in your file main.lua
You will find a function call createAsteroid(). Within this scope createAsteroid is not defined.
So let's see if we can find a definition for createAsteroid in this file.
Line 218: local function createAsteroid() ...
Check if the function call is within the scope of that function...
No! It is inside an if-statement while the function call is outside that statement. As createAsteroid is local to that if-statement it is unknown (nil) inside gameLoop and hence may not be called.
Finding such scope issues could be very easy if you had proper indentation!

Highlight a path in a grid

In my grid-based game, The player clicks on a unit then he moves his finger to determine where this unit should move. I'm using the "Jumper" library for the pathfinding. The code for getting the path works perfectly, but the code for highlighting the path, not so much.
local function onTileTouch( event )
local phase = event.phase
local tile = event.target
if ( phase == "began" ) then
-- I could create the line here
elseif ( phase == "moved" ) then
createPath( tile )
-- Getting the position of the first tile based on where the unit is
local t = tiles[currentSelectedUnit.pos.y][currentSelectedUnit.pos.x]
-- Create the line at the first tile's position
line = display.newLine( t.x, t.y, t.x, t.y )
line:setStrokeColor( 1,0,0 )
line.strokeWidth = 8
-- "foundPath" is a table of tiles of the correct path
for i=1,#foundPath do
line:append( foundPath[i].x,foundPath[i].y )
end
elseif ( phase == "ended" or phase == "cancelled" ) then
end
The line doesn't look right when being created in the "moved" phase. It does, however, look very accurate when being created in the "began" phase and then getting appended during the "moved" phase. But in this case, another extra line gets drawn that doesn't follow the path but gets directly from the start tile to the end tile.
My second problem with the "began" phase method, is I don't know how to keep deleting the old line and create a new one with for the new correct path.
Let me know if any extra information is needed.
I'm not sure what exactly you want so I have prepared my own version:
test.lua
local composer = require( 'composer' )
-- Library setup
local Grid = require ( 'jumper.grid' ) -- The grid class
local Pathfinder = require ( 'jumper.pathfinder' ) -- The pathfinder lass
local scene = composer.newScene()
-- First, set a collision map and value for walkable tiles
local board = {
tiles = {},
walkable = 0,
map = {
{0,1,0,0,0},
{0,1,0,1,0},
{0,1,0,0,0},
{0,0,0,0,0},
}
}
local function createPath( startx, starty, endx, endy )
-- Creates a grid object
local grid = Grid( board.map )
-- Creates a pathfinder object using Jump Point Search
local myFinder = Pathfinder( grid, 'JPS', board.walkable )
-- Calculates the path, and its length
local path = myFinder:getPath( startx, starty, endx, endy )
return path
end
function scene:create( event )
local sceneGroup = self.view
local rowNum = #board.map
local colNum = #board.map[1]
local width = 40
local height = width
local margin = 5
local function touch( self, event )
local phase = event.phase
local endTile = event.target
if ( phase == 'began' ) then
board.startTile = endTile
elseif ( phase == 'moved' ) then
if board.startTile ~= endTile then
if ( board.endTile ~= endTile ) then
board.endTile = endTile
local path = createPath( board.startTile.col, board.startTile.row, board.endTile.col, board.endTile.row )
if path then
-- Remove old lines
display.remove( board.lines )
board.lines = nil
-- Create new line
board.lines = display.newLine( sceneGroup, board.startTile.x, board.startTile.y, board.startTile.x, board.startTile.y + 1 )
for node, count in path:nodes() do
local id = colNum * ( node:getY() - 1 ) + node:getX()
local tile = board.tiles[id]
board.lines:append( tile.x, tile.y )
end
end
end
else
-- Remove old lines
display.remove( board.lines )
board.lines = nil
board.endTile = nil
end
elseif ( phase == 'ended' or phase == 'cancelled' ) then
-- Remove old lines
display.remove( board.lines )
board.lines = nil
board.endTile = nil
board.startTile = nil
end
end
for i=1, rowNum do
for j=1, colNum do
local tile = display.newRect( sceneGroup, (width + margin ) * j, ( height + margin ) * i, width, height )
tile.col = j
tile.row = i
tile.touch = touch
-- Add listener and change color for walkable tile
if board.map[i][j] == board.walkable then
tile:addEventListener( 'touch' )
tile:setFillColor( 0, 0.5, 0.5 )
end
local id = colNum * ( i -1 ) + j
board.tiles[id] = tile
end
end
end
function scene:show( event )
local sceneGroup = self.view
local phase = event.phase
if phase == 'will' then
elseif phase == 'did' then
end
end
function scene:hide( event )
local sceneGroup = self.view
local phase = event.phase
if event.phase == 'will' then
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

Game speed increases itself on every restart - Corona

am trying to make a jetpack joyride concept game on corona. First of all unlike other sdk where when you change a scene, the previous scene is automatically removed, in corona you have to remove each and object and function yourself.
After a week I was finally able to remove objects while changing scene, but now I have ran into a new problem. Everytime when I change scenes and come back to the gameScreen the speed of lasers and missiles increases every time ( maybe twice? ). How can I reset its speed?
I even added another "gameStatus" variable to remove the event listener and tried to print it, but game status never actually returned "ended", it was always "running". Here is the gameScene screen where everything happens
EDIT : I just noticed something, For example if 3 objects have already spawned then after reloading the screen only first 3 objects seem to be fast and its normal from fourth.
local composer = require( "composer" )
local scene = composer.newScene()
local physics = require( "physics" )
physics.start()
local image, text1
local function onSceneTouch( self, event )
if event.phase == "began" then
composer.gotoScene( "startScreen", "fade", 400 )
return true
end
end
function scene:create( event )
local sceneGroup = self.view
-- Variable
---------------------------------------
sW = display.contentWidth
sH = display.contentHeight
-- trying to reset gameStatus on restart
gameStatus = "ended"
gameStatus = "running"
--------------------------------------------------------
backgroundTiles = {}
drawBackground = function()
cieling = display.newRect( 0, 0, 2 * sW, sH * 0.05)
physics.addBody( cieling, "static", { density=0, friction=0, bounce=0 } )
cieling.x, cieling.y = cieling.contentWidth / 4, cieling.contentHeight * 0.25
cieling.alpha = 0
cieling.name = "ceiling"
ground = display.newRect( 0, sH, 2 * sW, sH * 0.05)
physics.addBody( ground, "static", { density=3, friction=1, bounce=0.1 } )
ground.x, ground.y = ground.contentWidth / 4, sH - ground.contentHeight * 0.25
ground.alpha = 0
ground.name = "ground"
end
drawBackground()
--------------------------------------------------------
--------------------------------------------------------
drawBird = function()
bird = display.newImageRect( "a/img/hero/hero.png", 80, 58)
bird.x, bird.y = 0 - sW * 0.1, ground.y - ground.contentHeight - 10
bird.isFixedRotation = true; bird.angularVelocity = 0;
physics.addBody( bird, { density=1, friction=0.5, bounce=0.1 } )
bird.name = "bird"
sceneGroup:insert( bird )
bird:addEventListener( "collision", onCollision )
end
coinsCollected, tokensCollected = 0, 0
birdFlight = function()
if ( gameStatus == "running" ) then
transition.to(bird, {
y = bird.y - 75,
transition = easing.outQuad,
onComplete = function() end
})
function birdFlight()
--transition.to( bird, { rotation=-45, time=300 } )
end
birdFlight()
function birdFall()
--transition.to( bird, { rotation=90, time=300 } )
end
timer.performWithDelay(700, birdFall, 1)
end
end
display.currentStage:addEventListener( "tap", birdFlight )
function onCollision(event)
if event.phase == "began" then
if event.other.name == "coin" then
coinsCollected = coinsCollected + 1
end
if event.other.name == "token" then
tokensCollected = tokensCollected + 1
end
if event.other.name == "missile" or event.other.name == "laser" or event.other.name == "rod" then
-- game ended
end
end
end
drawBird()
function atFrame(event)
if gameStatus == "running" then
bird.x,bird.y = sW / 3,bird.y + 9
if ( bird.y > sH - 70) then bird.y = sH - 70 end
if ( gameStatus == "ended" ) then bird.y = bird.y + 15 end
end
end
Runtime:addEventListener( "enterFrame", atFrame )
--------------------------------------------------------
--------------------------------------------------------
missile, missileAlert, missileMoving = {}, {}, {}
function removeMissile(i)
local onEnterFrame = function( event )
if missile[i] ~= nil and missile[i].x ~= nil and gameStatus == "running" then
if missile[i].x < -100 then
display.remove( missile[i] )
missile[i] = nil
end
end
end
Runtime:addEventListener( "enterFrame", onEnterFrame )
print(gameStatus)
if gameStatus == "ended" then
Runtime:removeEventListener( "enterFrame", onEnterFrame )
end
end
function flyMissile(i)
local onEnterFrame = function( event )
if missile[i] ~= nil and missile[i].x ~= nil and missile[i].y ~= nil and gameStatus == "running" then
if missileMoving[i] == true then
missile[i].y = bird.y
end
missile[i].x = missile[i].x - 5
if missileAlert[i] ~= nil then
if missileMoving[i] == true then
missileAlert[i].y = bird.y
end
if missile[i].x < sW then
display.remove( missileAlert[i] )
missileAlert[i] = nil
end
end
end
end
Runtime:addEventListener( "enterFrame", onEnterFrame )
print(gameStatus)
if gameStatus == "ended" then
Runtime:removeEventListener( "enterFrame", onEnterFrame )
end
end
function holdMissile(i)
local onEnterFrame = function( event )
if missile[i] ~= nil and missile[i].x ~= nil and gameStatus == "running" then
if missile[i].x < sW * 1.5 then
if missileAlert[i] ~= nil then
missileAlert[i]:setFillColor(1,1,0)
end
missileMoving[i] = false
end
end
end
Runtime:addEventListener( "enterFrame", onEnterFrame )
print(gameStatus)
if gameStatus == "ended" then
Runtime:removeEventListener( "enterFrame", onEnterFrame )
end
end
function spawnMissile(i)
missile[i] = display.newRect( sW*2, sH/2, 80, 80 )
missileAlert[i] = display.newRect( sW-80, bird.y, 80, 80 )
physics.addBody(missile[i],"kinematic",{isSensor=true})
missile[i].name = "missile"
sceneGroup:insert( missile[i] )
sceneGroup:insert( missileAlert[i] )
missileMoving[i] = true
flyMissile(i)
removeMissile(i)
holdMissile(i)
end
spawnMissile(1)
--------------------------------------------------------
--------------------------------------------------------
laser = {}
function moveAndRemovelaser(i)
local onEnterFrame = function( event )
if laser[i] ~= nil and laser[i].x ~= nil and gameStatus == "running" then
laser[i].x = laser[i].x - 5
if laser[i].x < 0 - sW / 2 then
display.remove( laser[i] )
laser[i] = nil
end
end
end
Runtime:addEventListener( "enterFrame", onEnterFrame )
print(gameStatus)
if gameStatus == "ended" then
Runtime:removeEventListener( "enterFrame", onEnterFrame )
end
end
function spawnlaser(i)
laserSize = math.random(1,2)
laserPosition = math.random(1,3)
laserRotation = math.random(1,4)
if laserSize == 1 then
laser[i] = display.newRect( 100,100,50,sH/3 )
else
laser[i] = display.newRect( 100,100,50,sH/2 )
end
sceneGroup:insert( laser[i] )
laser[i].x = sW * 2
laser[i].y = sH / 2
laser[i].name = "laser"
if laserPosition == 1 and laserRotation ~= 4 then
laser[i].y = sH * 0.05 + 12
laser[i].anchorY = 0
elseif laserPosition == 3 and laserRotation ~= 4 then
laser[i].y = sH * 0.95 - 12
laser[i].anchorY = 1
end
if laserPosition == 1 and laserRotation == 4 then
laser[i].y = sH * 0.05 + laser[i].contentHeight / 2
elseif laserPosition == 3 and laserRotation == 4 then
laser[i].y = sH * 0.95 - laser[i].contentHeight / 2
end
if laserRotation == 1 then
laser[i].rotation = -45
elseif laserRotation == 2 then
laser[i].rotation = 0
elseif laserRotation == 3 then
laser[i].rotation = 45
elseif laserRotation == 4 then
local onEnterFrame = function( event )
if laser[i] ~= nil and laser[i].rotation ~= nil then
laser[i].rotation = laser[i].rotation + 5
end
end
Runtime:addEventListener( "enterFrame", onEnterFrame )
end
laser[i]:setFillColor(1,1,0)
physics.addBody(laser[i],"kinematic",{isSensor=true})
moveAndRemovelaser(i)
end
spawnlaser(1)
--------------------------------------------------------
image = display.newRect (100,100,100,100)
image.x = display.contentCenterX
image.y = display.contentCenterY
sceneGroup:insert( image )
image.touch = onSceneTouch
text1 = display.newText( "Game Screen", 0, 0, native.systemFontBold, 24 )
text1:setFillColor( 255 )
text1.x, text1.y = display.contentWidth * 0.5, 50
sceneGroup:insert( text1 )
end
function scene:show( event )
local phase = event.phase
if "will" == phase then
gameStatus = "ended"
end
if "did" == phase then
print( "4" )
composer.removeScene( "startScreen" )
image:addEventListener( "touch", image )
gameStatus = "running"
end
end
function scene:hide( event )
local phase = event.phase
if "will" == phase then
print( "5" )
gameStatus = "ended"
image:removeEventListener( "touch", image )
Runtime:removeEventListener( "enterFrame", onEnterFrame )
end
end
function scene:destroy( event )
print( "6" )
end
scene:addEventListener( "create", scene )
scene:addEventListener( "show", scene )
scene:addEventListener( "hide", scene )
scene:addEventListener( "destroy", scene )
return scene
You are getting this problem because you are registering an event listener twice.
You need to add and remove them only when the scene enters and exits. The proper place to remove game objects is scene:destroy() and scene:hide()
My advice is that you try to re-organize your code and try to follow these guidelines:
scene:create() : This happens the first time a scene is used. You want create your game objects here. This event will only happen once, unless the scene is destroyed and then used again.
scene:show() : You want to register all your event listeners here (touch, etc) and setup your game. This function gets called every-time the scene is shown. Every event listener that is registered here should be un-registered to prevent double-triggering.
scene:hide() : You want to un-register all event listeners here. This will prevent the next scene from triggering events on your previous scene's listeners too.
scene:destroy() : You want to remove all game objects here (images, text, widgets, etc). The scene is no longer needed and everything should be cleaned.
To better understand how the Scene's work I recommend reading this doc:
http://coronalabs.com/blog/2014/01/21/introducing-the-composer-api-plus-tutorial/
So I ended up creating a function and putting all the removeEventListeners in that function and called that function when the game ended.
For future reference always keep a list of all the timer and event listeners that you have created anywhere in the screen and remove them all together when ending the game

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