# How do I make a curved surface out of rectangular blocks?

For a Peggle-like game, I want to make blocks that follow a curve, like this:

The blocks would then disappear as the ball hits them.

I managed to draw some horizontally, but I'm having trouble making them follow a path:

How do I do this? Do I need to create Box2D objects with custom vertices?

• Do you want the boxes to simply not overlap or do you want there to be no gaps anywhere? (I'm not exactly sure what you mean by "offsetting the object Y axis according to the object angle"). – Roy T. Feb 19 '14 at 14:51
• You can't fill a curve with non-overlapping rectangles, so you're going to have to create some custom geomety if you want no gaps. – Anko Feb 19 '14 at 14:58
• @RoyT. The gaps aren't important. My real problem is to compute the position of the block which follow each other with different angle. – Moerin Feb 19 '14 at 15:11
• The way I would approach this is to define a series of vertices that act as the common corners between each box. Even using a path to define them, you still need additional parameters to define the distance between the vertices and how long each box is. – user39686 Feb 19 '14 at 15:19
• The "boxes" on the first image are not boxes, they are pairs of triangles: i.stack.imgur.com/Tzuql.png – egarcia Feb 19 '14 at 15:46

Given a "root" curve, here's how you might generate block vertices.

The root curve is in the middle, in black. Its control points are shown with red Xs.

In short: I made a Bézier and sampled it (at a configurable rate). I then found the perpendicular vector of the vector from each sample to the next, normalised it, and scaled it to to a (configurable) half-width, first to the left, then inversely to the right. Then drew it.

Stuff you could add to this:

Here's my code. It's written in Lua (for the LÖVE game framework), but I think it's readable for anyone.

local v = require "vector"

-- A function that makes bezier functions
-- Beziers have start point     p0
--              control point   p1
--              end point       p2
local function makeBezierFunction(p0,p1,p2)
return function (t)
local pow = math.pow
return pow( (1-t),2 ) * p0
+ 2 * (1-t) * t * p1
+ pow(t,2) * p2
end
end

love.graphics.setBackgroundColor(255, 255, 255)
function love.draw()
local line = love.graphics.line
local colour = love.graphics.setColor

-- Bezier sampling parameters
local nSegments = 10
local segIncr = 1/nSegments

-- Bezier definition: Start (p0), control (p1) and end p2) point
local p0 = v(100,100)
local p1 = v( love.mouse.getX(), love.mouse.getY() )
local p2 = v(500,100)
local controlPoints = {p0,p1,p2}
local bez = makeBezierFunction(p0,p1,p2)

-- Sample the bezier
for i=0,1-segIncr,segIncr do
colour(0, 0, 0)
local x1,y1 = bez(i        ):unpack()
local x2,y2 = bez(i+segIncr):unpack()
line(x1,y1,x2,y2)

-- Find left and right points.
local center = v(x1, y1)
local forward = v(x2, y2) - center
local left = center + forward:perpendicular():normalize_inplace() * 10
local right = center - forward:perpendicular():normalize_inplace() * 10

-- Draw a line between them.
line(left.x, left.y, right.x, right.y)

-- Find *next* left and right points, if we're not beyond the end of
-- the curve.
if i + segIncr <= 1 then
local x3, y3 = bez(i+segIncr*2):unpack()
local center2 = v(x2, y2)
local forward2 = v(x3, y3) - center2
local left2 = center2 + forward2:perpendicular():normalize_inplace() * 10
local right2 = center2 - forward2:perpendicular():normalize_inplace() * 10

-- Connect the left and right of the current to the next point,
-- forming the top and bottom surface of the blocks.
colour(0, 0xff, 0)
line(left.x, left.y, left2.x, left2.y)
colour(0, 0, 0xff)
line(right.x, right.y, right2.x, right2.y)
end
end

-- Draw an X at the control points
for _,p in ipairs(controlPoints) do
local x,y = p:unpack()
colour(0xff,0x00,0x00)
line(x-5,y-5, x+5,y+5)
line(x-5,y+5, x+5,y-5)
end
-- Draw lines between control points
for i=1,#controlPoints do
colour(0xff,0x00,0x00, 100)
local cp1 = controlPoints[i]
local cp2 = controlPoints[i+1]
if cp1 and cp2 then
line(cp1.x, cp1.y
,cp2.x, cp2.y)
end
end
end


If you'd like to play with it: Get LÖVE and put the above code into main.lua in its own directory. Put vector.lua from the HUMP library in the same directory. Run it as love <that-directory> from a command line.

Move the mouse around! The middle control point is set to the mouse location:

• Anko have u tried LibGdx? if so, do u prefer Löve? I am moving away from using standard android API after my current game and Im trying to decide between LibGdx and Löve. Interesting answer above as always btw – Green_qaue Feb 19 '14 at 18:20
• @Anko Thanks a lot, it's more than i expected. More i think i can easly understand your code since i use MonkeyX for my game which is similar to LUA. – Moerin Feb 19 '14 at 18:42
• @iQ I haven't used Libgdx, but I've read lots about it and I know Java well. Libgdx is big. (It has accelerometer support, built in curve generators and everything), while Love2D is very small (it doesn't have any of those, theres' no shader support, etc). Thanks to its simplicity though, Love2D has been great for quick prototypes and small games, but it might be too minimalist for some projects. Who knows. (You do! Try it and see. :D) – Anko Feb 19 '14 at 18:57
• Great answer, and that GIF is really a nice bonus! – Roy T. Feb 25 '14 at 12:42