How can I create a touch area around a golf ball object?

I’m making a mini golf game in the theme of a blow fish, but I’m currently stuck on developing a firing mechanism of the golf ball object. I want to place a large circular touch area around the golf ball such that the player doesn’t need to be very precise in directly touching the golf ball. But, here’s my dilemma. I am able to adjust the force applied to the ball based on how far the touch is from the golf object and if the touch is released within the touch area, the golf ball fires. But, if it’s done outside of the circular area, then it doesn’t get released. How can I fix this?

local physics = require('physics') local math = require('math') local w, h = display.contentWidth, display.contentHeight local cX, cY = display.contentCenterX, display.contentCenterY physics.start() local blowfishGroup = display.newGroup() local blowfishGroup = display.newGroup() -- Create the blowfish local rad = 40 local blowfish = display.newCircle(blowfishGroup, 300, 50, rad) blowfish:setFillColor(.8,.3,0) -- Bloat fish force is set by a player moving the finger away from the canon blowfish.force = 0 blowfish.arrowLength = 0 physics.addBody(blowfish, 'dynamic', {radius = rad}) -- Minimum and maximum amount of force indicator local arrowLengthMin, arrowLenghtMax = 25, 200 -- Indicates force value local forceArrow = display.newRect(blowfishGroup, blowfish.x, blowfish.y , 15, 45) forceArrow.isVisible = false forceArrow.anchorX = 0.5; forceArrow.anchorY = 1 -- Touch area is larger than blowfish image so plyaer does not need to be very accurate with the fingers local touchMax = 300 local touchArea = display.newCircle(blowfishGroup, blowfish.x, blowfish.y, touchMax) touchArea.isVisible = true touchArea.isHitTestable = true touchArea:addEventListener('touch', blowfish) local touchAreaVis = display.newCircle(blowfishGroup, blowfish.x, blowfish.y, 0) touchAreaVis.isVisible = false touchAreaVis.isHitTestable = true touchAreaVis:setFillColor(1,0,0,.2) function blowfish:setForce(radius, rotation) self.rotation = math.deg(rotation) forceArrow.rotation = 90 - self.rotation self.forceX, self.forceY = radius \* .5 \* math.cos(rotation), radius \* .5 \* -math.sin(rotation) return self.rotation end function blowfish:engageforce() self:applyForce(self.forceX, self.forceY, self.x, self.y) self.linearDamping = 0.85 --self.angularDamping = 0.5 end function blowfish:enterFrame() local vx, vy = blowfish:getLinearVelocity() if vx \< 0.1 and vy \< 0.1 then touchArea.x, touchArea.y = blowfish.x, blowfish.y touchAreaVis.x, touchAreaVis.y = blowfish.x, blowfish.y forceArrow.x, forceArrow.y = blowfish.x, blowfish.y touchArea:addEventListener('touch',blowfish) end end Runtime:addEventListener('enterFrame', blowfish) function blowfish:touch(event) if event.phase == 'began' then -- Arrow appears and force controlled by user self.isFocused = true forceArrow.isVisible = true touchAreaVis.isVisible = true elseif event.phase == 'moved' then local touchX, touchY = event.x - self.x, self.y - event.y local rotation = math.atan2( touchY , touchX ) local radius = math.sqrt( touchX ^ 2 + touchY ^ 2) touchAreaVis.path.radius = radius if radius \< touchMax then self:setForce(radius, rotation) else self:setForce(30,rotation) end elseif event.phase == 'ended' then -- Releases the blowfish based on force display.getCurrentStage():setFocus(self,nil) self.isFocused = false forceArrow.isVisible = false touchArea:removeEventListener('touch', blowfish) self:engageforce() end return true end

My first thought was that you need to add an object to the golf ball after it’s turned into a physics object - you just make the added object much larger than the ball and then added the touch listener.

Having read your post a second time, I realise that your touch listener is probably not handling the touch events properly. The main problem is that you’re not using display.currentStage:setFocus() to tell Corona that your touch event needs to be directed to your listener object. Once you’ve done that you should set a boolean on the touch object to filter out all non-began, non-other objects in the touch listener.

It sounds a lot harder than it is, but here’s the simple form:

 function golfball:touch(e) if (e.phase == "began") then e.target.hasFocus = true display.currentStage:setFocus( e.target ) return true elseif (e.target.hasFocus) then if (e.phase == "moved") then else e.target.hasFocus = nil display.currentStage:setFocus( nil ) end return true end return false end golfball:addEventListener( "touch", golfball )

Thank you for the reply! This works to some extent, but now the object isn’t moving when released. In order for me to understand how to fix this, I feel like I need some clarification. Exactly what does the event.target refers to in this case? Is it the touchAreaVis display object, the touchArea, the blow fish, or the background? Also, why is it returned false instead of true in the last part?

I look forward to hearing from you.

In the touch listener, the event.target refers to the object which is listening for touches - whatever object you called :addEventListener( “touch”, <func> ) on.

In your case, it is the Runtime object. Having said that, I would not do that - I would attach to the blowfish object, instead of Runtime.

My code (which was written rather hastily as I left the house this morning) is not 100% compatible with yours as it assumes the listener has been attached to an object rather than the environment in general. To make it work, you can replace event.target with blowfish and it should be fine.

I think you need to read the event guide: https://docs.coronalabs.com/daily/guide/events/touchMultitouch/index.html

Basically, when you start handling a touch event you want to tell the system that you’re going to be handling the particular event for this object until it ends. This is why we tell the stage to direct all events with the given id to the listening object.

We also say that we are going to filter out any events which get received if the object has not accepted a began phase, because otherwise it could be a touch which is sweeping in from somewhere else and isn’t relevant. We only want touch events which begin on our listening object - not touches which started on something else. This is handled by the .hasFocus value.

Returning true from any event listener tells the system not to propagate the event to any other listening objects because we handled it. Returning false says that we didn’t and to continue trying to send the event to other objects, until something does handle it.

e.g.: If we are receiving a began phase on our object we return true because we want to start a drag operation. If we receive a moved event but we haven’t received a began, hasFocus will be false so we don’t care, then we return false - it’s not our touch.

Can you just add transparency around the golf ball  making it bigger? Your physics body would be the size and shape of the golf ball. You would need to turn .isHitTestable = true on so that you could interact with the transparent area.

My first thought was that you need to add an object to the golf ball after it’s turned into a physics object - you just make the added object much larger than the ball and then added the touch listener.

Having read your post a second time, I realise that your touch listener is probably not handling the touch events properly. The main problem is that you’re not using display.currentStage:setFocus() to tell Corona that your touch event needs to be directed to your listener object. Once you’ve done that you should set a boolean on the touch object to filter out all non-began, non-other objects in the touch listener.

It sounds a lot harder than it is, but here’s the simple form:

 function golfball:touch(e) if (e.phase == "began") then e.target.hasFocus = true display.currentStage:setFocus( e.target ) return true elseif (e.target.hasFocus) then if (e.phase == "moved") then else e.target.hasFocus = nil display.currentStage:setFocus( nil ) end return true end return false end golfball:addEventListener( "touch", golfball )

Thank you for the reply! This works to some extent, but now the object isn’t moving when released. In order for me to understand how to fix this, I feel like I need some clarification. Exactly what does the event.target refers to in this case? Is it the touchAreaVis display object, the touchArea, the blow fish, or the background? Also, why is it returned false instead of true in the last part?

I look forward to hearing from you.

In the touch listener, the event.target refers to the object which is listening for touches - whatever object you called :addEventListener( “touch”, <func> ) on.

In your case, it is the Runtime object. Having said that, I would not do that - I would attach to the blowfish object, instead of Runtime.

My code (which was written rather hastily as I left the house this morning) is not 100% compatible with yours as it assumes the listener has been attached to an object rather than the environment in general. To make it work, you can replace event.target with blowfish and it should be fine.

I think you need to read the event guide: https://docs.coronalabs.com/daily/guide/events/touchMultitouch/index.html

Basically, when you start handling a touch event you want to tell the system that you’re going to be handling the particular event for this object until it ends. This is why we tell the stage to direct all events with the given id to the listening object.

We also say that we are going to filter out any events which get received if the object has not accepted a began phase, because otherwise it could be a touch which is sweeping in from somewhere else and isn’t relevant. We only want touch events which begin on our listening object - not touches which started on something else. This is handled by the .hasFocus value.

Returning true from any event listener tells the system not to propagate the event to any other listening objects because we handled it. Returning false says that we didn’t and to continue trying to send the event to other objects, until something does handle it.

e.g.: If we are receiving a began phase on our object we return true because we want to start a drag operation. If we receive a moved event but we haven’t received a began, hasFocus will be false so we don’t care, then we return false - it’s not our touch.

Can you just add transparency around the golf ball  making it bigger? Your physics body would be the size and shape of the golf ball. You would need to turn .isHitTestable = true on so that you could interact with the transparent area.