Scrolling and dragging --> BUG?

Hi

I have a event listener asigned to an object. This then triggers the startDrag function. I’m moving my character in the game with dragging. But as soon as I scroll or move the display, my dragging function gets all messed up. It doesn’t react as it should and the object(event.target) can’t be controled at all.

Did anyone else had this expirience? 

Is this a corona bug?

How can I solve this?

Can I temporarily disable the startDrag event listener for the time of scrolling? Or maybe restart it? 

All help would be greatly appreciated. 

Post some code - scrolling and dragging are pretty significant aspects of Corona, so I highly doubt there’s a bug with the framework, usually it’s the implementation.

I use this code for dragging physics bodies:

local function startDrag( event, params ) local body = event.target local phase = event.phase local stage = display.getCurrentStage() local direction = event.direction if "began" == phase then stage:setFocus( body, event.id ) body.isFocus = true event.target.bodyType = "dynamic" -- Create a temporary touch joint and store it in the object for later reference if params and params.center then -- drag the body from its center point body.tempJoint = physics.newJoint( "touch", body, body.x, body.y ) else -- drag the body from the point where it was touched body.tempJoint = physics.newJoint( "touch", body, event.x, event.y ) end --body.tempJoint.maxForce = 0.25\*body.tempJoint.maxForce -- Apply optional joint parameters if params then local maxForce, frequency, dampingRatio if params.maxForce then -- Internal default is (1000 \* mass), so set this fairly high if setting manually body.tempJoint.maxForce = params.maxForce end if params.frequency then -- This is the response speed of the elastic joint: higher numbers = less lag/bounce body.tempJoint.frequency = params.frequency end if params.dampingRatio then -- Possible values: 0 (no damping) to 1.0 (critical damping) body.tempJoint.dampingRatio = params.dampingRatio end end elseif body.isFocus then if "moved" == phase then -- Update the joint to track the touch body.tempJoint:setTarget( event.x, event.y ) --zoomScreen(event) elseif "ended" == phase or "cancelled" == phase then stage:setFocus( body, nil ) body.isFocus = false -- Remove the joint when the touch ends body.tempJoint:removeSelf() body.bodyType = "static" end end -- Stop further propagation of touch event return true end

And I drag just a circle, which has over it attached an invisible circle. This one operates as a sensor for dragging.

To move the screen I use this function, also pretty simple:

function runtimeListener(e) if button3.y \< -allElements.y + 300 then allElements.y = -(button3.y - 300) end end Runtime:addEventListener("enterFrame", runtimeListener)

I have also tryed to use a dragging function for non physics objects, and this one works OK. 

Haven’t studied your code but I would immediately believe it’s probably to do with the issue of physics and moving display groups - it’s a tricky subject that catches most people out. Try researching that - but bottom line is don’t move display groups if using physics.

P.S What is allElements - I’m assuming a display group?

yes, its a group where I have all the display elements joined.

I have also stripped the code to the minimum. Just drag the circle up, let it go and drag again. Then it starts to get strange.

local physics = require( "physics" ) physics.start() physics.setContinuous( false ) display.setStatusBar( display.HiddenStatusBar ) physics.setScale( 60 ) physics.setDrawMode( "hybrid" ) local height = display.contentHeight local width = display.contentWidth local allElements = display.newGroup() local texsGroup = display.newGroup() local backGround = display.newRect(0,0-height,width,2\*height) backGround:setFillColor(91,91,91) backGround:toBack() local wallBottom = display.newRect(texsGroup, 0,height-20,width,20) physics.addBody(wallBottom, "static", { density=5, friction=0.5, bounce=0.3 } ) local tex = {} local numberRips = 60 local texSize = { -- w: texwidth, h: texheight, s: strength required {w=30, h=20, s=1}, {w=20, h=10, s=1.5}, {w=10, h=10, s=2}, } local r local lim = display.newGroup() local function createRips() local originX = 0 local originY = height -75 for i=0,numberRips do r = math.random(3) local x = originX + math.random(width) local y = originY - math.random(2\*height) tex[i] = display.newRect(lim, x, y, texSize[r].w, texSize[r].h) tex[i].status = "active" tex[i].size = texSize[r].s if (r == 1) then tex[i]:setFillColor(51,255,0) elseif (r == 2) then tex[i]:setFillColor(255,51,51) elseif (r == 3) then tex[i]:setFillColor(51,51,255) end end end createRips() local w, h, r = width/2, height - 265, 12 local L = display.newCircle(w-115,h+29,r) local buttonRadius = 35 local button3 = display.newCircle((L.x),(L.y),buttonRadius) button3.myName = "L" allElements:insert(button3) allElements:insert(lim) allElements:insert(L) local krog = display.newCircle(lim,w+117,h+29,40) local d, f, b = 15, 1, 0.15 physics.addBody(L, "dynamic", { density=d, friction=f, bounce=b, radius=r } ) button3.isVisible = false button3.isHitTestable = true physics.addBody( button3, "static", { density=1, radius=buttonRadius } ) local function addFrictionJoint(a, b, posX, posY, lowerAngle, upperAngle, mT) local j = physics.newJoint ( "pivot", a, b, posX, posY, rFrom, rTo) j.isLimitEnabled = true j:setRotationLimits (lowerAngle, upperAngle) return j end -- JOINTS addFrictionJoint( button3, L, L.x, L.y, 0, 0 ) local function startDrag( event, params ) local body = event.target local phase = event.phase local stage = display.getCurrentStage() local direction = event.direction if "began" == phase then stage:setFocus( body, event.id ) body.isFocus = true event.target.bodyType = "dynamic" -- Create a temporary touch joint and store it in the object for later reference if params and params.center then -- drag the body from its center point body.tempJoint = physics.newJoint( "touch", body, body.x, body.y ) else -- drag the body from the point where it was touched body.tempJoint = physics.newJoint( "touch", body, event.x, event.y ) end --body.tempJoint.maxForce = 0.25\*body.tempJoint.maxForce -- Apply optional joint parameters if params then local maxForce, frequency, dampingRatio if params.maxForce then -- Internal default is (1000 \* mass), so set this fairly high if setting manually body.tempJoint.maxForce = params.maxForce end if params.frequency then -- This is the response speed of the elastic joint: higher numbers = less lag/bounce body.tempJoint.frequency = params.frequency end if params.dampingRatio then -- Possible values: 0 (no damping) to 1.0 (critical damping) body.tempJoint.dampingRatio = params.dampingRatio end end elseif body.isFocus then if "moved" == phase then -- Update the joint to track the touch body.tempJoint:setTarget( event.x, event.y ) elseif "ended" == phase or "cancelled" == phase then stage:setFocus( body, nil ) body.isFocus = false -- Remove the joint when the touch ends body.tempJoint:removeSelf() body.bodyType = "static" end end -- Stop further propagation of touch event return true end function moveCamera(e) if button3.y \< -lim.y + 300 then allElements.y = -(button3.y - 300) end end Runtime:addEventListener("enterFrame", moveCamera) button3:addEventListener( "touch", startDrag )

Just wondering… Do you want the dragged character to always stay in the centre of the screen or would it be better to have a little drift? The former is pretty easy, but the latter can be a little tricky to fine tune.

Well, you know what? I went and wrote my own version of your code off the top of my head - not using yours as a reference - just to see what I could get. This little script took an hour to build and I think I pretty much have the same result as you. I’ll keep working on this and let you know if I succeed, if you can let us know if you do too!

local physics = require( "physics" ) physics.start() physics.setGravity(0,0) physics.setDrawMode( "hybrid" ) local sWidth, sHeight = display.contentWidth, display.contentHeight local stage = display.getCurrentStage() local arena = display.newGroup() local border = display.newRect( arena, 0, 0, sWidth\*2, sHeight\*2 ) border:setFillColor(0,50,0,0) border.strokeWidth = 10 border:setStrokeColor(255,255,255) -- random fill for i=1, 10 do &nbsp;&nbsp;&nbsp;&nbsp;display.newCircle( arena, math.random(50, sWidth\*2-50), math.random( 50, sHeight\*2-50 ), math.random(1,40) ):setFillColor(0,255,0,50) end local player = display.newCircle( arena, 250, 250, 25 ) physics.addBody( player ) local touchBorder = 150 local xOffset, yOffset = 0, 0 local function calcTouchOffset( e ) &nbsp;&nbsp;&nbsp;&nbsp;local x, y = 0, 0 &nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;if (e.x \< touchBorder) then &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;x = e.x - touchBorder &nbsp;&nbsp;&nbsp;&nbsp;elseif (e.x \> sWidth-touchBorder) then &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;x = e.x - (sWidth-touchBorder) &nbsp;&nbsp;&nbsp;&nbsp;end &nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;if (e.y \< touchBorder) then &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;y = e.y - touchBorder &nbsp;&nbsp;&nbsp;&nbsp;elseif (e.y \> sHeight-touchBorder) then &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;y = e.y - (sHeight-touchBorder) &nbsp;&nbsp;&nbsp;&nbsp;end &nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;return x, y end function drag(e) &nbsp;&nbsp;&nbsp;&nbsp;if (e.phase == "began") then &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;e.target.hasFocus = true &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;local x, y = arena:contentToLocal( e.x, e.y ) &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;e.target.touchjoint = physics.newJoint( "touch", e.target, x, y ) &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;stage:setFocus( e.target ) &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;xOffset, yOffset = 0, 0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return true &nbsp;&nbsp;&nbsp;&nbsp;elseif (e.target.hasFocus) then &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (e.phase == "moved") then &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;e.target.touchjoint:setTarget( e.x+arena.x, e.y+arena.y ) &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;xOffset, yOffset = calcTouchOffset( e ) &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;else &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;e.target.hasFocus = false &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;e.target.touchjoint:removeSelf() &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;e.target.touchjoint = nil &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;stage:setFocus( nil ) &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;xOffset, yOffset = 0, 0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;timer.performWithDelay( 150, function() &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;e.target:setLinearVelocity( 0, 0 ) &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;e.target.angularVelocity = 0 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end, 1 ) &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return true &nbsp;&nbsp;&nbsp;&nbsp;end &nbsp;&nbsp;&nbsp;&nbsp;xOffset, yOffset = 0, 0 &nbsp;&nbsp;&nbsp;&nbsp;return false end player:addEventListener("touch",drag) function arena:update() &nbsp;&nbsp;&nbsp;&nbsp;arena.x, arena.y = arena.x - xOffset, arena.y - yOffset &nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;if (arena.x \> 0) then &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;arena.x = 0 &nbsp;&nbsp;&nbsp;&nbsp;elseif (arena.x \< -sWidth) then &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;arena.x = -sWidth &nbsp;&nbsp;&nbsp;&nbsp;end &nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;if (arena.y \> 0) then &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;arena.y = 0 &nbsp;&nbsp;&nbsp;&nbsp;elseif (arena.y \< -sHeight) then &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;arena.y = -sHeight &nbsp;&nbsp;&nbsp;&nbsp;end end function enterFrame() &nbsp;&nbsp;&nbsp;&nbsp;arena:update() end Runtime:addEventListener("enterFrame", enterFrame)

Great, thank you very much. I’m a beginner here and this problem is way over my head. Completly stuck here. :frowning:

I have also tryed to scroll just the non-physics objects, which work fine. But then the physics object won’t reat to them.

I have also tryed to move the camera with this http://developer.coronalabs.com/code/perspective#comment-142501. But got the same results.

@horacebury, I ran your code and edited just two lines (marked below), and it seems to achieve the effect you guys are going for:

[lua]

local physics = require( “physics” )

physics.start()

physics.setGravity(0,0)

physics.setDrawMode( “hybrid” )

local sWidth, sHeight = display.contentWidth, display.contentHeight

local stage = display.getCurrentStage()

local arena = display.newGroup()

local border = display.newRect( arena, 0, 0, sWidth*2, sHeight*2 )

border:setFillColor(0,50,0,0)

border.strokeWidth = 10

border:setStrokeColor(255,255,255)

– random fill

for i=1, 10 do

    display.newCircle( arena, math.random(50, sWidth*2-50), math.random( 50, sHeight*2-50 ), math.random(1,40) ):setFillColor(0,255,0,50)

end

local player = display.newCircle( arena, 250, 250, 25 )

physics.addBody( player )

local touchBorder = 150

local xOffset, yOffset = 0, 0

local function calcTouchOffset( e )

    local x, y = 0, 0

    

    if (e.x < touchBorder) then

        x = e.x - touchBorder

    elseif (e.x > sWidth-touchBorder) then

        x = e.x - (sWidth-touchBorder)

    end

    

    if (e.y < touchBorder) then

        y = e.y - touchBorder

    elseif (e.y > sHeight-touchBorder) then

        y = e.y - (sHeight-touchBorder)

    end

    

    return x, y

end

function drag(e)

    if (e.phase == “began”) then

        e.target.hasFocus = true

        local x, y = arena:contentToLocal( e.x, e.y )

        e.target.touchjoint = physics.newJoint( “touch”, e.target, x, y )

        stage:setFocus( e.target )

        xOffset, yOffset = 0, 0

        return true

    elseif (e.target.hasFocus) then

        if (e.phase == “moved”) then

            local x,y = arena:contentToLocal(e.x, e.y)     – This line is changed

            e.target.touchjoint:setTarget( x, y )               – This line is changed

            xOffset, yOffset = calcTouchOffset( e )

        else

            e.target.hasFocus = false

            e.target.touchjoint:removeSelf()

            e.target.touchjoint = nil

            stage:setFocus( nil )

            xOffset, yOffset = 0, 0

            timer.performWithDelay( 150, function()

                e.target:setLinearVelocity( 0, 0 )

                e.target.angularVelocity = 0

            end, 1 )

        end

        return true

    end

    xOffset, yOffset = 0, 0

    return false

end

player:addEventListener(“touch”,drag)

function arena:update()

    arena.x, arena.y = arena.x - xOffset, arena.y - yOffset

    

    if (arena.x > 0) then

        arena.x = 0

    elseif (arena.x < -sWidth) then

        arena.x = -sWidth

    end

    

    if (arena.y > 0) then

        arena.y = 0

    elseif (arena.y < -sHeight) then

        arena.y = -sHeight

    end

end

function enterFrame()

    arena:update()

end

Runtime:addEventListener(“enterFrame”, enterFrame)

[/lua]

Great, its working. Thank you, aukStudios. I will try this solution in my game and will let you know. Just have some questions, since I’m a begginer at programing. 

  1. Why are those two changed lines so important?

  2. Why you use hasFocus instead of setFocus?

  3. What does contentToLocal do? As I have read the documentation, you get with this method the distance from the touch event towards to groups center. Is this true?

Great, I’m glad it’s working!  As for your questions:

  1. When the user moves their touch, these two lines control how the coordinates of the touch are interpreted in moving the arena and the joint connecting the touch to the player.  Since that was the aspect of the original version that wasn’t working, it was a clue that maybe it was these two lines that needed to be fixed.

  2. The code is actually using both.  The setFocus() method tells Corona to set the focus on a particular object.  However, there’s no API to ask Corona which object has the focus.  So, when you set the focus on an object, the strategy is to also add a custom property to that object, hasFocus, which you can refer to later.  This is a common strategy in multitouch applications, although it may not be necessary here.

  3. Basically, contentToLocal converts x,y coordinates on the screen into “local” coordinates for a particular display group.  Each display group has its own local coordinate system, and contentToLocal is a method that helps translate from the screen to it.  For example, let’s say you had the screen coordinates 100,200.  If you had a display group called group that was centered at 100,200, then group:contentToLocal(100,200) would (I think) return 0,0.

  • Andrew

Super, its working also in my game now. Thank you also for answering my additional questions. It was realy kind of you…

Tomaz

Post some code - scrolling and dragging are pretty significant aspects of Corona, so I highly doubt there’s a bug with the framework, usually it’s the implementation.

I use this code for dragging physics bodies:

local function startDrag( event, params ) local body = event.target local phase = event.phase local stage = display.getCurrentStage() local direction = event.direction if "began" == phase then stage:setFocus( body, event.id ) body.isFocus = true event.target.bodyType = "dynamic" -- Create a temporary touch joint and store it in the object for later reference if params and params.center then -- drag the body from its center point body.tempJoint = physics.newJoint( "touch", body, body.x, body.y ) else -- drag the body from the point where it was touched body.tempJoint = physics.newJoint( "touch", body, event.x, event.y ) end --body.tempJoint.maxForce = 0.25\*body.tempJoint.maxForce -- Apply optional joint parameters if params then local maxForce, frequency, dampingRatio if params.maxForce then -- Internal default is (1000 \* mass), so set this fairly high if setting manually body.tempJoint.maxForce = params.maxForce end if params.frequency then -- This is the response speed of the elastic joint: higher numbers = less lag/bounce body.tempJoint.frequency = params.frequency end if params.dampingRatio then -- Possible values: 0 (no damping) to 1.0 (critical damping) body.tempJoint.dampingRatio = params.dampingRatio end end elseif body.isFocus then if "moved" == phase then -- Update the joint to track the touch body.tempJoint:setTarget( event.x, event.y ) --zoomScreen(event) elseif "ended" == phase or "cancelled" == phase then stage:setFocus( body, nil ) body.isFocus = false -- Remove the joint when the touch ends body.tempJoint:removeSelf() body.bodyType = "static" end end -- Stop further propagation of touch event return true end

And I drag just a circle, which has over it attached an invisible circle. This one operates as a sensor for dragging.

To move the screen I use this function, also pretty simple:

function runtimeListener(e) if button3.y \< -allElements.y + 300 then allElements.y = -(button3.y - 300) end end Runtime:addEventListener("enterFrame", runtimeListener)

I have also tryed to use a dragging function for non physics objects, and this one works OK. 

Haven’t studied your code but I would immediately believe it’s probably to do with the issue of physics and moving display groups - it’s a tricky subject that catches most people out. Try researching that - but bottom line is don’t move display groups if using physics.

P.S What is allElements - I’m assuming a display group?