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)