Improved Interactive Line Drawing to Physics Body

Posted by gander, Posted on September 3, 2011

4 votes

A proof of concept on how to draw a line and have it act as a physical body. I owe MichaelAssadi a huge debt of gratitude for posting his example, it was what got me started.

The problem with other examples is that it basically draws a box at the intersection of each segment which I couldn't come up with a way of making it smooth. Also, as the move event fires (it looks like) every frame when drawing if you're swiping across the screen fast there are large gaps which objects may fall through (easy to do in Michael's example.)

I took inspiration from that and from the fact that you can use custom polygons to create physical bodies on a displayObject and am just laying a polygon on each segment. I think this should perform fairly well (would think as well as the boxes as it's just a 3-sided poly per segment and it gives 100% smooth coverage along the line. The one possible point of failure is that it is literally a flat poly, the poly is just (from line segment perspective) x1,y1 -> x2, y2 -> x1, y1. I can't imagine any cases where that would break but it's the biggest question about it in my mind. You could also have a problem if you have a fat line with the poly being it the middle. The interacting object won't "touch" until the center of the line (to see what I'm talking about set line_width = 25 or something. Could probably cheat this by doing radius on the ball's physics body but haven't played with it.

One way I'd like to try improving this if I get time to spend is to apply the smoothCurve (catmull splines) code that's being used in the Flight Path example. Anybody? Anybody? ; )

I'm not a neophyte developer but am new to Lua and Corona and did this very sloppy so forgive the lack of comments (should be fairly understandable though). Also, if anybody sees anything desperately wrong or has any style/code/design suggestions please let me know (you know, like mixing snake and camel case ; ).

NOTES:
There are two boxes on the bottom, the box on the left will drop a new ball (not collecting balls, so potential memory leak of seriously abused), and the box on the right will clear all lines.

The colors are the way they are because my 4 y/o daughter started playing with it on my iPad and I did that as a little treat for her.

It does seem to chug along pretty seriously at like the bottom of a U type curve and I haven't been able to figure that one out at all, any suggestions would be greatly appreciated. Set DrawMode to hybrid to see it clearly.

Thanks to all for sharing your code gems here and for any feedback and/or improvements!

Gerald

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
require("physics")
physics.start()
physics.setDrawMode("normal")
 
local HEIGHT = display.contentHeight
local WIDTH = display.contentWidth
 
 
local lines = {}
local line_number = 1
local line_width = 5
local prev_x, prev_y, ball
 
local function draw_line(e)
 
  if e.phase == "began" then
    prev_x = e.x
    prev_y = e.y
 
  elseif e.phase == "moved" then
    lines[line_number] = display.newLine(prev_x, prev_y, e.x, e.y) 
    lines[line_number]:setColor(math.random(255), math.random(255), math.random(255))
    lines[line_number].width = line_width
    dist_x = e.x - prev_x
    dist_y = e.y - prev_y
    -- Add a physics body that's a flat polygon that follows each segment of the line
    physics.addBody(lines[line_number], "static", { density = 1, friction = 0.5, bounce = 0, shape = {0, 0, dist_x, dist_y, 0, 0} } )
    prev_x = e.x
    prev_y = e.y
    line_number = line_number + 1
  elseif e.phase == "ended" then
  end
end
 
 
new_ball_button = display.newRect(10, HEIGHT - 75, 150, 75)
delete_lines_button = display.newRect(WIDTH - 150, HEIGHT - 75, 150, 75)
 
-- Create a new ball
function new_ball_button:touch(e)
  if e.phase == "ended" then
    ball = display.newCircle(50, 50, 25) 
    physics.addBody(ball, {density = 5000, friction = 0.5, bounce = 0.1, radius = 25} )
    ball:setFillColor(math.random(255), math.random(255), math.random(255))
  end
end
 
-- Delete all existing lines
function delete_lines_button:touch(e)
 
  if e.phase == "ended" then
    for i = #lines, 1, -1 do
      if (lines[i]) then
        lines[i].parent:remove(lines[i])
        lines[i] = nil
      end
    end
    lines = {}
    line_number = 1
  end
end
 
 
Runtime:addEventListener("touch", draw_line)
new_ball_button:addEventListener("touch", new_ball_button)
delete_lines_button:addEventListener("touch", delete_lines_button)


Replies

mightE
User offline. Last seen 2 weeks 2 days ago. Offline
Joined: 27 Jul 2011

Very pretty lines, quick look at memory:

started at 84ish, rose to 360 after 10 seconds of drawing (yes I timed ;) ) and when lines were erased went down to 115, so yes. memory leak

MichaelAssadi
User offline. Last seen 1 day 19 hours ago. Offline
Joined: 20 Jan 2011

Very impressive! You don't need to thank me, this was all you. Great job! You did far better ;)

nicholasclayg's picture
nicholasclayg
User offline. Last seen 13 hours 9 min ago. Offline
Joined: 16 May 2011

I noticed the ball won't chug along if you are quick to draw out the line, if you cause little ripples then ya it chugs along.

I think the way I understand it is that every pixel of the line becomes a physics body or something. I know that's the case for a tool called "Svg level editor" by Karnakgames. He says if you free hand draw something in the svg inkscape program and it parses out the data, it will treat each pixel of the free hand drawing as it's own physics body.

I just noticed, bumping line_width from 5 to 100, it's "crosses" (best way I can describe it lol) that are close together being drawn across the screen, with a line going through the intersection.....interesting. I wish I had talent like this to do these amazing things!

ng

nicholasclayg's picture
nicholasclayg
User offline. Last seen 13 hours 9 min ago. Offline
Joined: 16 May 2011

How would I remove the ball?

I don't quite understand the whole nil thing, so I know it's a function I create and to remove the ball.

Let's say I draw a line in a half circle and the ball is stuck, and I want to delete the ball along with the lines, how would that be done?

I was thinking about adding something to the
delete_lines_button but not entirely sure how to code that. That's due to being a noob though :)

Either case, my 2 week old boy likes the rainbow lines, ESPECIALLY WHEN I MAKE THEM 1 INCH THICK. lol.

ng

nicholasclayg's picture
nicholasclayg
User offline. Last seen 13 hours 9 min ago. Offline
Joined: 16 May 2011

Ok, so I may not be able to write my OWN code to figure things out, but I was able to use the "Bridge" example to figure out how to insert it into this code.

Basically, an event listener is now on the ball so you can remove the ball. I suppose now that I think about it I should have added a button that removes the ball in case the ball falls out of sight (for memory I suppose). I'll let someone else handle that piece though :)

At least I can contribute something, even though I simply copied and pasted into the correct areas....that counts for something right :)?

ng

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
local physics = require("physics")
 
local physics = require("physics")
physics.start()
 
physics.setPositionIterations(32)
--"hybrid" "debug" "normal"
physics.setDrawMode("hybrid")
 
 
 
local HEIGHT = display.contentHeight
local WIDTH = display.contentWidth
 
local lines = {}
local line_number = 1
local line_width = 10
local prev_x, prev_y, ball
 
 
local function draw_line(e)
 
  if e.phase == "began" then
    prev_x = e.x
    prev_y = e.y
 
  elseif e.phase == "moved" then
    lines[line_number] = display.newLine(prev_x, prev_y, e.x, e.y) 
    lines[line_number]:setColor(math.random(255), math.random(255), math.random(255))
    lines[line_number].width = line_width
    dist_x = e.x - prev_x
    dist_y = e.y - prev_y
    -- Add a physics body that's a flat polygon that follows each segment of the line
    physics.addBody(lines[line_number], "static", { density = 1, friction = 0.1, bounce = 0, shape = {0, 0, dist_x, dist_y, 0, 0} } )
    prev_x = e.x
    prev_y = e.y
    line_number = line_number + 1
  elseif e.phase == "ended" then
  end
end
 
 
new_ball_button = display.newRect(10, HEIGHT - 75, 150, 75)
delete_lines_button = display.newRect(WIDTH - 150, HEIGHT - 75, 150, 75)
 
  local removeBody = function( event )
        local t = event.target
        local phase = event.phase
 
        if "began" == phase then
                t:removeSelf() -- destroy object
        end
 
        -- Stop further propagation of touch event
        return true
end
 
 
-- Create a new ball
function new_ball_button:touch(e)
  if e.phase == "ended" then
    ball = display.newCircle(50, 300, 25)
        --ball = display.newImage ("blueball.png")
    physics.addBody(ball, {density = 5, friction = 100, bounce = 0.1, radius = 25} )
    ball:setFillColor(math.random(255), math.random(255), math.random(255))
        ball.IsBullet = true
  end
 
---+++++
---CODE TO REMOVE BALL YAY!!!!!
  
   ball:addEventListener( "touch", removeBody ) -- assign touch listener to rock
  
end
 
 
-- Delete all existing lines
function delete_lines_button:touch(e)
 
  if e.phase == "ended" then
    for i = #lines, 1, -1 do
      if (lines[i]) then
        lines[i].parent:remove(lines[i])
        lines[i] = nil
      end
    end
    lines = {}
    line_number = 1
  end
end
 
 
Runtime:addEventListener("touch", draw_line)
new_ball_button:addEventListener("touch", new_ball_button)
delete_lines_button:addEventListener("touch", delete_lines_button) 
 
 

guruk's picture
guruk
User offline. Last seen 1 day 22 hours ago. Offline
Joined: 9 Feb 2010

I had to laugh while reading your comment :)
Finally as u did.. thats coding... just bring the right pieces together.. haha.

thats the way I create programs since 25 years /big smile/

all the best
greets chris