So, I've been working on this game where you drag objects onto a playing area which is populated with arbitrarily shaped objects (defined as complex bodies) and I wanted to implement a way of checking whether the dragged object was overlapping an object on the stage or not. In real time.
Turned out this wasn't that easy... but I came up with a "hack" that did the job. Not very optimized or anything and I really hope there is a better way of doing it, but I'm sticking with it 'til I (or you) come up with something better. Hope it helps/inspires someone else with a similar problem.
This code assumes that you have created a display object named draggableObject and added a touch event handler to it:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | function draggableObject:touch( event ) local phase = event.phase -- Some setup code etc has been removed for clarity if phase == "moved" then -- Updates position. startX and startY are set elsewhere self.x = event.x - self.startX self.y = event.y - self.startY -- Here we call the method that checks for overlaps self:checkLegalPosition() -- isPositionLegal is set to true when the object is created -- As an example I set the fill color to green or red respectively to show if the -- object is in the clear or not. You can do whatever you like here of course. if self.isPositionLegal then self:setFillColor(0, 255, 0) else self:setFillColor(255,0,0) end -- Drop when finger is lifted elseif phase == "ended" or phase == "cancelled" then -- Some code removed for clarity -- Check if object is in the clear. -- If so drop it on the playing area. If not remove it. -- This is just what I did. You might want to do something else here. if self.isPositionLegal then self:dropOnStage() else self:destroy() -- This also destroys the collision detector if it exists end end return true end |
Great stuff - thanks for sharing it :) Many people ask about this and it seems like a solid workaround.
Nicely done!
Peach :)
Glad you like it :D
I've seen a lot of posts asking about functionality similar to this. I hope people find this post and can make some use of it. It's hard writing a headline that people can relate to though: similar problem -> different ways of describing it...
Hi,
I'm new in Corona and still fighting agains my low programming skills. This solutions would work great for my game, but I tried to implant it into my code and it didn't work. Could someone please post a simple code example with this solution?
Thanks very much in advance!
Is that different from the function I wrote?
http://developer.anscamobile.com/code/flashs-hittestobject-emulated-using-contentbounds
Jhocking, thanks for the reply, but your function would not work for me. The above functions, if I am not wrong, use the physics collision to test overlapping. As I will be working with polylines, your function will not work, as the polyline border is very complex.
But thanks for the response.
Hello,
I have prepared this simple code with the detection functions, but it doesn't work. I wanted to test if the overlapping of the created circle (it is created once the background is touched), but no way. I asume I am too new wih Corona. There might be something that I miss with the layers (I suppose somehow I need to detect collision only with the square, not with the background) and something else that I miss with the overlapping detection functions.
Anyone could help me, please? It wold be much apreciated.
Here is the code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 | ------------ local screenW, screenH = display.contentWidth, display.contentHeight local background = display.newRect(0, 0, screenW, screenH) background:setFillColor(100,100,100) local objectToTouch = display.newRect(200, 200, 50, 50) objectToTouch:setFillColor(200,50,50) function CreateCircle( event ) local phase = event.phase if "began" == phase then circle = display.newCircle(event.x, event.y, 10) circle:setFillColor(0,255,255) circle:checkLegalPosition() if circle.isPositionLegal then circle:setFillColor(0, 255, 0) else circle:setFillColor(255,0,0) end Runtime:addEventListener("touch", MoveCircle) end if "ended" == phase then circle:removeSelf() end end function MoveCircle( event ) print (event.name.."occured") circle.x = event.x circle.y = event.y end background:addEventListener("touch", CreateCircle) function circle:checkLegalPosition() self.isPositionLegal = true if not collisionDetector then createCollisionDetector( self ) else destroyCollisionDetector( self ) createCollisionDetector( self ) end end function createCollisionDetector( forObject ) collisionDetector = display.newCircle( 0, 0, 10 ) registry.currentLevelStage:insert( collisionDetector ) collisionDetector.x, collisionDetector.y = registry.currentLevelStage:contentToLocal( forObject.x, forObject.y ) physics.addBody( collisionDetector, "static", { isSensor = true, radius = 16 }) collisionDetector:addEventListener("collision", forObject) end function destroyCollisionDetector( forObject ) if collisionDetector then collisionDetector:removeEventListener("collision", forObject) collisionDetector:removeSelf() collisionDetector = nil end end function circle:collision( event ) if event.phase == "began" then self.isPositionLegal = false; self:setFillColor(255, 0, 0) end return true end -------------------- |
The above functions, if I am not wrong, use the physics collision to test overlapping. As I will be working with polylines, your function will not work, as the polyline border is very complex.
ah I see, that wasn't clear from your thread title.
Thanks for this post Daniel, this seems like a great work around. However I can't seem to get a working example running. Is there anyway you would be willing to post a simple working version of this in the code exchange section, or here in this thread? If you could, I and the corona community would greatly appreciate it.
Hi all and thanks for the response. Unfortunately my iOS game dev endeavors have been in hibernation of late so my memory isn't all that fresh on the subject, but give me some time and I'll post an example.
Cheers
Ok, still haven't been doing any Corona stuff but I'll post the code in it's entirety. This is totally unreviewed and provided as-is. Hope you can make use of it :)
This is all in a module named bomb.lua
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 | module (..., package.seeall) local physics = require( "physics" ) local physicsData = (require "shapeDefs").physicsData(1.0) local cos = math.cos local sin = math.sin local ui = require("ui") local collisionDetector = nil function new() local bomb = display.newCircle( 0, 0, 16 ) -- Fields bomb.startX = 0 bomb.startY = 0 bomb.isPositionLegal = true function bomb:init() self:setFillColor( 0, 200, 0 ); self:addEventListener( "touch", self ) end function bomb:collision( event ) if event.phase == "began" then self.isPositionLegal = false; self:setFillColor(255, 0, 0) end return true end -- Handle touch events function bomb:touch( event ) local phase = event.phase if "began" == phase then -- Make target the top-most object local parent = ui self.x, self.y = parent:contentToLocal( event.x, event.y ) parent:insert( self ) display.getCurrentStage():setFocus( self ) self.hasFocus = true self.startX = event.x - self.x self.startY = event.y - self.y elseif self.hasFocus then -- Follow finger if phase == "moved" then self.x = event.x - self.startX self.y = event.y - self.startY self:checkLegalPosition() if self.isPositionLegal then self:setFillColor(0, 255, 0) else self:setFillColor(255,0,0) end -- Drop when finger is lifted elseif phase == "ended" or phase == "cancelled" then display.getCurrentStage():setFocus( nil ) self.hasFocus = false -- check if bomb is in legal position ie somewhwere on the playing field. -- If so drop it there. If not remove it. if self.isPositionLegal then self:dropOnStage() else self:destroy() end end end -- Returning true keeps the event from propagating return true end function bomb:checkLegalPosition() self.isPositionLegal = true local testX, testY = registry.currentLevelStage:contentToLocal( self.x, self.y ) local halfWidth, halfHeight = self.width * .5, self.height * .5 -- check if inside playing field if testX < halfWidth or testX > registry.playingFieldBounds.width - halfWidth then self.isPositionLegal = false end if testY < halfHeight or testY > registry.playingFieldBounds.height - halfHeight then self.isPositionLegal = false end -- check if overlapping other objects if not collisionDetector then createCollisionDetector( self ) else destroyCollisionDetector( self ) createCollisionDetector( self ) end end function bomb:dropOnStage() destroyCollisionDetector( self ) registry.activeBombs:insert( self ) self.x, self.y = registry.currentLevelStage:contentToLocal( self.x, self.y ) registry.currentLevelStage:insert( self ) end function bomb:destroy() if self then destroyCollisionDetector( self ) self:removeEventListener( "touch", self ) self:removeSelf() self = nil end end bomb:init(); return bomb end function createCollisionDetector( forBomb ) collisionDetector = display.newCircle( 0, 0, 16 ) registry.currentLevelStage:insert( collisionDetector ) collisionDetector.x, collisionDetector.y = registry.currentLevelStage:contentToLocal( forBomb.x, forBomb.y ) physics.addBody( collisionDetector, "static", physicsData:get( "bomb" )) collisionDetector:addEventListener("collision", forBomb) end function destroyCollisionDetector( forBomb ) if collisionDetector then collisionDetector:removeEventListener("collision", forBomb) collisionDetector:removeSelf() collisionDetector = nil end end |