Create and scale circles while touch

Aloha!

I want to create a new circle and scale it up for as long as I keep my finger pressed.
I managed to do just this, but my problem is that for as long as I press it also creates new circles.
Also in the end I add it to the physics world.

In short what I want to do: Creat ONE circle and scale it up for as long as I touch then add physics… and repeat on new touch!

Any tricks from the pros? :slight_smile:

[lua]-- HANDLE TOUCH/DRAG EVENTS
function onMouseHandler(e)

if(e.phase==“began” or e.phase==“moved”) then
_mouseDown = true;
_mousePoint = { x = e.x, y = e.y}
startscale = system.getTimer()
print(startscale)

elseif(e.phase==“ended” or e.phase==“cancelled”) then
_mouseDown = false;
_mousePoint = nil

end

end

– SPAWN DISKS
function enterFrameHandler(event)

if (_mouseDown) then

scaler = (system.getTimer() - startscale) / 1000
if disk == nil then
randImage = diskGfx[math.random( 1, 3 )]
allDisks[#allDisks + 1] = display.newImage( randImage )
local disk = allDisks[#allDisks]

print(scaler)

disk.x = _mousePoint.x; disk.y = _mousePoint.y
disk.rotation = math.random( 1, 360 )
disk.xScale = scaler; disk.yScale = scaler
sensorRadius = scaler*70

Body = { density=0.3, friction=0.6, bounce=0.5, radius=sensorRadius }

physics.addBody( disk, Body )
disk.linearDamping = 0.4
disk.angularDamping = 0.6 end
end
end

Runtime:addEventListener(“enterFrame”, enterFrameHandler); [import]uid: 10657 topic_id: 4246 reply_id: 304246[/import]

onMouseHandler isn’t being called [import]uid: 9371 topic_id: 4246 reply_id: 13171[/import]

Yeah it is just didn’t include it!

It’s working but I just want to create one circle and scale that one in the loop. But the loop also makes it create new circles while scaling. [import]uid: 10657 topic_id: 4246 reply_id: 13174[/import]

You could add a ‘flag’ called _creatingCircle and set it to true if you are currently scaling a circle. Set it to false when the mouse ended event is fired. Then only create a new circle if it’s false. [import]uid: 9371 topic_id: 4246 reply_id: 13175[/import]

Awesome idea… I’m gonna try that!! BRB [import]uid: 10657 topic_id: 4246 reply_id: 13176[/import]

Excellent my friend! Worked like a charm!!! [import]uid: 10657 topic_id: 4246 reply_id: 13178[/import]

Send the cheque to the usual place! [import]uid: 9371 topic_id: 4246 reply_id: 13180[/import]

IMHO, I think you want to separate the “began” and “moved” event handlers. Use the “began” event to create the circle, then the “moved” event to resize it. Use the “ended” event to add physics to it and the “cancelled” to delete it as if it had never been started. This would make for much simpler and cleaner code.

But, you know, just MHO…

Matt. [import]uid: 8271 topic_id: 4246 reply_id: 13191[/import]

Here’s my take on what you’re trying to do… I’ve not tried it yet cos I’m not in front of my mac I’m afraid…

[lua]physics.start()
– just a useful function to get the distance between two points
function getLength( a, b )
local width, height = b.x-a.x, b.y-a.y
return math.sqrt(width*width + height*height)
end
– list of circles which have been completed and turned into physical objects
local circles = {}

– current circle being created but not yet a physics body
local newcircle = nil
function touch( event )

if (event.phase == “began”) then
– creates a new circle as just a circle, default radius of 10 pixels
if (newcircle == nil) then
newcircle = display.newCircle( event.x, event.y, 10 )
newcircle:setFillColor( 255,255,255,255 )
end

elseif (event.phase == “moved”) then
– destroys the circle and creates a new one with the radius equal to
– the distance from the original starting point and the current touch point
if (newcircle ~= nil) then
local x, y = newcircle.x, newcircle.y
local radius = getLength( newcircle, event )

newcircle:removeSelf()

newcircle = display.newCircle( x, y, radius )
newcircle:setFillColor( 255,255,255,255 )
end

elseif (event.phase == “ended”) then
– turns the circle into a body and adds it to the list of physical circles
if (newcircle ~= nil) then
physics.addBody( newcircle, { density = 1.0, friction = 0.3, bounce = 0.5, radius=newcircle.radius } )
circles[#circles +1] = newcircle
newcircle = nil
end

elseif (event.phase == “cancelled”) then
– destroys the circle - somehow the drag operation was cancelled
if (newcircle ~= nil) then
newcircle:removeSelf()
newcircle = nil
end

end

end

Runtime:addEventListener( “touch”, touch )[/lua] [import]uid: 8271 topic_id: 4246 reply_id: 13213[/import]

This forum is so helpful! Thank you Matt!
This is way better!!

:slight_smile: [import]uid: 10657 topic_id: 4246 reply_id: 13218[/import]

Doh… seems to be somekind of bug

the ‘newcircle.radius’ doesn’t work so I added in phase moved:
[lua]local radius = getLength( newcircle, event )
radiusPh = radius[/lua]
[lua]if (newcircle ~= nil) then
physics.addBody( newcircle, { density = 1.0, friction = 0.3, bounce = 0.5, radius=radiusPh } )
[/lua] [import]uid: 10657 topic_id: 4246 reply_id: 13222[/import]

Another thing I’ve been searching for while scaling up, can you detect if the scaling circle is overlapping for example edge or another already built circle and make the scaling stop? [import]uid: 10657 topic_id: 4246 reply_id: 13223[/import]

Sorry, yes, as I said, I’m doing this from memory…
Line 24.5 should be:

newcircle.radius = 10

Line 37.5 should be:

newcircle.radius = radius
Matt. [import]uid: 8271 topic_id: 4246 reply_id: 13224[/import]

To check for when the resizing circle is colliding with another, pre-existing, circle you’ve got the choice of checking it’s location/radius against the location/radius of all the circles in the circle list or adding it as a physics body with “static” body type for each “moved” event.

matt. [import]uid: 8271 topic_id: 4246 reply_id: 13225[/import]

Ok, here’s a version of the program where the circles cannot become any larger if the drag point causes them to touch another circle. Also, the physics is paused while this is happening and the circles can be dragged once placed on the stage. Also, they get removed if they fall too far off the screen. I hope this is useful.

Matt.

[lua]local physics = require( “physics” )
physics.start()
physics.pause()
physics.setGravity( 0, 10 )
physics.setDrawMode( “normal” ) – normal, hybrid, debug
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 } )

function distance( a, b )
local width, height = b.x-a.x, b.y-a.y
return math.sqrt( width*width + height*height )
end
local circles = {}
local circle = nil
function circles:isColliding( c )
for i=1, #circles do
local distance = distance( c, circles[i] )
if (distance <= c.radius+circles[i].radius) then
return true
end
end
return false
end

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 )
function circleTouch( event )

if (event.phase == “began”) then
event.target.prev = { x=event.x, y=event.y }
display.getCurrentStage():setFocus( event.target )
event.target.bodyType = “static”
elseif (event.phase == “moved”) then
event.target.x = event.target.x + (event.x - event.target.prev.x)
event.target.y = event.target.y + (event.y - event.target.prev.y)
event.target.prev = { x=event.x, y=event.y }
elseif (event.phase == “ended” or event.phase == “cancelled”) then
display.getCurrentStage():setFocus( nil )
event.target.bodyType = “dynamic”
end

return true
end
function touch( event )

if (event.phase == “began”) then
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 (circle ~= nil) then
local radius = distance( circle, event )
local x, y = circle.x, circle.y

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
if (circle ~= nil) then
physics.addBody( circle, “dynamic”, { friction=.3, bounce=.3, density=.2, radius=circle.radius } )
circle:addEventListener( “touch”, circleTouch )

circles[#circles +1] = circle
circle = nil
physics.start()
end
elseif (event.phase == “cancelled”) then
if (circle ~= nil) then
circle:removeSelf()
circle = nil
physics.start()
end
end

end

Runtime:addEventListener( “touch”, touch )[/lua] [import]uid: 8271 topic_id: 4246 reply_id: 13263[/import]

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]

Haha got damnit Matt, if you help me even further I have to start sending you christmas cards every year :slight_smile:

Thanks a lot, I’m gonna play around with it!!

Cheers [import]uid: 10657 topic_id: 4246 reply_id: 13312[/import]

No problem - I enjoy making these little bits and pieces more than proper coding, I think!

Let me know if there’s anything you need explaining or converting. I think an interesting exercise would be to convert it to multi-touch and have two fingers be used to move and scale and new circle or existing circles. Although, to be fair, that’s quite a change…

m [import]uid: 8271 topic_id: 4246 reply_id: 13313[/import]