Hi all,
I’m in real need of help because I’ve been going around in circles with this code for the past 48 hours.
I have a game board in my app which the user can zoom in on. At first it only supported double tapping to zoom, but after watching many people repeatedly try to pinch zoom I figured I should implement multitouch to support the pinch gesture. Shouldn’t be too hard right?
WRONG.
I decided to start with the Pinch Zoom XL sample found here and tweaked it to my needs. Mainly, I don’t want/need multitouch to be active all the time, and I only need the pinch gesture to affect the game board. No other objects should be “multitouched”. Also, there is no need to recognize more than two touches.
My code works for the most part. You can pinch open to zoom in and pinch closed to zoom out. The problem comes when there are a lot of touches on the screen (moving around, tapping, trying to touch other objects while still touching the game board).
I haven’t been able to pinpoint the cause, but sometimes the game board will lose focus and the multitouch will stay active. Once that happens the board seems to no longer register the touch events and won’t respond to pinching or dragging. But since the multitouch stays active I am able to touch multiple other objects in the scene simultaneously and move them around. That is not something that I want.
Without further ado, here is my code. Are there any Corona Masters out there that can help me?
function multitouch( event, gameBoard, params) local body = event.target --make sure the touch originates within the bounds of the gameboard if(event.yStart \>= gameBoard.boardZone.top and event.yStart \<= gameBoard.boardZone.bottom) then local phase = event.phase local previousTouches = body.previousTouches body.zoomState = nil --I only want multitouch active when the gameboard is being touched if(not body.multiTouchActivated and body.reactivate and (body.numPreviousTouches == nil or body.numPreviousTouches \<= 2)) then system.activate( "multitouch" ) body.multiTouchActivated = true end local numTotalTouches = 1 if previousTouches then -- add in total from previousTouches, subtract one if event is already in the array numTotalTouches = numTotalTouches + body.numPreviousTouches if previousTouches[event.id] then numTotalTouches = numTotalTouches - 1 end end --only want to register at most two touches if("began" == phase and (body.numPreviousTouches == nil or body.numPreviousTouches == 1)) then -- Very first "began" event if not body.isFocus then display.getCurrentStage():setFocus( body ) body.isFocus = true system.activate( "multitouch" ) body.multiTouchActivated = true previousTouches = {} body.previousTouches = previousTouches body.numPreviousTouches = 0 body.firstTouch = event --second touch elseif not body.distance then local dx,dy local cx,cy if previousTouches and numTotalTouches == 2 then dx,dy = calculateDelta( previousTouches, event ) cx,cy = calculateCenter( previousTouches, event ) end -- initialize to distance between two touches if dx and dy then local d = math.sqrt( dx\*dx + dy\*dy ) if d \> 0 then body.distance = d end end end if not previousTouches[event.id] then body.numPreviousTouches = body.numPreviousTouches + 1 end previousTouches[event.id] = event --if this is the first touch then create a temp joint to allow dragging the game board if(body.firstTouch.id == event.id and body.numPreviousTouches == 1 and body.zoomed and not body.tempJoint) then if params then body.tempJoint = physics.newJoint( "touch", body, event.x, event.y ) body.tempJoint.maxForce = params.maxForce body.tempJoint.frequency = params.frequency body.tempJoint.dampingRatio = params.dampingRatio end end elseif body.isFocus then if "moved" == phase then if body.distance then local dx,dy local cx,cy if previousTouches and body.numPreviousTouches == 2 then dx,dy = calculateDelta( previousTouches, event ) cx,cy = calculateCenter( previousTouches, event ) end --THIS IS WHERE I NEED HELP!! if dx and dy then local newDistance = math.sqrt( dx\*dx + dy\*dy ) body.zoomState = newDistance \< body.distance and "small" or "big" if(body.numPreviousTouches == 2 and body.zoomState ) then if((body.zoomed and body.zoomState == "small") or (not body.zoomed and body.zoomState == "big")) then --deactivate multitouch because the zoom will occur --and multitouch is no longer necessary system.deactivate( "multitouch" ) body.multiTouchActivated = false --stop any dragging motion if the board is being moved body:setLinearVelocity(0,0) zoomBoard(body, {x=cx,y=cy}, gameBoard, body.zoomState) if(body.tempJoint) then body.tempJoint:removeSelf() body.tempJoint = nil end body.distance = nil --set this to false so that multitouch does not get reactivated if --the user keeps their fingers on the screen after the zoom has --occured body.reactivate = false end end end else -- dont move unless this is the first touch id. if(event.id == body.firstTouch.id and body.numPreviousTouches == 1) then if(body.zoomed and body.tempJoint) then -- Update the joint to track the touch body.tempJoint:setTarget( event.x, event.y ) end end end elseif "ended" == phase or "cancelled" == phase then if previousTouches[event.id] then body.numPreviousTouches = body.numPreviousTouches - 1 previousTouches[event.id] = nil print("Removing event") end if body.numPreviousTouches == 1 then -- must be at least 2 touches remaining to pinch/zoom body.distance = nil if(event.id == body.firstTouch.id and body.tempJoint) then body.tempJoint:removeSelf() body.tempJoint = nil end system.deactivate( "multitouch" ) body.multiTouchActivated = false print("One touch remaining") end if body.numPreviousTouches == 0 then --no touches left so the board should lose focus display.getCurrentStage():setFocus( nil ) body.isFocus = nil system.deactivate( "multitouch" ) body.multiTouchActivated = false --set this to true to allow future multitouch on the game board --once the user removes all touches body.reactivate = true if(event.id == body.firstTouch.id and body.tempJoint) then body.tempJoint:removeSelf() body.tempJoint = nil end body.distance = nil body.previousTouches = nil body.numPreviousTouches = nil print("No touches remaining") end end end end return true end function zoomBoard(body, event, gameBoard, zoomState) local dx = event.x - body.x local dy = event.y - body.y if(zoomState == "big") then transition.cancel(body) body:setLinearVelocity(0,0) body.bodyType = "static" body.zoomed = true local zoomedX = event.x - dx\*body.scaleMax local zoomedY = event.y - dy\*body.scaleMax transition.to( body, { time=200, transition=easing.linear, xScale=body.scaleMax, yScale=body.scaleMax, x=zoomedX, y=zoomedY} ) timer.performWithDelay( 250, function () body.bodyType = "dynamic" end ) elseif(zoomState == "small" ) then transition.cancel(body) body:setLinearVelocity(0,0) body.bodyType = "static" body.zoomed = false transition.to( body, { time=200, transition=easing.linear, xScale=1, yScale=1, x=body.xReset, y=body.yReset} ) end end
Thanks for taking the time to look at it. Also, if you have any suggestions on how to clean up the multitouch code I’m all ears. I feel like there is a lot of unnecessary code in there but I’m so frazzled right now that I can’t think straight :wacko: