LUA - Unable to make the game restart after the timer finishes - lua

I have to make a small game for my college work and at the end of the game I would like it so if you click the game over button it restarts it, however every time I have tried to do this it hasn't worked with either errors or just the game restarting and the game over image not being removed.
I know that the current code exits the app when the gameover sign is clicked due to the closeDownApp function (this is the code from before I tried to add the ability to restart)
Any help on how to do it will be greatly appreciated
--Define the variables
rotateAmt = 5
rotateMin = -90
rotateMax = 90
cannonRotation = 0
cannonForce = 1200
playerPoints = 0
targetHit = false
timeLeft = 600
--Define the functions
function closeDownApp(event)
os.exit ( )
end
function update( )
--decrease the time counter
timeLeft = timeLeft - 1
scoreDisplay.text = 'Kills: ' .. playerPoints .. ' Time: ' .. timeLeft
--check if the time has run out
if (timeLeft <= 0) then
--display the final score
scoreDisplay.text = 'Your score was ' .. playerPoints
--remove all of the screen objects
display.remove(cannonBarrel)
display.remove(target)
display.remove(upButton)
display.remove(downButton)
display.remove(fireButton)
--display the 'game over' sign
gameOver = display.newImage('gameover.png', 150, 75)
gameOver:addEventListener('tap', closeDownApp)
-- make a group
end
--check if the target has been hit
if (targetHit == true) then
targetHit = false
playerPoints = playerPoints + 1
Hit = display.newImage('Hit.png', target.x, target.y)
--replace the target with the hit picture
transition.dissolve(Hit, target, 1000, 0)
display.remove(bullet)
--put the target back to a random position
target.x = math.random(280, 500 )
target.y = math.random(100, 290 )
end
end
function onCollide(event)
targetHit=true
end
function fire(event)
--only fire at the beginning of a touch event
if (event.phase == 'began') then
media.playSound('bang.wav')
bullet = display.newImage('bullet.png')
--move the image
bullet.x, bullet.y = cannonBarrel:localToContent(70, 0)
bullet.rotation = cannonRotation
--apply physics to the cannonball
physics.addBody( bullet, { density=2.0, friction=0.2, radius=15 } )
--determine the appropriate ratio of horizontal to vertical force
force_x = math.cos(math.rad(cannonRotation)) * cannonForce
force_y = math.sin(math.rad(cannonRotation)) * cannonForce
--fire the cannonball
bullet:applyForce( force_x, force_y, bullet.x, bullet.y )
end
end
function moveDown(event)
--only move the barrel if the touch event started
if (event.phase == 'began') then
cannonRotation = cannonRotation + rotateAmt
if (cannonRotation >= rotateMax) then
cannonRotation = rotateMax
end
cannonBarrel.rotation = cannonRotation
fireButton.rotation = cannonRotation
end
end
function moveUp(event)
--only move the barrel if the touch event started
if (event.phase == 'began') then
cannonRotation = cannonRotation - rotateAmt
if (cannonRotation <= rotateMin) then
cannonRotation = rotateMin
end
cannonBarrel.rotation = cannonRotation
fireButton.rotation = cannonRotation
end
end
function makeTarget( )
target = display.newImage('target.png')
target.x = math.random(280, 450)
target.y = math.random(100, 290)
physics.addBody(target,{density=1.0, friction=0.5, bounce=0.05, radius=15})
target.bodyType = 'static'
target:addEventListener('collision', onCollide)
end
function makeInterface( )
--up button
upButton = display.newImage('up_button.png')
upButton:translate(3, 37)
upButton:addEventListener('touch', moveUp)
--down button
downButton = display.newImage('down_button.png')
downButton:translate(3, 176)
downButton:addEventListener('touch', moveDown)
--fire button
fireButton = display.newImage('fire_button.png')
fireButton:translate(19, 124)
fireButton:addEventListener('touch', fire)
--display cannon parts
cannonBarrel = display.newImage('cannon_barrel.png')
cannonBarrel:translate(73, 109)
--display score
scoreDisplay = display.newText( ('Points: ' .. playerPoints), 70, -18, native.systemFont, 20 )
scoreDisplay:setTextColor( 255,255,255 )
scoreDisplay:translate(display.contentHeight /2, 30)
end
--Define control structure
function init( )
audio.stop (1)
display.setStatusBar(display.HiddenStatusBar)
background = display.newImage('bg.jpg')
physics = require('physics')
physics.setDrawMode('normal')
physics.start( )
makeInterface( )
makeTarget( )
Runtime:addEventListener('enterFrame', update)
local backgroundMusic = audio.loadStream("theme.mp3")
local backgroundMusicChannel = audio.play( backgroundMusic,{channel=2, loops=-1, fadein=10 })
audio.setVolume( 0.5, { channel=backgroundMusicChannel } )
end
function introScreen( )
local backgroundMusic1 = audio.loadStream("intro.mp3")
local backgroundMusicChannel1 = audio.play( backgroundMusic1,{channel=1, loops=1, fadein=10 })
-- Display the background
background = display.newImage ("IntroScreen.png")
-- Display the play button at the top right
playBtn = display.newImage ("playBtn.png", 350, 10)
playBtn:addEventListener('tap', init)
end
--Call the code
introScreen( )

This seems like it was written for Corona SDK. Their forums (https://forums.coronalabs.com/) seem very active.
I'm sure that you'd be able to solve this issue a lot faster by asking for help there.

Related

Receiving "Attempt to index global 'sprite' (a nil value)" in Lua

Im attempting to make a simple game app and keep running into this problem. I stopped programming in Lua for a few years so I don't exactly remember how to fix this. Anyway, my code is as follows:
EDIT: Here is the entire file. Still trying to figure out the formatting of Stack Overflow. Error occurs at line 76.
module(..., package.seeall)
-- Main function - MUST return a display.newGroup()
function new()
local localGroup = display.newGroup()
---------
local Rad = math.rad
local Sin = math.sin
local Cos = math.cos
local Pi = math.pi
local Atan2 = math.atan2
local radD = 180 / Pi
local DegR = Pi / 180
local touchPoint = display.newCircle(localGroup, -50, -50, 20)
touchPoint.isFocus = false
touchPoint.alpha = 0
function GetDistanceFromObjects(obj1, obj2)
local xDist = obj1.x - obj2.x
local yDist = obj1.y - obj2.y
local dist = Sqrt((xDist * xDist) + (yDist * yDist))
return dist
end
function getAngleDeg(inX1, inY1, inX2, inY2)
local xDist = inX2 - inX1
local yDist = inY2 - inY1
local angRad = Atan2(yDist, xDist)
return angRad * radD + 90
end
require "sprite"
function VectorFromAngle(inAngle, inVelocity)
local vx = Cos(Rad(inAngle-90))
local vy = Sin(Rad(inAngle-90))
if(inVelocity ~= nil)then
vx = vx * inVelocity
vy = vy * inVelocity
end
return vx,vy
end
require ( "physics" )
physics.start()
physics.setGravity( 1, 1 )
--( x, y )
--physics.setDrawMode ( "hybrid" )
math.randomseed(os.time())
local background = display.newImage("yazd.jpeg")
localGroup:insert(background)
--width of image divided by # of pics lined up from left to right (in the sprite) = the first #
--height of image divided by # of pics lined up from top to bottom (in the sprite) = the second #
local birdSheet = sprite.newSpriteSheet( "enemy.jpg", 59, 50 )
local birdSet = sprite.newSpriteSet(birdSheet, 1, 1)
-- images 1-14
sprite.add( birdSet, "bird", 1, 1, 200, 0 )
-- play 1-14, each image every 200 ms, 0 = loop count, which is infinite
local bird1 = sprite.newSprite( birdSet )
bird1.x = 40 -- starting point
bird1.y = 40 -- starting point
bird1.xScale = 0.5 --scale down x
bird1.yScale = 0.5 --scale down y
bird1:prepare("bird") --prepare sprite sequence
bird1:play() --play sprite
localGroup:insert(bird1)
--only local to this group
local killSheet = sprite.newSpriteSheet("explosion.png", 100, 100)
local killSet = sprite.newSpriteSet(killSheet, 1, 9)
sprite.add(killSet, "kill", 1, 9, 200, 1)
local birdCount = 1
local transDirection12
local function transDirection1()
bird1.xScale = 0.5
transition.to(bird1, {time=math.random(200,500), x = math.random(200,490), y = math.random(10,310), alpha = (math.random(9,100))/100, onComplete = transDirection12})
end
transDirection12 = function()
bird1.xScale = 0.5
transition.to(bird1, {time= math.random(200,500), x = math.random(200,490), y = math.random(10,310), alpha = (math.random(9,100))/100, onComplete = transDirection1})
end
transDirection1()
-- local transDirection1 declares what will be used (local function)
-- transDirection1 = function
-- following it are the function qualities
-- declares it will use object/image called bird1 and scales it to .5
-- time = ____ means it will take a certain time, between ____ and ____ to complete the transition
-- x=____ means that is where it will move to on the x axis
-- y=____ means that is where it will move to on the y axis
-- alpha = ___ means the is how transparent it will be
-- onComplete = ________ means that when the action is complete, it will call another function
-- The next function has the same qualities as transDirection1, but the onComplete part calls transDirection1 and they continue to loop
-- transDirection1() declares transDirection1 so the app knows about it and can use it
-- the other trans do not need to be declared because they are part of transDirection1, which is already declared
--(x, y, size.x, size.y)
local player = display.newImage( "mk11.png" )
player.x = 240
player.y = 260
player.xScale = .5
player.yScale = .5
localGroup:insert( player )
-- add physics to all the objects wanted: (object wanted, "static" or "dynamic")
physics.addBody(player, "static", {radius=30, isSensor = true})
physics.addBody(bird1, "static", {radius=23})
local function shoot(inPointX, inPointY)
-- (start at the x of the player + 10, also start at the y of the player, the radius of the circle is 5)
local bullet = display.newImage( "bullet2.png" )
bullet.x = player.x
bullet.y = player.y
-- add physics to the object, which is the bullet.
-- Make the bullet "dynamic" or moving
physics.addBody(bullet, "dynamic")
bullet.isFixedRotation = true
localGroup:insert( bullet )
local velocity = 300
local vx, vy = VectorFromAngle(player.rotation, velocity)
bullet.rotation = player.rotation
bullet:setLinearVelocity(vx, vy)
end
function RotateToTouchPoint(inPointX, inPointY)
local ang = getAngleDeg(player.x, player.y, inPointX, inPointY)
player.rotation = ang
end
local function ScreenTouchListener(event)
local phase = event.phase
if(phase == "began")then
if(touchPoint.isFocus == false)then
touchPoint.alpha = 1
touchPoint.x = event.x
touchPoint.y = event.y
display.getCurrentStage():setFocus(touchPoint, event.id)
touchPoint.isFocus = true
RotateToTouchPoint(event.x, event.y)
shoot(event.x, event.y)
end
elseif(touchPoint.isFocus)then
if(phase == "moved")then
touchPoint.x = event.x
touchPoint.y = event.y
RotateToTouchPoint(event.x, event.y)
elseif(phase == "ended" or phase == "cancelled")then
display.getCurrentStage():setFocus(touchPoint, nil)
touchPoint.isFocus = false
touchPoint.alpha = 0
end
end
return true
end
local function gotShot (event)
event.target:removeSelf()
event.other:removeSelf()
local explosion = sprite.newSprite(killSet)
explosion.x, explosion.y = event.target.x, event.target.y
explosion:prepare("kill")
explosion:play()
localGroup:insert( explosion )
birdCount = birdCount - 1
-- when there are no more birds, remove the runtime event listener and perform the
-- function with a delay of 500 m.s. The function changes the scene to test.lua
if "ended" then
if birdCount == 0 then
Runtime:removeEventListener("touch", ScreenTouchListener)
timer.performWithDelay(500, function()
director:changeScene("mainPage") end, 1)
end
end
end
bird1:addEventListener("collision", gotShot)
Runtime:addEventListener("touch", ScreenTouchListener)
---------
-- MUST return a display.newGroup()
return localGroup
end
Any help is appreciated!
The error message is perfectly clear -- the variable sprite used at this line:
local bird1 = sprite.birdSheet( birdSet )
has a nil value, meaning it has not been initialized or was set to nil. You need to show the earlier code where you should have set it up.
(After OP updates)
I think this line
require "sprite"
should actually be
sprite = require "sprite"
You can read more in modules tutorial here:
http://lua-users.org/wiki/ModulesTutorial

Attempt to Index GlobalCredits 'event' (a nil value)

I have just started to learn LUA in school, but i cannot find many helpful tutorials o the internet to aid in my learning. I have made a simple game (which doesn't work yet, i realize that) and a main menu. However, when i try to start the app, it gives me this error:
/Users/jordanmcbride/Desktop/Lua Projects/Tapinator/main.lua:47: attempt to index global 'showCredits' (a nil value)
stack traceback:
/Users/jordanmcbride/Desktop/Lua Projects/Tapinator/main.lua:47: in main chunk
[Finished in 9.4s]
I have looked the error, and I cannot seem to understand how to fix it. The other questions have said something about returning the function returning a nil value, and that I should add a return statement to the end, but that doesn't work either.
Here is the code # line 47.
function showCredits.touch(e)
playButton.isVisible = false
creditsButton.isVisible = false
creditsView = display.newImage('credits.png', 0, display.contentHeight)
lastY = name.y
transition.to(name, {time = 300, y = display.contentHeight * 0.5 - title.height - 25})
transition.to(creditsView, {time = 300, y = display.contentHeight * 0.5 + creditsView.height, onComplete = function() creditsView:addEventListener('tap', hideCredits) end})
end
Here is my full code, in case the problem lies elsewhere:
display.setStatusBar(display.HiddenStatusBar)
radius = 40
smallTime = 200
bigTime = 800
score = 0
scoreInc = 2000
--HomePage
local name
local playButton
local creditsButton
local homePage
--Credits
local creditsPage
--Sounds
local circleSpawn = audio.loadSound( "circle_spawn.wav" )
local circleTap = audio.loadSound( "circle_tap.wav" )
function Main()
name = display.newImage('title.png', display.contentWidth / 2, 53)
name:scale( .5, .5 )
playButton = display.newImage('playButton.png', display.contentWidth / 2, 245)
playButton:scale( .5, .5 )
creditsButton = display.newImage('creditsButton.png', display.contentWidth / 2, 305)
creditsButton:scale( .5, .5 )
homePage = display.newGroup(name, playButton, creditsButton)
startButtonListeners('add')
end
function showCredits.touch(e)
playButton.isVisible = false
creditsButton.isVisible = false
creditsView = display.newImage('credits.png', 0, display.contentHeight)
lastY = name.y
transition.to(name, {time = 300, y = display.contentHeight * 0.5 - title.height - 25})
transition.to(creditsView, {time = 300, y = display.contentHeight * 0.5 + creditsView.height, onComplete = function() creditsView:addEventListener('tap', hideCredits) end})
end
function hideCredits.touch(e)
transition.to(creditsView, {time = 300, y = display.contentHeight, onComplete = function() creditsButton.isVisible = true playButton.isVisible = true creditsView:removeEventListener('tap', hideCredits) display.remove(creditsView) creditsView = nil end})
transition.to(name, {time = 300, y = lastY});
end
function startButtonListeners(action)
if(action == 'add') then
playButton:addEventListener('touch', playGame)
creditsButton:addEventListener('touch', showCredits)
else
playButton:removeEventListener('touch', playGame)
creditsButton:removeEventListener('touch', showCredits)
end
end
Main()
printScore = display.newText("Score: " .. tostring(score), display.contentWidth-80, 40, native.systemFontBold, 20)
-- A function that creates random circles
function generateCircle ()
-- Creates a new circle between 0 (the left most bounds) and the width of the display (being the content width), and also
-- 0 (the upper most bounds) and the height of the display (being the content height). The radius of the circle is 'radius'
x = math.random(radius, display.contentWidth-radius)
y = math.random(80, display.contentHeight)
score = score + scoreInc
myCircle = display.newCircle( x, y, radius )
myCircle:setFillColor( math.random(), math.random(), math.random() )
delayTime = math.random(smallTime, bigTime)
score = score + scoreInc
printScore.text = "Score:"..tostring(scores)
local spawnChannel = audio.play( circleSpawn )
timer.performWithDelay( delayTime, generateCircle )
end
generateCircle()
function myCircle:touch( event )
local tapChannel = audio.play( circleTap )
myCircle:removeSelf()
end
myCircle:addEventListener( "touch", myCircle )
The answer by greatwolf will work. But just a tip from my experience. One way I like to create functions is to try to define the name of the function first near the top of the lua file. Then I will define the function later on in the file. Something like this:
--function preallocation
local onPlayTap
local onSoundOnTap
local onSoundOffTap
local onCreditsTap
local onHelpTap
---------------------------------------------------------------------------------
-- Custom Function Definitions
---------------------------------------------------------------------------------
--Called when Sound On Button is tapped, turn off sound
onSoundOnTap = function(event)
end
--Called when Sound Off Button is tapped, turn on sound
onSoundOffTap = function(event)
end
--Called when Credits button is tapped, shows credits
onCreditsTap = function(event)
end
--Called when Help button is tapped, shows help
onHelpTap = function(event)
end
--Callback to Play button. Moves scene to Level Picker Scene
onPlayTap = function(event)
end
What this does is allow each function to be called by any other function in the file. If you do it the way you are doing it by adding the function name before the function like so:
local showCredits = {}
function showCredits.touch(e)
end
local hideCredits = {}
function hideCredits.touch(e)
end
your showCredit function will not be able to call the hideCredits function below it because the hideCredits variable has not been defined yet when the showCredit function was defined. Although this may not effect your current game, in future apps or games, you may need to call functions inside of other functions. To make this work properly, predefine all your function variables first, then define all your function afterwards. Hope this helps.

How would I get my script to reset its score upon button press?

I need some help understanding how would I reset the score of numMiss, numHit, and numPercent back to 0 as soon as I tap the "reset.png" button and while doing so will also start the game from the beginning again.
Also, let me know if there are any corrections to be made within my code.
Heres what I have of the code so far
--width and height
WIDTH = display.contentWidth --320
HEIGHT = display.contentHeight --480
--display background
local p = display.newImageRect("park.png" ,500, 570)
p.x = WIDTH/2
p.y = HEIGHT/2
--display bouncing dog
local RADIUS = 5
local d = display.newImageRect("dogeball.png", 70, 70)
d.x = 50
d.y = 100
--display treat
local t = display.newImageRect("treat.png", 50, 50)
t.x = 245
t.y = math.random(HEIGHT)
--displays the reset button
local r = display.newImageRect("reset.png", 100,100)
r.x = 280
r.y = 480
--starting value of gravity and bounce(will change)
local GRAVITY = 0.3
local BOUNCE = 0.75
--downward force
local velocity = 0
--Tells the score to reset when true
local reset = false
--shows number of hits
local numHit = 0
--shows number of misses
local numMiss = 0
--Gets Percentage score
local numPercent = 0
--make hits and misses display
scoreHits = display.newText("Hits = " .. numHit, WIDTH/7, 1, native.systemFont, 18)
scoreMisses = display.newText("Misses = " .. numMiss, WIDTH/2.1, 1, native.systemFont, 18)
scorePercent = display.newText("Hit % = " .. numPercent, WIDTH/1.2, 1, native.systemFont, 18)
function enterFrame()
d.y = d.y + velocity
velocity = velocity + GRAVITY
local HIT_SLOP = RADIUS * 8 -- Adjust this to adjust game difficulty
if math.abs(t.x - d.x) <= HIT_SLOP
and math.abs(t.y - d.y) <= HIT_SLOP then
numHit = numHit + 1
scoreHits.text = "Hits = " .. numHit
--count 1 hit once dog and treat hit eachother
if (t.x - d.x) <= HIT_SLOP and (t.y - d.y) <= HIT_SLOP then
t.x = 400 --resets treat postioning
t.y = math.random(HEIGHT) --gives treat a random y coordinate
end
end
--puts the barrier at the bottom of the screen and tells dog to bounce from there
if (d.y > HEIGHT) then
d.y = HEIGHT
velocity = -velocity * BOUNCE
end
t.x = t.x - 5 --speed treat goes
if t.x < -350 then--position of the treat
t.x = 400
scoreMisses.text = "Misses = " .. numMiss
else if t.x < -100 then
t.y = math.random(HEIGHT) --random height after treat goes past dog
else if t.x < -99 then
numMiss = numMiss + 1 --calculates misses when goes past screen
scoreMisses.text = "Misses = " .. numMiss
end
end
end
--calculate percentage hits
numPercent = 100 * numHit / (numHit + numMiss)
scorePercent.text = "Hit % = " .. math.round(numPercent) --prints and rounds percentage
function tapped(event) --when tapped on reset, score gets reset
--reset function goes here
end
end
r:addEventListener( "tap", tapped )
end
function touched(event)
-- print(event.phase)
if event.phase == "began" then
velocity = velocity - 6 -- thrusts dog
end
return true
end
Runtime:addEventListener( "enterFrame" , enterFrame )
Runtime:addEventListener( "touch", touched )
To make your image a button you need to add an event listener that responds to touch or tap events.
see http://docs.coronalabs.com/api/event/touch/index.html
Or you use the widget library which gives you the possibility to use a blank button background and set only the label for each button what will be very handy when you include translations for other languages.
see http://docs.coronalabs.com/api/library/widget/newButton.html
In my game I have a function gameInit() that sets the hole game and all variables. This function is called when the game starts and also when the player wants to do a reset as it over writes the old variables. (there are other technics, depending on the complexity of your game and if you for example want to store the game settings for the next time the player starts the game)

How to utilize combo scoring in Corona?

i just have a quick question. Im playing around with Corona to try and get the look and feel of it and im currently editing some one else's sample code. I have added a scoring mechanism, and now i want to add a combo scoring mechanism so that when the user chops more than one fruit, i can add an extra 5 points. Its just that i have no idea how to start. If some one would point me in the right direction that would be great thank you. :)
Or if this would be much clearer: How about a function that will help me detect how many objects the user touches/slices in one touch/move?
require ("physics")
local ui = require("ui")
physics.start()
-- physics.setDrawMode ( "hybrid" ) -- Uncomment in order to show in hybrid mode
physics.setGravity( 0, 9.8 * 2)
physics.start()
-- Audio for slash sound (sound you hear when user swipes his/her finger across the screen)
local slashSounds = {slash1 = audio.loadSound("slash1.wav"), slash2 = audio.loadSound("slash2.wav"), slash3 = audio.loadSound("slash3.wav")}
local slashSoundEnabled = true -- sound should be enabled by default on startup
local minTimeBetweenSlashes = 150 -- Minimum amount of time in between each slash sound
local minDistanceForSlashSound = 50 -- Amount of pixels the users finger needs to travel in one frame in order to play a slash sound
-- Audio for chopped fruit
local choppedSound = {chopped1 = audio.loadSound("chopped1.wav"), chopped2 = audio.loadSound("chopped2.wav")}
-- Audio for bomb
local preExplosion = audio.loadSound("preExplosion.wav")
local explosion = audio.loadSound("explosion.wav")
-- Adding a collision filter so the fruits do not collide with each other, they only collide with the catch platform
local fruitProp = {density = 1.0, friction = 0.3, bounce = 0.2, filter = {categoryBits = 2, maskBits = 1}}
local catchPlatformProp = {density = 1.0, friction = 0.3, bounce = 0.2, filter = {categoryBits = 1, maskBits = 2}}
-- Gush filter should not interact with other fruit or the catch platform
local gushProp = {density = 1.0, friction = 0.3, bounce = 0.2, filter = {categoryBits = 4, maskBits = 8} }
-- Will contain all fruits available in the game
local avalFruit = {}
-- Slash line properties (line that shows up when you move finger across the screen)
local maxPoints = 5
local lineThickness = 20
local lineFadeTime = 250
local endPoints = {}
-- Whole Fruit physics properties
local minVelocityY = 850
local maxVelocityY = 1100
local minVelocityX = -200
local maxVelocityX = 200
local minAngularVelocity = 100
local maxAngularVelocity = 200
-- Chopped fruit physics properties
local minAngularVelocityChopped = 100
local maxAngularVelocityChopped = 200
-- Splash properties
local splashFadeTime = 2500
local splashFadeDelayTime = 5000
local splashInitAlpha = .5
local splashSlideDistance = 50 -- The amoutn of of distance the splash slides down the background
-- Contains all the available splash images
local splashImgs = {}
-- Gush properties
local minGushRadius = 10
local maxGushRadius = 25
local numOfGushParticles = 15
local gushFadeTime = 500
local gushFadeDelay = 500
local minGushVelocityX = -350
local maxGushVelocityX = 350
local minGushVelocityY = -350
local maxGushVelocityY = 350
-- Timer references
local bombTimer
local fruitTimer
-- Game properties
local fruitShootingInterval = 1000
local bombShootingInterval = 5000
-- Groups for holding the fruit and splash objects
local splashGroup
local fruitGroup
local sampleVar = true
local score = 0
local scoreText
function main()
score = 0
display.setStatusBar( display.HiddenStatusBar )
setUpBackground()
scoreText = display.newText("Score: 0", 415, 100, native.systemFont, 50)
scoreText:setTextColor(255, 255, 255)
scoreText.text = ("Score: " )..score
pauseAndResume ()
setUpCatchPlatform()
initGroups()
initFruitAndSplash()
Runtime:addEventListener("touch", drawSlashLine)
timer.performWithDelay( 1000, displayScore)
startGame()
end
function displayScore()
scoreText.text = ("Score: " )..score
score = score + 2
end
function startGame()
shootObject("fruit")
bombTimer = timer.performWithDelay(bombShootingInterval, function(event) shootObject("bomb") end, 0)
fruitTimer = timer.performWithDelay(fruitShootingInterval, function(event) shootObject("fruit") end, 0)
end
-- Display the pause button
function pauseAndResume ()
pausebutton = display.newImage ("paused2.png", 10, 100)
pausebutton:addEventListener ("touch" , pauseGame)
resumebutton = display.newImage ("resume.png", 10, 100)
resumebutton.isVisible = false
resumebutton:addEventListener ("touch", resumeGame)
end
function pauseGame (event)
if (event.phase == "ended") then
physics.pause ()
pausebutton.isVisible = false
resumebutton.isVisible = true
timer.pause(fruitTimer)
timer.pause(bombTimer)
sampleVar = false
return true
end
end
function resumeGame (event)
if (event.phase == "ended") then
physics.start()
pausebutton.isVisible = true
resumebutton.isVisible = false
timer.resume(fruitTimer)
timer.resume(bombTimer)
sampleVar = true
return true
end
end
function initGroups()
splashGroup = display.newGroup()
fruitGroup = display.newGroup()
end
function setUpBackground()
local background = display.newImage("bg.png", true)
background.x = display.contentWidth / 2
background.y = display.contentHeight / 2
end
-- Populates avalFruit with all the fruit images and thier widths and heights
function initFruitAndSplash()
local watermelon = {}
watermelon.whole = "watermelonWhole.png"
watermelon.top = "watermelonTop.png"
watermelon.bottom = "watermelonBottom.png"
watermelon.splash = "redSplash.png"
table.insert(avalFruit, watermelon)
local strawberry = {}
strawberry.whole = "strawberryWhole.png"
strawberry.top = "strawberryTop.png"
strawberry.bottom = "strawberryBottom.png"
strawberry.splash = "redSplash.png"
table.insert(avalFruit, strawberry)
-- Initialize splash images
table.insert(splashImgs, "splash1.png")
table.insert(splashImgs, "splash2.png")
table.insert(splashImgs, "splash3.png")
end
function getRandomFruit()
local fruitProp = avalFruit[math.random(1, #avalFruit)]
local fruit = display.newImage(fruitProp.whole)
fruit.whole = fruitProp.whole
fruit.top = fruitProp.top
fruit.bottom = fruitProp.bottom
fruit.splash = fruitProp.splash
return fruit
end
function getBomb()
local bomb = display.newImage( "bomb.png")
return bomb
end
function shootObject(type)
local object = type == "fruit" and getRandomFruit() or getBomb()
fruitGroup:insert(object)
object.x = display.contentWidth / 2
object.y = display.contentHeight + object.height * 2
fruitProp.radius = object.height / 2
physics.addBody(object, "dynamic", fruitProp)
if(type == "fruit") then
object:addEventListener("touch", function(event) chopFruit(object) end)
else
local bombTouchFunction
bombTouchFunction = function(event) explodeBomb(object, bombTouchFunction); end
object:addEventListener("touch", bombTouchFunction)
end
-- Apply linear velocity
local yVelocity = getRandomValue(minVelocityY, maxVelocityY) * -1 -- Need to multiply by -1 so the fruit shoots up
local xVelocity = getRandomValue(minVelocityX, maxVelocityX)
object:setLinearVelocity(xVelocity, yVelocity)
-- Apply angular velocity (the speed and direction the fruit rotates)
local minAngularVelocity = getRandomValue(minAngularVelocity, maxAngularVelocity)
local direction = (math.random() < .5) and -1 or 1
minAngularVelocity = minAngularVelocity * direction
object.angularVelocity = minAngularVelocity
end
function explodeBomb(bomb, listener)
bomb:removeEventListener("touch", listener)
-- The bomb should not move while exploding
bomb.bodyType = "kinematic"
bomb:setLinearVelocity(0, 0)
bomb.angularVelocity = 0
-- Shake the stage
local stage = display.getCurrentStage()
local moveRightFunction
local moveLeftFunction
local rightTrans
local leftTrans
local shakeTime = 50
local shakeRange = {min = 1, max = 25}
moveRightFunction = function(event) rightTrans = transition.to(stage, {x = math.random(shakeRange.min,shakeRange.max), y = math.random(shakeRange.min, shakeRange.max), time = shakeTime, onComplete=moveLeftFunction}); end
moveLeftFunction = function(event) leftTrans = transition.to(stage, {x = math.random(shakeRange.min,shakeRange.max) * -1, y = math.random(shakeRange.min,shakeRange.max) * -1, time = shakeTime, onComplete=moveRightFunction}); end
moveRightFunction()
local linesGroup = display.newGroup()
-- Generate a bunch of lines to simulate an explosion
local drawLine = function(event)
local line = display.newLine(bomb.x, bomb.y, display.contentWidth * 2, display.contentHeight * 2)
line.rotation = math.random(1,360)
line.width = math.random(15, 25)
linesGroup:insert(line)
end
local lineTimer = timer.performWithDelay(100, drawLine, 0)
-- Function that is called after the pre explosion
local explode = function(event)
audio.play(explosion)
blankOutScreen(bomb, linesGroup);
timer.cancel(lineTimer)
stage.x = 0
stage.y = 0
transition.cancel(leftTrans)
transition.cancel(rightTrans)
end
-- Play the preExplosion sound first followed by the end explosion
audio.play(preExplosion, {onComplete = explode})
timer.cancel(fruitTimer)
timer.cancel(bombTimer)
end
function blankOutScreen(bomb, linesGroup)
local gameOver = displayGameOver()
gameOver.alpha = 0 -- Will reveal the game over screen after the explosion
-- Create an explosion animation
local circle = display.newCircle( bomb.x, bomb.y, 5 )
local circleGrowthTime = 300
local dissolveDuration = 1000
local dissolve = function(event) transition.to(circle, {alpha = 0, time = dissolveDuration, delay = 0, onComplete=function(event) gameOver.alpha = 1 end}); gameOver.alpha = 1 end
circle.alpha = 0
transition.to(circle, {time=circleGrowthTime, alpha = 1, width = display.contentWidth * 3, height = display.contentWidth * 3, onComplete = dissolve})
end
function displayGameOver()
-- Will return a group so that we can set the alpha of the entier menu
local group = display.newGroup()
-- Dim the background with a transperent square
local back = display.newRect( 0,0, display.contentWidth, display.contentHeight )
back:setFillColor(0,0,0, 255 * .1)
group:insert(back)
local gameOver = display.newImage( "gameover.png")
gameOver.x = display.contentWidth / 2
gameOver.y = display.contentHeight / 2
group:insert(gameOver)
local replayButton = ui.newButton{
default = "replayButton.png",
over = "replayButton.png",
onRelease = function(event) group:removeSelf(); main() ; end
}
group:insert(replayButton)
replayButton.x = display.contentWidth / 2
replayButton.y = gameOver.y + gameOver.height / 2 + replayButton.height / 2
return group
end
-- Return a random value between 'min' and 'max'
function getRandomValue(min, max)
return min + math.abs(((max - min) * math.random()))
end
function playRandomSlashSound()
audio.play(slashSounds["slash" .. math.random(1, 3)])
end
function playRandomChoppedSound()
audio.play(choppedSound["chopped" .. math.random(1, 2)])
end
function getRandomSplash()
return display.newImage(splashImgs[math.random(1, #splashImgs)])
end
function chopFruit(fruit)
if (sampleVar == true) then
displayScore()
playRandomChoppedSound()
createFruitPiece(fruit, "top")
createFruitPiece(fruit, "bottom")
createSplash(fruit)
createGush(fruit)
fruit:removeSelf()
end
end
-- Creates a gushing effect that makes it look like juice is flying out of the fruit
function createGush(fruit)
local i
for i = 0, numOfGushParticles do
local gush = display.newCircle( fruit.x, fruit.y, math.random(minGushRadius, maxGushRadius) )
gush:setFillColor(255, 0, 0, 255)
gushProp.radius = gush.width / 2
physics.addBody(gush, "dynamic", gushProp)
local xVelocity = math.random(minGushVelocityX, maxGushVelocityX)
local yVelocity = math.random(minGushVelocityY, maxGushVelocityY)
gush:setLinearVelocity(xVelocity, yVelocity)
transition.to(gush, {time = gushFadeTime, delay = gushFadeDelay, width = 0, height = 0, alpha = 0, onComplete = function(event) gush:removeSelf() end})
end
end
function createSplash(fruit)
local splash = getRandomSplash()
splash.x = fruit.x
splash.y = fruit.y
splash.rotation = math.random(-90,90)
splash.alpha = splashInitAlpha
splashGroup:insert(splash)
transition.to(splash, {time = splashFadeTime, alpha = 0, y = splash.y + splashSlideDistance, delay = splashFadeDelayTime, onComplete = function(event) splash:removeSelf() end})
end
-- Chops the fruit in half
-- Uses some trig to calculate the position
-- of the top and bottom part of the chopped fruit (http://en.wikipedia.org/wiki/Rotation_matrix#Rotations_in_two_dimensions)
function createFruitPiece(fruit, section)
local fruitVelX, fruitVelY = fruit:getLinearVelocity()
-- Calculate the position of the chopped piece
local half = display.newImage(fruit[section])
half.x = fruit.x - fruit.x -- Need to have the fruit's position relative to the origin in order to use the rotation matrix
local yOffSet = section == "top" and -half.height / 2 or half.height / 2
half.y = fruit.y + yOffSet - fruit.y
local newPoint = {}
newPoint.x = half.x * math.cos(fruit.rotation * (math.pi / 180)) - half.y * math.sin(fruit.rotation * (math.pi / 180))
newPoint.y = half.x * math.sin(fruit.rotation * (math.pi / 180)) + half.y * math.cos(fruit.rotation * (math.pi / 180))
half.x = newPoint.x + fruit.x -- Put the fruit back in its original position after applying the rotation matrix
half.y = newPoint.y + fruit.y
fruitGroup:insert(half)
-- Set the rotation
half.rotation = fruit.rotation
fruitProp.radius = half.width / 2 -- We won't use a custom shape since the chopped up fruit doesn't interact with the player
physics.addBody(half, "dynamic", fruitProp)
-- Set the linear velocity
local velocity = math.sqrt(math.pow(fruitVelX, 2) + math.pow(fruitVelY, 2))
local xDirection = section == "top" and -1 or 1
local velocityX = math.cos((fruit.rotation + 90) * (math.pi / 180)) * velocity * xDirection
local velocityY = math.sin((fruit.rotation + 90) * (math.pi / 180)) * velocity
half:setLinearVelocity(velocityX, velocityY)
-- Calculate its angular velocity
local minAngularVelocity = getRandomValue(minAngularVelocityChopped, maxAngularVelocityChopped)
local direction = (math.random() < .5) and -1 or 1
half.angularVelocity = minAngularVelocity * direction
end
-- Creates a platform at the bottom of the game "catch" the fruit and remove it
function setUpCatchPlatform()
local platform = display.newRect( 0, 0, display.contentWidth * 4, 50)
platform.x = (display.contentWidth / 2)
platform.y = display.contentHeight + display.contentHeight
physics.addBody(platform, "static", catchPlatformProp)
platform.collision = onCatchPlatformCollision
platform:addEventListener( "collision", platform )
end
function onCatchPlatformCollision(self, event)
-- Remove the fruit that collided with the platform
event.other:removeSelf()
end
-- Draws the slash line that appears when the user swipes his/her finger across the screen
function drawSlashLine(event)
-- Play a slash sound
if(endPoints ~= nil and endPoints[1] ~= nil) then
local distance = math.sqrt(math.pow(event.x - endPoints[1].x, 2) + math.pow(event.y - endPoints[1].y, 2))
if(distance > minDistanceForSlashSound and slashSoundEnabled == true) then
playRandomSlashSound();
slashSoundEnabled = false
timer.performWithDelay(minTimeBetweenSlashes, function(event) slashSoundEnabled = true end)
end
end
-- Insert a new point into the front of the array
table.insert(endPoints, 1, {x = event.x, y = event.y, line= nil})
-- Remove any excessed points
if(#endPoints > maxPoints) then
table.remove(endPoints)
end
for i,v in ipairs(endPoints) do
local line = display.newLine(v.x, v.y, event.x, event.y)
line.width = lineThickness
transition.to(line, {time = lineFadeTime, alpha = 0, width = 0, onComplete = function(event) line:removeSelf() end})
end
if(event.phase == "ended") then
while(#endPoints > 0) do
table.remove(endPoints)
end
end
end
main()
Try Runtime touch listener ...in that listener event.phase == "move" you can do like above.In event.phase=="ended" reset the value.
Initial set some temp value.
Tempvalue=0
function displayScore()
if Tempvalue == 0 then
Tempvalue=Tempvalue+1
score = score + 2
scoreText.text = ("Score: " )..score
else
score = score + 5
scoreText.text = ("Score: " )..score
end
I just put the sample how to do.
temp=0
local newLine = function(event)
if event.phase=="began" then
elseif event.phase=="moved" then
for i=1,3 do
if event.target.name==i then
temp=temp+1
--here you can do your action to the object(like remove or score)
print("touch"..temp)
end
end
elseif event.phase=="ended" then
end
return true
end
for i=1,3 do
local myCircle = display.newCircle( 100*i, 100, 9 )
myCircle:setFillColor(128,128,128)
myCircle.name=i
myCircle:addEventListener("touch",newLine)
end

Issue with attempted to index a nil variable in lua / corona sdk

I'm pretty new to lua as you can probably tell by the code, i'm trying to remove an object that says "Stop!" when the time runs out by using an event listener on a button object that's also created when the time runs out. This returns the error attempt to index global 'stopit' (a nil value). I declared it as a local var in the class that adds it to the screen so i'm not sure whats going on. I have organized and tried this several different ways and I can't get it to loop continually without randomly crashing either immediately or after one or two rounds of the game.
Here is the code:
display.setStatusBar(display.HiddenStatusBar)
local centerX = display.contentCenterX
local centerY = display.contentCenterY
local score = 0
local dextime;
local stopit;
local button3;
function newTarget(event)
timer.performWithDelay(100, function() display.remove(target) end)
transition.to(target, {time=99, xScale=.4, yScale=.4})
timer.performWithDelay(101, dexit)
score = score + 10
scoreTxt.text = ("Score:" .. score)
end
function dexit()
stopit = display.newImage("stop.png")
stopit.x = 300
stopit.y = 600
stopit.isVisible = false
button3 = display.newImage("button3.png")
button3:addEventListener("tap", removeitems)
button3.x = centerX
button3.y = centerY
button3.isVisible = false
target = display.newImage("target.png")
target.xScale = .25
target.yScale = .25
target.x = math.random(50, 550)
target.y = math.random(50, 750)
target:addEventListener("tap", newTarget)
local function removeitems(event)
stopit:removeSelf()
button3:removeSelf()
scoreTxt:removeSelf()
timerTxt:removeSelf()
timer.performWithDelay(500, setup)
dextime = 15
score = 0
end
timer.performWithDelay(15000, function() display.remove(target) end)
timer.performWithDelay( 15000, function() button3.isVisible = true end)
timer.performWithDelay(15000, function() stopit.isVisible = true end)
end
local function dexgo()
timer.performWithDelay(1000, function() dextime = dextime - 1 end, 15)
timer.performWithDelay(1001, function() timerTxt.text = ("Time:" .. dextime) end, 15)
dexit()
end
local function one2()
local one = display.newImage("1.png")
one.x = centerX
one.y = centerY
one.alpha = 0
transition.to(one, {time=1000, alpha =1, onComplete=dexgo})
timer.performWithDelay( 1000, function()
display.remove(one)
end, 1)
end
local function two2()
local two = display.newImage("2.png")
two.x = centerX
two.y = centerY
two.alpha = 0
transition.to(two, {time=1000, alpha =1, onComplete=one2})
timer.performWithDelay( 1000, function()
display.remove(two)
end, 1)
end
local function dexMode()
local three = display.newImage("3.png")
three.x = centerX
three.y = centerY
three.alpha = 0
timerTxt = display.newText("Time:" .. dextime,-1, centerX - 440, "Helvetica", 40)
scoreTxt = display.newText( "Score:" .. score, 440, -140, "Helvetica", 40)
display.remove(mode1)
display.remove(mode2)
display.remove(title)
transition.to(three, {time=1000, alpha =1, onComplete=two2})
timer.performWithDelay( 1000, function()
display.remove(three)
end, 1)
bg = nil
title = nil
mode1 = nil
mode2 = nil
end
function listener(event)
simpleMode()
end
function listener2(event)
dexMode()
end
function startGame()
transition.to( title, { time=2000, y=0, alpha=.9, onComplete=showTitle})
transition.to(bg, { time=2000, y=centerY, alpha=1})
transition.to(mode1, { time=2000, x=centerX, alpha=.9})
transition.to(mode2, { time=2000, x=centerX, alpha=.9})
end
function setup()
dextime = 15;
bg = display.newImage("background.png")
bg.yScale = 1.4
bg.alpha = 0
title = display.newImage("title.png")
title.x = centerX
title.y = -200
title.alpha = 0
mode1 = display.newImage("button1.png")
mode1.xScale = 1.23
mode1.yScale = 1.23
mode1.x = 800
mode1.y = 500
mode1.alpha = 0
mode2 = display.newImage("button2.png")
mode2.xScale = 1.23
mode2.yScale = 1.23
mode2.x = -200
mode2.y = 625
mode2.alpha = 0
mode1:addEventListener( "touch", listener )
mode2:addEventListener( "touch", listener2 )
startGame()
end
setup()
The usage of in stopit button3, scoreTxt and timerTxt in function removeitems(event) are globally scoped. When removeitems gets called in dexit it cannot see the local variables you declared in dexit.
The easiest solution is to make removeitems a closure by moving it into dexit:
function dexit()
local stopit = display.newImage("stop.png")
local button3 = display.newImage("button3.png")
local function removeitems(event)
stopit:removeSelf()
button3:removeSelf()
scoreTxt:removeSelf()
timerTxt:removeSelf()
timer.performWithDelay(500, setup)
end
-- ...
end
Try this:
if(stopit~=nil)then
stopit:removeSelf()
end
You are getting this error due to timer and transition. When you are removing an object without canceling timer or transition, it's calling in loop and getting nil value after removing once.
First of all you need to store all the timer and transition in array and release array while stoping game or changing scene. Then Reinitialize array again.
Ex: local timerId = {}
local TransitionID = {}
timerId[#timerId+1] = timer.performWithDelay(15000, function() display.remove(target) end)
TransitionID[#TransitionID+1] = transition.to(two, {time=1000, alpha =1, onComplete=one2})
When removing all the object remove timer and transition first.
for i = 1, #timerId do
timer.cancel(timerId[i])
timerId[i] = nil
timerId = {} //Initializing array
end
for j = 1, #transitionID do
transition.cancel(transitionID[j])
transitionID[j] = nil
transitionID = {}
end

Resources