Hi
I am on Chapter 5 of the ‘Getting Started’ Corona tutorial, here, and I am at a loss now with this issue.
The error:
“attempt to perform arithmetic on upvalue ‘score’ (a table value)”
This error is the same with the variable ‘lives’ depending on which is changed first in the game.
What I’ve tried:
- I have made sure the variables are declared and assigned to at the top of the file.
- I copy-pasted the source provided by the tutorial and continue to get the same error, which confirms for me that I had indeed followed along correctly, which I was pretty certain of just from combing through it.
- There doesn’t appear to be anything in outside of this file ‘game.lua’ that would cause this problem. (other files are ‘main.lua’ and ‘menu.lua’)
The code:
I have shortened the code for readability. I am pretty sure the below snippets are what’s relevant. However, I will paste the full source at the bottom of this post just in case, it’s relatively short for the beginner tutorial.
I inserted *** HERE *** comments on the right at the points where the variables are declared and the arithmetic in question is performed.
Edited code:
local composer = require( "composer" ) local scene = composer.newScene() -- MORE CODE HERE -- Initialise variables local lives = 3 -- \*\*\* HERE \*\*\* local score = 0 local died = false -- VARIOUS FUNCTIONS HERE -- collision handler/listener function local function onCollision( event ) if ( event.phase == "began" ) then -- get references to the objects local obj1 = event.object1 local obj2 = event.object2 -- laser/asteroid collision if ( ( obj1.myName == "laser" and obj2.myName == "asteroid" ) or ( obj1.myName == "asteroid" and obj2.myName == "laser" ) ) then -- remove both laser and asteroid display.remove( obj1 ) display.remove( obj2 ) -- remove the asteroid from asteroidsTable for i = #asteroidsTable, 1, -1 do if ( asteroidsTable[i] == obj1 or asteroidsTable[i] == obj2 ) then table.remove( asteroidsTable, i ) break -- no need to keep searching end end -- increase score for hitting the roid score = score + 100 -- \*\*\* HERE \*\*\* scoreText.text = "Score: " .. score -- ship/asteroid collision elseif ( ( obj1.myName == "asteroid" and obj2.myName == "ship" ) or ( obj1.myName == "ship" and obj2.myName == "asteroid" ) ) then -- are we dead now?? if ( died == false ) then died = true -- take a life lives = lives - 1 -- \*\*\* HERE \*\*\* livesText.text = "Lives: " .. lives -- check for Game Over if ( lives == 0 ) then display.remove( ship ) -- \*\*\* GAME OVER \*\*\* else -- else respawn ship.alpha = 0 timer.performWithDelay( 1000, respawnShip ) end end end end end -- ----------------------------------------------------------------------------------- -- Scene event functions -- -----------------------------------------------------------------------------------
Full source:
local composer = require( "composer" ) local scene = composer.newScene() -- ----------------------------------------------------------------------------------- -- Code outside of the scene event functions below will only be executed ONCE unless -- the scene is removed entirely (not recycled) via "composer.removeScene()" -- ----------------------------------------------------------------------------------- -- start da physics local physics = require("physics") physics.start() physics.setGravity(0, 0) -- Image sheet instructions local sheetOptions = { frames = { { -- 1) asteroid 1 x = 0, y = 0, width = 102, height = 85 }, { -- 2) asteroid 2 x = 0, y = 85, width = 90, height = 83 }, { -- 3) asteroid 3 x = 0, y = 168, width = 100, height = 97 }, { -- 4) ship x = 0, y = 265, width = 98, height = 79 }, { -- 5) laser x = 98, y = 265, width = 14, height = 40 }, } } -- initialise a sprite/image sheet local objectSheet = graphics.newImageSheet( "gameObjects.png", sheetOptions ) -- Initialise variables local lives = 3 -- \*\*\*HERE \*\*\* local score = 0 local died = false local asteroidsTable = {} local ship local gameLoopTimer local livesText local scoreText -- main game display groups local backGroup local mainGroup local uiGroup -- update HUD function local function updateHUD() livesText.text = "Lives: " .. lives scoreText.text = "Score: " .. score end -- function to create asteroid local function createAsteroid() local asteroid = display.newImageRect(mainGroup, objectSheet, 1, 102, 85) table.insert(asteroidsTable, asteroid) physics.addBody( asteroid, { radius=40, bounce=0.8 } ) asteroid.myName = "asteroid" -- set entry point of asteroid local entryPoint = math.random(3) -- top, left, right if ( entryPoint == 1 ) then -- from the left asteroid.x = -60 asteroid.y = math.random(500) asteroid:setLinearVelocity( math.random(40, 120), math.random(20, 60) ) elseif ( entryPoint == 2) then -- from the top asteroid.x = math.random( display.contentWidth ) asteroid.y = -60 asteroid:setLinearVelocity( math.random(-40, 40), math.random(40, 120)) elseif (entryPoint == 3) then -- from right asteroid.x = display.contentWidth + 60 asteroid.y = math.random(500) asteroid:setLinearVelocity( math.random(-120, -40), math.random(20, 60)) end -- give asteroid rotation asteroid:applyTorque( math.random(-6, 6) ) end -- function to load laser local function fireLaser() local laser = display.newImageRect( mainGroup, objectSheet, 5, 14, 40 ) laser.x = ship.x laser.y = ship.y physics.addBody( laser, "dynamic", { isSensor=true }) laser.myName = "laser" laser:toBack() -- send to bottom layer of mainGroup -- fire laser transition.to(laser, { y=-40, time=500, onComplete = function() display.remove( laser ) end }) end -- function to add ship movement via touch/drag local function dragShip(event) local ship = event.target -- the ship local phase = event.phase -- which phase of touch if ( phase == "began" ) then -- set focus on the ship display.currentStage:setFocus( ship ) ship.touchOffsetX = event.x - ship.x -- retain offset elseif ( phase == "moved" ) then -- move the ship to new position -- calculate new position local newPosition = event.x - ship.touchOffsetX -- keep the ship fully on-screen if ( newPosition \>= display.contentWidth - 150 ) then -- check the right ship.x = display.contentWidth - 150 elseif ( newPosition \<= 150 ) then -- check the left ship.x = 150 else ship.x = newPosition -- go ahead end elseif ( phase == "ended" or phase == "cancelled" ) then -- release touch focus on the hip display.currentStage:setFocus( nil ) end return true -- prevent touch event from affecting other objects end -- implement a game loop local function gameLoop() -- create an asteroid createAsteroid() -- garbage collect spent asteroids -- iterate through asteroidTable for i = #asteroidsTable, 1, -1 do local thisAsteroid = asteroidsTable[i] -- bounds check for spent asteroids if ( thisAsteroid.x \< -100 -- check left or thisAsteroid.x \> display.contentWidth + 100 -- check right or thisAsteroid.y \> display.contentHeight + 100 -- check bottom or thisAsteroid.y \< -100 ) then -- check top display.remove( thisAsteroid ) -- clean up spent asteroid table.remove( asteroidsTable, i ) -- remove asteroid from table end end end -- function to respawn the ship after death (live allowing) local function respawnShip() ship.isBodyActive = false ship:setLinearVelocity( 0, 0 ) ship.x = display.contentCenterX ship.y = display.contentHeight - 100 -- fade the ship in transition.to( ship, { alpha=1, time=4000, onComplete = function() ship.isBodyActive = true died = false end }) end -- collision handler/listener function local function onCollision( event ) if ( event.phase == "began" ) then -- get references to the objects local obj1 = event.object1 local obj2 = event.object2 -- laser/asteroid collision if ( ( obj1.myName == "laser" and obj2.myName == "asteroid" ) or ( obj1.myName == "asteroid" and obj2.myName == "laser" ) ) then -- remove both laser and asteroid display.remove( obj1 ) display.remove( obj2 ) -- remove the asteroid from asteroidsTable for i = #asteroidsTable, 1, -1 do if ( asteroidsTable[i] == obj1 or asteroidsTable[i] == obj2 ) then table.remove( asteroidsTable, i ) break -- no need to keep searching end end -- increase score for hitting the roid score = score + 100 -- \*\*\* HERE \*\*\* scoreText.text = "Score: " .. score -- ship/asteroid collision elseif ( ( obj1.myName == "asteroid" and obj2.myName == "ship" ) or ( obj1.myName == "ship" and obj2.myName == "asteroid" ) ) then -- are we dead now?? if ( died == false ) then died = true -- take a life lives = lives - 1 -- \*\*\* HERE \*\*\* livesText.text = "Lives: " .. lives -- check for Game Over if ( lives == 0 ) then display.remove( ship ) -- \*\*\* GAME OVER \*\*\* else -- else respawn ship.alpha = 0 timer.performWithDelay( 1000, respawnShip ) end end end end end -- ----------------------------------------------------------------------------------- -- Scene event functions -- ----------------------------------------------------------------------------------- -- create() function scene:create( event ) local sceneGroup = self.view -- Code here runs when the scene is first created but has not yet appeared on screen -- Pause so that placed objects don't start moving until games starts physics.pause() -- insert the display groups into the scene backGroup = display.newGroup() sceneGroup:insert( backGroup ) mainGroup = display.newGroup() sceneGroup:insert( mainGroup ) uiGroup = display.newGroup() sceneGroup:insert( uiGroup ) -- load the background local background = display.newImageRect( backGroup, "background.png", 800, 1400 ) background.x = display.contentCenterX background.y = display.contentCenterY -- load the ship ship = display.newImageRect( mainGroup, objectSheet, 4, 98, 79 ) ship.x = display.contentCenterX ship.y = display.contentHeight - 100 physics.addBody( ship, { radius=30, isSensor=true }) ship.myName = "ship" -- load the score/lives text lives = display.newText( uiGroup, "Lives: " .. lives, 200, 80, native.systemFont, 36 ) score = display.newText( uiGroup, "Score: " .. score, 560, 80, native.systemFont, 36 ) -- add ship's event listeners ship:addEventListener( "tap", fireLaser ) ship:addEventListener( "touch", dragShip ) end -- show() function scene:show( event ) local sceneGroup = self.view local phase = event.phase if ( phase == "will" ) then -- Code here runs when the scene is still off screen (but is about to come on screen) elseif ( phase == "did" ) then -- Code here runs when the scene is entirely on screen -- start physics engine again physics.start() Runtime:addEventListener( "collision", onCollision ) gameLoopTimer = timer.performWithDelay( 500, gameLoop, 0 ) end end -- hide() function scene:hide( event ) local sceneGroup = self.view local phase = event.phase if ( phase == "will" ) then -- Code here runs when the scene is on screen (but is about to go off screen) elseif ( phase == "did" ) then -- Code here runs immediately after the scene goes entirely off screen end end -- destroy() function scene:destroy( event ) local sceneGroup = self.view -- Code here runs prior to the removal of scene's view end -- ----------------------------------------------------------------------------------- -- Scene event function listeners -- ----------------------------------------------------------------------------------- scene:addEventListener( "create", scene ) scene:addEventListener( "show", scene ) scene:addEventListener( "hide", scene ) scene:addEventListener( "destroy", scene ) -- ----------------------------------------------------------------------------------- return scene