Come to think of it… I wasn’t happy with the way the dragging was being done, so here’s an update. The circles can now be moved around using touch joints, which removes the need to convert their body type - it really cleans up the code in the circleTouch event, I think.
One thing to note is that the ‘return true’ in the event handler functions stops the event from propagating to handlers below, which is good to know - the top objects in the display hierarchy get the touch events first. Set isHitTestable = true to allow invisible (either isVisible=false or alpha=0) objects to continue to receive touch (‘hit’) events.
Oh yes, added some comments. The code could be tidier and frankly, I try never to have global functions (I’m an OO developer) but sometimes they are useful in small programs. In this case, exclusively using table functions would cause the code to be longer and a bit more complex.
Also, as a kick, I’ve added tilting gravity, so deploy to device, add some circles and tilt it and they should roll off the board (thank Ansca for that one)…
[lua]-- the usual stuff
local physics = require( “physics” )
physics.start()
physics.setGravity( 0, 9.8 )
physics.setDrawMode( “normal” ) – normal, hybrid, debug
– just a rectangle at the bottom so the circles have something to bounce off
local rect = display.newRect( 0,0 , display.contentWidth, 10 )
rect.x = display.contentWidth/2
rect.y = display.contentHeight
rect:setFillColor( 255, 255, 255, 255 )
physics.addBody( rect, “static”, { friction=.2, bounce=.1, density=.1 } )
– basic tilting gravity (as found in ansca’s shape tumbler sample code)
function onTilt( event )
physics.setGravity( ( 9.8 * event.xGravity ), ( -9.8 * event.yGravity ) )
end
Runtime:addEventListener( “accelerometer”, onTilt )
– basic hypoteneuse calculation to work out the distance between two points
– input a and b are: {x,y} tables
function distance( a, b )
local width, height = b.x-a.x, b.y-a.y
return math.sqrt( width*width + height*height )
end
– list of circles currently bouncing about
local circles = {}
– the circle currently being sized
local circle = nil
– a function declared on the circles table so checking for collisions with a new circle is easy
function circles:isColliding( c )
for i=1, #circles do
local distance = distance( c, circles[i] )
if (distance <= c.radius+circles[i].radius) then
– oh dear, found a collision with an existing circle
return true
end
end
– current size of new circle does not collide with existing circles
return false
end
– table timer used to remove circles which have gone too far from the screen (memory conservation)
function circles:timer( event )
for i=1, #circles do
local x, y = circles[i].x, circles[i].y
if (x < -100 or x > display.contentWidth + 100 or y < -100 or y > display.contentHeight + 100) then
local c = circles[i]
table.remove( circles, i )
c:removeSelf()
c = nil
end
end
end
timer.performWithDelay( 1000, circles, 0 )
– global function used to manage the circles
– in a real app, this would probably be declared within a newCircle function to keep object oriented principles
– but here it is easier to read as a global func
function circleTouch( event )
if (event.phase == “began”) then
display.getCurrentStage():setFocus( event.target )
event.target.joint = physics.newJoint( “touch”, event.target, event.x, event.y )
elseif (event.phase == “moved”) then
event.target.joint:setTarget( event.x, event.y )
elseif (event.phase == “ended” or event.phase == “cancelled”) then
display.getCurrentStage():setFocus( nil )
event.target.joint:removeSelf()
event.target.joint = nil
end
return true
end
– the magic
function touch( event )
if (event.phase == “began”) then
– if a circle is not being made, create one
if (circle == nil) then
physics.pause()
circle = display.newCircle( event.x, event.y, 2 )
circle:setFillColor( 255, 255, 255, 255 )
circle.radius = 2
end
elseif (event.phase == “moved”) then
– if a circle is being made, check its next radius will not collide with other circles and then resize it
if (circle ~= nil) then
local radius = distance( circle, event )
local x, y = circle.x, circle.y
– the resizing, but only if its new size won’t overlap another circle
– (though, its fun to watch when they do - try commenting this ‘if’ condition out)
if (not circles:isColliding( { x=x, y=y, radius=radius } )) then
circle:removeSelf()
circle = display.newCircle( x, y, radius )
circle:setFillColor( 255, 255, 255, 255 )
circle.radius = radius
end
end
elseif (event.phase == “ended”) then
– circle has been completed properly, add it as a body and nil out the circle variable
if (circle ~= nil) then
physics.addBody( circle, “dynamic”, { friction=.3, bounce=.7, density=.5, radius=circle.radius } )
circle:addEventListener( “touch”, circleTouch )
circles[#circles +1] = circle
circle = nil
physics.start()
end
elseif (event.phase == “cancelled”) then
– something caused the new circle to be cancelled (i can’t think what could do this right now)
if (circle ~= nil) then
circle:removeSelf()
circle = nil
physics.start()
end
end
end
– the stage responds to basic touch events, but this will never get called if the touch happens on top of an existing circle
Runtime:addEventListener( “touch”, touch )[/lua] [import]uid: 8271 topic_id: 4246 reply_id: 13311[/import]