Can't figure out how to manually stop the dragging of an object

In my game, the player users their finger to drag the character around. I want it so that when the player is hit by an enemy, they are no longer controllable and fall off the stage.

I can’t seem to get it to work however. When I remove the event listener from the player object, the player just stays still, instead of being affected by gravity forces (I have tried setting the player to dynamic but it did not help).

Alternatively, I don’t remove the event listener, I use the “isDragAllowed” flag to detect when the collision happens (it’s set to false when player collides with enemy). This produces the desired effect (player object is released from the drag and falls off the stage due to gravity) if the user is dragging the player around, but doesn’t work if the user is holding the player still (player object stays still, until user tries to move again).

How do I make it work so that in the moment the player is hit by an enemy, the touch/drag listener on him stops?

This is my drag handler:

 isDragAllowed = true local function handleDrag( event ) local t = event.target local phase = event.phase if "began" == phase then isDragAllowed = true display.getCurrentStage():setFocus( t ) --t.isFocus = true -- Store initial position t.x0 = event.x - t.x t.y0 = event.y - t.y -- Make body type temporarily "kinematic" (to avoid gravitional forces) event.target.bodyType = "kinematic" event.target.isSensor = true -- Stop current motion, if any event.target:setLinearVelocity( 0, 0 ) event.target.angularVelocity = 0 end --elseif t.isFocus then if "moved" == phase then t.x = event.x - t.x0 t.y = event.y - t.y0 end if "ended" == phase or "cancelled" == phase or isDragAllowed == false then display.getCurrentStage():setFocus( nil ) --t.isFocus = false t.bodyType = "dynamic" t.isSensor = false isDragAllowed = true end --end -- Stop further propagation of touch event! return true end

Hi @exclaimteam,

I would suggest simply dispatching a “pseudo” event to the drag handler with a phase of “ended”, using the “object:dispatchEvent()” API. This API is incredibly useful in certain cases, as it allows you to optimize code and dispatch events to listeners which may not have (physically) occurred. For example, you could dispatch an “ended” event to a touch listener even if the user didn’t actually lift their finger off the object, effectively using the same function instead of some boolean flag(s) or whatever.

https://docs.coronalabs.com/api/type/EventListener/dispatchEvent.html

So basically, something like this (let’s assume your object is named “player”:

[lua]

player:dispatchEvent( { name=“touch”, target=player, phase=“ended” } )

[/lua]
 

As for the player not falling when the drag ends, I’m not sure why. You may want to simply keep the body type as “dynamic” but set its gravityScale property to 0 while dragging it around, then reset it to 1 after the drag ends.

Hope this helps,

Brent

Hrm okay I tried doing what you said, with the dispatchEvent what not. It’s still not working.

Here’s what I have:

My player object is called handle, and this is its dispatch, it’s called at the beginning of a physics collision listener:

           handle = handleTable[handleNum-1]

handle:dispatchEvent( { name=“touch”, target=handle, phase=“ended” } )

The handle is spawned elsewhere, and given the event listener as so:

 handle:addEventListener( "touch", handleDrag )

Again, this is the handleDrag function:

 local function handleDrag( event ) local t = event.target local phase = event.phase if "began" == phase then display.getCurrentStage():setFocus( t ) t.isFocus = true -- Store initial position t.x0 = event.x - t.x t.y0 = event.y - t.y -- Make body type temporarily "kinematic" (to avoid gravitional forces) event.target.bodyType = "kinematic" --event.target.gravityScale = 1 event.target.isSensor = true -- Stop current motion, if any event.target:setLinearVelocity( 0, 0 ) event.target.angularVelocity = 0 end --elseif t.isFocus then if "moved" == phase then t.x = event.x - t.x0 t.y = event.y - t.y0 end if "ended" == phase or "cancelled" == phase then if isDragAllowed then display.getCurrentStage():setFocus( nil ) t.isFocus = false t.bodyType = "dynamic" --event.target.gravityScale = -1 t.isSensor = false else print("THIS PHASE IS NOW ENDING") display.getCurrentStage():setFocus( nil ) handleTable[handleNum-1].isFocus = false handleTable[handleNum-1].bodyType = "dynamic" --event.target.gravityScale = -1 handleTable[handleNum-1].isSensor = false end end --end -- Stop further propagation of touch event! return true end

The handleDrag function should print “THIS PHASE IS NOW ENDING” when the drag has ended. And it does, so the dispatchEvent is reaching that point, but for some reason the handle object remains draggable, and does not exhibit proper end-of-drag behavior.

(I’ve tried it with both “t” and “handleTable[handleNum-1]” as the object, it does the same thing, aka nothing)

Hi again,

This could be because you’re not handling the change of physical behavior after a short timer. See the yellow box in the following guide as to which calls/properties must be delayed for at least one time step. “.bodyType” is one of these.

https://docs.coronalabs.com/guide/physics/collisionDetection/index.html

Brent

Alright so I added this:

 timer.performWithDelay( 100, function () t.bodyType = "dynamic" end)

In the place of my original t.bodyType setting and now it works perfectly.

Thanks so much.

Good to hear! You can actually go lower on the timer delay (10 milliseconds should work… it basically just needs to delay the physical changes until the next time step).

Brent

Hi @exclaimteam,

I would suggest simply dispatching a “pseudo” event to the drag handler with a phase of “ended”, using the “object:dispatchEvent()” API. This API is incredibly useful in certain cases, as it allows you to optimize code and dispatch events to listeners which may not have (physically) occurred. For example, you could dispatch an “ended” event to a touch listener even if the user didn’t actually lift their finger off the object, effectively using the same function instead of some boolean flag(s) or whatever.

https://docs.coronalabs.com/api/type/EventListener/dispatchEvent.html

So basically, something like this (let’s assume your object is named “player”:

[lua]

player:dispatchEvent( { name=“touch”, target=player, phase=“ended” } )

[/lua]
 

As for the player not falling when the drag ends, I’m not sure why. You may want to simply keep the body type as “dynamic” but set its gravityScale property to 0 while dragging it around, then reset it to 1 after the drag ends.

Hope this helps,

Brent

Hrm okay I tried doing what you said, with the dispatchEvent what not. It’s still not working.

Here’s what I have:

My player object is called handle, and this is its dispatch, it’s called at the beginning of a physics collision listener:

           handle = handleTable[handleNum-1]

handle:dispatchEvent( { name=“touch”, target=handle, phase=“ended” } )

The handle is spawned elsewhere, and given the event listener as so:

 handle:addEventListener( "touch", handleDrag )

Again, this is the handleDrag function:

 local function handleDrag( event ) local t = event.target local phase = event.phase if "began" == phase then display.getCurrentStage():setFocus( t ) t.isFocus = true -- Store initial position t.x0 = event.x - t.x t.y0 = event.y - t.y -- Make body type temporarily "kinematic" (to avoid gravitional forces) event.target.bodyType = "kinematic" --event.target.gravityScale = 1 event.target.isSensor = true -- Stop current motion, if any event.target:setLinearVelocity( 0, 0 ) event.target.angularVelocity = 0 end --elseif t.isFocus then if "moved" == phase then t.x = event.x - t.x0 t.y = event.y - t.y0 end if "ended" == phase or "cancelled" == phase then if isDragAllowed then display.getCurrentStage():setFocus( nil ) t.isFocus = false t.bodyType = "dynamic" --event.target.gravityScale = -1 t.isSensor = false else print("THIS PHASE IS NOW ENDING") display.getCurrentStage():setFocus( nil ) handleTable[handleNum-1].isFocus = false handleTable[handleNum-1].bodyType = "dynamic" --event.target.gravityScale = -1 handleTable[handleNum-1].isSensor = false end end --end -- Stop further propagation of touch event! return true end

The handleDrag function should print “THIS PHASE IS NOW ENDING” when the drag has ended. And it does, so the dispatchEvent is reaching that point, but for some reason the handle object remains draggable, and does not exhibit proper end-of-drag behavior.

(I’ve tried it with both “t” and “handleTable[handleNum-1]” as the object, it does the same thing, aka nothing)

Hi again,

This could be because you’re not handling the change of physical behavior after a short timer. See the yellow box in the following guide as to which calls/properties must be delayed for at least one time step. “.bodyType” is one of these.

https://docs.coronalabs.com/guide/physics/collisionDetection/index.html

Brent

Alright so I added this:

 timer.performWithDelay( 100, function () t.bodyType = "dynamic" end)

In the place of my original t.bodyType setting and now it works perfectly.

Thanks so much.

Good to hear! You can actually go lower on the timer delay (10 milliseconds should work… it basically just needs to delay the physical changes until the next time step).

Brent