Having some trouble.....Attempt to index upvalue 'rope' (a nil value)/joint not getting destroyed

So I am trying to make a grappling hook for my game, but I’m running into some problems when destroying the distance joint. When I touch the screen (which should destroy the joint and let the box drop to the ground) nothing happens (the player just keeps swinging). Then when I try to move the player up or down, it displays an error that says “Attempt to index upvalue ‘rope’ (a nil value)”. The grappling hook is supposed to create a joint between the player and a target when the target is touched, then be destroyed whenever the screen is touched again.

can someone please help me???

[lua]

function grapple:touch (event)        

    local rope = physics.newJoint (“distance”, player, grapple, player.x, player.y, event.x, event.y)

    

    local function raise (event)

        if event.phase == “began” and rope.length>30 then

            moveup = true

        end

        if event.phase == “ended” or rope.length<30 then

            moveup = false

        end    

            

        local function shortenrope (event)

            if moveup == true then

                rope.length = rope.length - 2

            end

        end

        Runtime:addEventListener(“enterFrame”, shortenrope)

    end

    up.touch = raise

    up:addEventListener(“touch”,raise)

    local function lower(event)

    if event.phase == “began” and rope.length<400 then

            movedown = true

        end

        if event.phase == “ended” or rope.length>400 then

            movedown = false

        end    

        local function lengthenrope (event)

            if movedown == true then 

                rope.length = rope.length + 2

            end    

        end    

        Runtime:addEventListener(“enterFrame”, lengthenrope)

    end

    down.touch=lower

    down:addEventListener(“touch”,lower)

    

    --this is where the problem starts

    function screen:touch(event)

        if event.phase == “began” then

            if rope ~= nil then

                rope:removeSelf() 

                rope = nil     

            end    

        end

    end    

    screen:addEventListener(“touch”,screen) 

end    

grapple:addEventListener(“touch”,grapple)

[/lua]

I haven’t read your code with respect to fixing the bug, but more on that in a moment, though I did notice that you’re trying to make a grappling hook using a distance joint and setting it’s length. Though I could be wrong now, some time ago I tried this and found that adjusting the length of a distance joint does not work, you have to rely on the distance between the two objects when the joint is created. You might be better off using a couple of touch joints. I can provide that code if you like as I posted something like it to the Code Exchange a year ago. I have other physics code here, too:

http://springboardpillow.blogspot.co.uk/2012/04/sample-code.html

So, I took your code and added bodies until it worked. This is what I ended up with. Maybe it’s a bit different from yours, see what you think:

require("physics") physics.start() physics.setGravity(0,10) physics.setDrawMode("hybrid") local screen = display.newRect( display.actualContentWidth/2, display.actualContentHeight/2, display.actualContentWidth, display.actualContentHeight ) local grapple = display.newRect( 100, 100, 100, 100 ) local player = display.newCircle( 100, 300, 50 ) local up, down = display.newRect( 300, 100, 50, 50 ), display.newRect( 350, 100, 50, 50 ) screen.fill={1,1,1,.1} up.fill={0,1,0} down.fill={1,0,0} physics.addBody( player, "dynamic", { radius=50 } ) physics.addBody( grapple, "static" ) function grapple:touch (event) local rope = physics.newJoint("distance", player, grapple, player.x, player.y, event.x, event.y) local function raise (event) if event.phase == "began" and rope.length\>30 then moveup = true end if event.phase == "ended" or rope.length\<30 then moveup = false end local function shortenrope (event) if moveup == true then rope.length = rope.length - 2 end end Runtime:addEventListener("enterFrame", shortenrope) end up.touch = raise up:addEventListener("touch",raise) local function lower(event) if event.phase == "began" and rope.length\<400 then movedown = true end if event.phase == "ended" or rope.length\>400 then movedown = false end local function lengthenrope (event) if movedown == true then rope.length = rope.length + 2 end end Runtime:addEventListener("enterFrame", lengthenrope) end down.touch=lower down:addEventListener("touch",lower) --this is where the problem starts function screen:touch(event) if event.phase == "began" then if rope ~= nil then rope:removeSelf() rope = nil end end end screen:addEventListener("touch",screen) end grapple:addEventListener("touch",grapple)

Ok, I’ve found the bug and its quite simple really.

Place a print(“here”) immediately after this line:

function screen:touch(event)

Now run your test again. You will see that the screen’s touch function is called when you tap the grapple touch function. So, what is happening is the joint is being destroyed but a new distance joint is being created whenever you touch the grapple button.

Why? Well…

  • Your touch listener functions don’t have a return statement at the end, so the touch event drops through the buttons and hits the ‘screen’, where it causes the joint to be destroyed.

  • ALL of the code in the grapple:touch() function is executed when you touch the grapple object. This means that the ‘rope’ joint is created as a new object on each touch. You are adding lots of touch listeners to your buttons.

Btw, this is not necessary:

 up.touch = raise up:addEventListener("touch",raise)

You either assign the function to the object and make it a local listener OR you let the event listener call the function directly. Your code is trying to do both.

Local ‘table’ listener:

function up:touch(e) ... return true end up:addEventListener("touch",up) -- calling the touch listener function on the local table

Direct listener:

local function raise(e) ... return true end up:addEventListener("touch",raise) -- directly calling an arbitrary function on event

Below you can see the fixed code. I’ve modified it quite a lot, but I think you can see where it’s changed. I’ve moved the button listeners out of the grapple touch function and made the rope joint it’s own variable. In my own code, I would make the rope variable a property of the grapple object, but it’s personal preference. I’ve also moved the up/down addEventListener calls inside the grapple:touch() function, so that they are only active when there is a distance joint (I also made the buttons look disabled.)

Side note: It seems that my earlier comment is now wrong - the length property does in fact change the length of the distance joint. This is great news and I only wish it had worked like that three years ago!

-- grapple demo require("physics") physics.start() physics.setGravity(0,10) physics.setDrawMode("hybrid") local screen = display.newRect( display.actualContentWidth/2, display.actualContentHeight/2, display.actualContentWidth, display.actualContentHeight ) local grapple = display.newRect( 100, 100, 100, 100 ) local player = display.newCircle( 100, 300, 50 ) local up, down = display.newRect( 300, 100, 50, 50 ), display.newRect( 350, 100, 50, 50 ) local rope = nil screen.fill={1,1,1} up.fill={.5,.5,.5} down.fill={.5,.5,.5} physics.addBody( player, "dynamic", { radius=50 } ) physics.addBody( grapple, "static" ) function up:touch(event) print("up") if event.phase == "began" and rope.length\>30 then moveup = true end if event.phase == "ended" or rope.length\<30 then moveup = false end local function shortenrope (event) if moveup == true then rope.length = rope.length - 2 end end Runtime:addEventListener("enterFrame", shortenrope) return true end function down:touch(event) print("down") if event.phase == "began" and rope.length\<400 then movedown = true end if event.phase == "ended" or rope.length\>400 then movedown = false end local function lengthenrope (event) if movedown == true then rope.length = rope.length + 2 end end Runtime:addEventListener("enterFrame", lengthenrope) return true end --this is where the problem starts function screen:touch(event) print("screen") if event.phase == "began" then if rope ~= nil then rope:removeSelf() rope = nil up:removeEventListener("touch",up) down:removeEventListener("touch",down) up.fill={.5,.5,.5} down.fill={.5,.5,.5} end end return true end screen:addEventListener("touch",screen) function grapple:touch (event) if event.phase == "began" and rope == nil then rope = physics.newJoint("distance", player, grapple, player.x, player.y, event.x, event.y) up.fill={0,1,0} down.fill={1,0,0} up:addEventListener("touch",up) down:addEventListener("touch",down) print("grapple") end return true end grapple:addEventListener("touch",grapple)

Your code was perfect, and your explanations made my mistake easy to understand and fix. It also helped me understand some new coding strategies. Thank you so much for your help!!! 

I haven’t read your code with respect to fixing the bug, but more on that in a moment, though I did notice that you’re trying to make a grappling hook using a distance joint and setting it’s length. Though I could be wrong now, some time ago I tried this and found that adjusting the length of a distance joint does not work, you have to rely on the distance between the two objects when the joint is created. You might be better off using a couple of touch joints. I can provide that code if you like as I posted something like it to the Code Exchange a year ago. I have other physics code here, too:

http://springboardpillow.blogspot.co.uk/2012/04/sample-code.html

So, I took your code and added bodies until it worked. This is what I ended up with. Maybe it’s a bit different from yours, see what you think:

require("physics") physics.start() physics.setGravity(0,10) physics.setDrawMode("hybrid") local screen = display.newRect( display.actualContentWidth/2, display.actualContentHeight/2, display.actualContentWidth, display.actualContentHeight ) local grapple = display.newRect( 100, 100, 100, 100 ) local player = display.newCircle( 100, 300, 50 ) local up, down = display.newRect( 300, 100, 50, 50 ), display.newRect( 350, 100, 50, 50 ) screen.fill={1,1,1,.1} up.fill={0,1,0} down.fill={1,0,0} physics.addBody( player, "dynamic", { radius=50 } ) physics.addBody( grapple, "static" ) function grapple:touch (event) local rope = physics.newJoint("distance", player, grapple, player.x, player.y, event.x, event.y) local function raise (event) if event.phase == "began" and rope.length\>30 then moveup = true end if event.phase == "ended" or rope.length\<30 then moveup = false end local function shortenrope (event) if moveup == true then rope.length = rope.length - 2 end end Runtime:addEventListener("enterFrame", shortenrope) end up.touch = raise up:addEventListener("touch",raise) local function lower(event) if event.phase == "began" and rope.length\<400 then movedown = true end if event.phase == "ended" or rope.length\>400 then movedown = false end local function lengthenrope (event) if movedown == true then rope.length = rope.length + 2 end end Runtime:addEventListener("enterFrame", lengthenrope) end down.touch=lower down:addEventListener("touch",lower) --this is where the problem starts function screen:touch(event) if event.phase == "began" then if rope ~= nil then rope:removeSelf() rope = nil end end end screen:addEventListener("touch",screen) end grapple:addEventListener("touch",grapple)

Ok, I’ve found the bug and its quite simple really.

Place a print(“here”) immediately after this line:

function screen:touch(event)

Now run your test again. You will see that the screen’s touch function is called when you tap the grapple touch function. So, what is happening is the joint is being destroyed but a new distance joint is being created whenever you touch the grapple button.

Why? Well…

  • Your touch listener functions don’t have a return statement at the end, so the touch event drops through the buttons and hits the ‘screen’, where it causes the joint to be destroyed.

  • ALL of the code in the grapple:touch() function is executed when you touch the grapple object. This means that the ‘rope’ joint is created as a new object on each touch. You are adding lots of touch listeners to your buttons.

Btw, this is not necessary:

 up.touch = raise up:addEventListener("touch",raise)

You either assign the function to the object and make it a local listener OR you let the event listener call the function directly. Your code is trying to do both.

Local ‘table’ listener:

function up:touch(e) ... return true end up:addEventListener("touch",up) -- calling the touch listener function on the local table

Direct listener:

local function raise(e) ... return true end up:addEventListener("touch",raise) -- directly calling an arbitrary function on event

Below you can see the fixed code. I’ve modified it quite a lot, but I think you can see where it’s changed. I’ve moved the button listeners out of the grapple touch function and made the rope joint it’s own variable. In my own code, I would make the rope variable a property of the grapple object, but it’s personal preference. I’ve also moved the up/down addEventListener calls inside the grapple:touch() function, so that they are only active when there is a distance joint (I also made the buttons look disabled.)

Side note: It seems that my earlier comment is now wrong - the length property does in fact change the length of the distance joint. This is great news and I only wish it had worked like that three years ago!

-- grapple demo require("physics") physics.start() physics.setGravity(0,10) physics.setDrawMode("hybrid") local screen = display.newRect( display.actualContentWidth/2, display.actualContentHeight/2, display.actualContentWidth, display.actualContentHeight ) local grapple = display.newRect( 100, 100, 100, 100 ) local player = display.newCircle( 100, 300, 50 ) local up, down = display.newRect( 300, 100, 50, 50 ), display.newRect( 350, 100, 50, 50 ) local rope = nil screen.fill={1,1,1} up.fill={.5,.5,.5} down.fill={.5,.5,.5} physics.addBody( player, "dynamic", { radius=50 } ) physics.addBody( grapple, "static" ) function up:touch(event) print("up") if event.phase == "began" and rope.length\>30 then moveup = true end if event.phase == "ended" or rope.length\<30 then moveup = false end local function shortenrope (event) if moveup == true then rope.length = rope.length - 2 end end Runtime:addEventListener("enterFrame", shortenrope) return true end function down:touch(event) print("down") if event.phase == "began" and rope.length\<400 then movedown = true end if event.phase == "ended" or rope.length\>400 then movedown = false end local function lengthenrope (event) if movedown == true then rope.length = rope.length + 2 end end Runtime:addEventListener("enterFrame", lengthenrope) return true end --this is where the problem starts function screen:touch(event) print("screen") if event.phase == "began" then if rope ~= nil then rope:removeSelf() rope = nil up:removeEventListener("touch",up) down:removeEventListener("touch",down) up.fill={.5,.5,.5} down.fill={.5,.5,.5} end end return true end screen:addEventListener("touch",screen) function grapple:touch (event) if event.phase == "began" and rope == nil then rope = physics.newJoint("distance", player, grapple, player.x, player.y, event.x, event.y) up.fill={0,1,0} down.fill={1,0,0} up:addEventListener("touch",up) down:addEventListener("touch",down) print("grapple") end return true end grapple:addEventListener("touch",grapple)

Your code was perfect, and your explanations made my mistake easy to understand and fix. It also helped me understand some new coding strategies. Thank you so much for your help!!!