简体   繁体   中英

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!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM