# The World’s Simplest 2D Curve Library

For the game I’m working on I need to have sprites that travel along curving paths.

I’m talking about the sprites traveling along somewhat arbitrary curves, meaning curves that look good, not curves that result from gravity or other physical forces. If you need those kinds of curves, e.g. the parabolic trajectories of cannonballs, you need to simulate the forces acting on the sprites and that is not what I’m talking about in this post.

Caveat aside, an arbitrary curving path is a pretty common thing to need but I think is unnecessarily headache-inducing because curves in graphics are just confusing. Maybe you’ve found yourself thinking

• I don’t know what the difference between a spline, a bezier curve, a bezier curve of various degrees, a B-spline, a t-spline, etc. is.
• I don’t know which of the things mentioned above I need.
• Every time I try read the wikipedia article on these things the math gets heavy and my eyes glaze over

etc.?

So assuming it’s not just me, as a public service I’m going to try to clear this up.

Short version, if you need to have a sprite that travels along a curving path from point A to point B in x amount of time, you probably need a cubic bezier curve and generally, in 2d game programming, all you will ever need probably is n cubic bezier curves possibly concatenated together. You can concatenate them yourself if you need to do that, so what you need is a way to define a cubic bezier and a function get the point along the bezier at some time t. Despite what you would think from trying read the literature, this turns out to be trivial — I mean less than a dozen lines of code.

More thoroughly, explaining away my bulleted list above:

• A spline is a more general term than a “bezier curve”: a bezier curve is a particular polynomial function (that I will implement below) that defines a curve that goes from point A to point B given some control points. A bezier spline is an aggregation of n of these. A general spline can be an aggregation of other kinds curves e.g. a B-spline is composed of a bunch of curves that are generalizations of bezier curves.
• The only kinds of beziers you need to be concerned with are quadratic and cubic beziers. Quadratic beziers are just parabolas and are not interesting. Cubic beziers are curves that go from point A to point B and are tangent to a given line at A and tangent to given line at B. They are defined by A and B plus two other control points that define the tangent lines and the weight they have on the curve.
• Cubic bezier curves are easy to implement. See below.

So here is my curve “library”:
Bezier.h

#include &lt;utility&gt;

class Bezier {
private:
float x1_, y1_, x2_, y2_, x3_, y3_, x4_, y4_;
public:
Bezier(float x1, float  y1, float x2, float y2, float x3, float y3, float x4, float y4);
std::pair&lt;float,float&gt; getPoint(float t) const;
};


Bezier.cpp

#include &quot;Bezier.h&quot;

Bezier::Bezier(float x1, float  y1, float x2, float y2, float x3, float y3, float x4, float y4) :
x1_(x1), y1_(y1),
x2_(x2), y2_(y2),
x3_(x3), y3_(y3),
x4_(x4), y4_(y4) {
}

std::pair&lt;float,float&gt; Bezier::getPoint(float t) const {
float x = (x1_+t*(-x1_*3+t*(3*x1_ - x1_*t))) + t*(3*x2_+t*(-6*x2_ + x2_*3*t)) + t*t*(x3_*3-x3_*3*t) + x4_*t*t*t;
float y = (y1_+t*(-y1_*3+t*(3*y1_ - y1_*t))) + t*(3*y2_+t*(-6*y2_ + y2_*3*t)) + t*t*(y3_*3-y3_*3*t) + y4_*t*t*t;
return std::pair&lt;float,float&gt;(x,y);
}


You define a cubic bezier by making a Bezier object giving the constructor four points. (x1,y1) and (x4,y4) will be the start and end of the curve. The curve will be tangent to line segment (x1,y1)-(x2,y2) at its start and tangent to (x3,y3)-(x4,y4) at the end. To get a point along the curve call getPoint(t) where t=0.0 gives you (x1,y1), t=1.0 gives you (x4,y4), and 0.0 < t < 1.0 gives you the point along the curve in which 100t percent of the curve has been traversed e.g. 0.5 is halfway.

So that’s it. Code is here. I also included a Win32 GDI project that draws cubic beziers, screenshot below. (The sample program is also a little example of how to write a very basic Win32 program, which these days younger programmers seem to appreciate as a sort of parlor trick…)

# Syzygy Update

So, looking at the early entries of this blog, I must have started working on Syzygy around the beginning of the year 2012 because by March 2012 I had the Win32 prototype done. At that time, I didn’t own a Macintosh, didn’t own an iOS device, had never heard of cocos2d-x, and, professionally-wise, was still writing image processing code for Charles River Labs / SPC. Since then SPC was killed, and I moved from Seattle to Los Angeles … but anyway as of today, about a year later, I have the primary functionality of Syzygy running on my iPad, re-using the source code, mostly, from that prototype. I haven’t really been working on it the whole time — there was a lot of moving-to-California in there somewhere, but here’s a screenshot (click for full-size):

There’s a common question in the mobile games forum of gamedev.net, “How can I make a game for iOS only using Windows?”. The answer to this is either (1) you can’t or (2) write your game to Marmalade or cocos2d-x on Windows and then when you are done get a friend with a Mac to let you register as an Apple developer, build under Xcode, and submit to the App store. I always say (1) is the serious answer and if you are unserious, or want to develop a really simple game, then go with (2). Basically I say this because you need to run your game on a device frequently and early, and I’m seeing the truth to this now.

Now that I have Syzygy running on a device I’m seeing issues with input which are artifacts of running on an iPad. The prototype implemented mouse input as a stand-in for touch input. It turns out touch screens and mice aren’t the same thing. The game plays on the device, but when you drag tiles your finger is in the way of the tile visually. You can’t see the tile you are dragging — this seems like it wouldn’t be a big deal, but it kind of is. … This sort of thing is the reason, in my opinion, that if you are not testing on a device during primary development then you are not really serious…

So not sure what I’m going to do about this, I’m thinking of making the tile the user is dragging larger and offset to the upper-left while the user is dragging it. The problem with this is to make it look nice I’d have to have large versions of all the relevant art and some of it I don’t even really remember how I rendered in the first place…

gamedev.net recently linked to this video about the making of Marble Madness, which got me thinking about the raster-to-vector via contour extraction script I wrote in Python last year and the fact that, it being the future and all, I can probably find all of the art from Marble Madness unrolled into a single image file. So three clicks later and, oh yeah:

(Click the image for full size)

So I ran the above through my raster-to-vector converter. Here are the results (zipped, this is a huge file, over 20,000 SVG paths)  This file kills Adobe Illustrator. It took 15 minutes just to open it.

SVG for a single level is more manageable.  Here’s  the 2nd level as unzipped SVG … (curious to see if various browsers can handle this) Illustrator could handle this one pretty well so I experimented with applying various vector filters. Below is a bit of it with the corners rounded on the paths (click for a larger version):

Not sure what this all amounts to … just some stuff I did today. However I did learn

• Python is slow. It took a really long time to generate the big file, seemed too long. I think C++ would’ve been like an order of magnitude faster — that’s my intuition anyway.
• My contour extraction program really works which is kind of surprising — I thought for sure running it on something like this would crash it. (It does still have the problem that it can’t handle paletted raster image formats, but that’s the only bug I encountered)

# Sprite Packing in Python…

I’ve been working on my puzzle game Syzygy again, after a long hiatus, and am now writing to iOS/cocos2d-x rather than just working on the prototype I had implemented to Win32.

The way that you get sprite data into cocos2d is by including as a resource a sprite sheet image and a .plist file which is XML that specifies which sprite is where. Plists are apparently an old Mac thing — I had never heard of this format. .plists describing a lot of sprites would be a chore to write by hand so there is a cottage industry of sprite packing applications.

I tried out one called TexturePacker and liked it a lot — except that it is crippleware; I need a few features that are only in the full version; plus I can’t stand crippleware; and I think $30 is too much for something that I can write myself over the weekend. So I decided to write my own sprite packer over the weekend. The result is pypacker, a python script: source code here. Usage is like pypacker -i [input] -o [output] -m [mode] -p where • [input] = a path to a directory containing image files. (In any format supported by the python PIL module.) • [output] = a path + filename prefix for the two output files e.g. given C:\foo\bar the script will generate C:\foo\bar.png and c:\foo\bar.plist • [mode] = the packing mode. Can be either “grow” or fixed dimensions such as “256×256″. “grow” tells the algorithm to begin packing rectangles from a blank slate expanding the packing as necessary. “256×256″ et. al. tell the algorithm to start with the given image size and pack sprites into it by subdivision, throwing an error if they all won’t fit. • -p = optional flag indicating you want the output image file dimensions padded to the nearest power-of-two-sized square. The algorithm I used is a recursive bin packing algorithm in which sprites are placed one-by-one into a binary tree. I based it directly on Jake Gordon’s work in Javascript for generating sprite sheets for use in CSS, described here, only my algorithm is sort of like version 2 of his i.e. I fixed an issue that bugged me about his algorithm. The core of the algorithm is a function that looks like this: def pack_images( named_images, grow_mode, max_dim): root=() while named_images: named_image = named_images.pop() if not root: if (grow_mode): root = rect_node((), rectangle(0, 0, named_image.img.size[0], named_image.img.size[1])) else: root = rect_node((), rectangle(0, 0, max_dim[0], max_dim[1])) root.split_node(named_image) continue leaf = find_empty_leaf(root, named_image.img) if (leaf): leaf.split_node(named_image) else: if (grow_mode): root.grow_node(named_image) else: raise Exception("Can't pack images into a %d by %d rectangle." % max_dim) return root  We iterate through the images we want to pack. For each image, try to find a rectangular node in the tree that can contain the image. If one exists, place the image in the node and subdivide the node such that the remaining space, not taken up by the image, is available in the tree (this is what ‘split_node’ does). If such a node cannot be found, throw an exception if we are not in ‘grow’ mode or expand the root rectangle node to accommodate the new image if we are in ‘grow’ mode. This routine is very similar to the Javascript implementation I linked to above. The difference is in the details about the structure of the binary tree. Jake Gordon’s Javascript implementation uses a node type that stores an image in the upper left and has children that he calls ‘right’ and ‘down’ like this: Since actual data is always burnt into the upper left, it means that the tree can never subdivide into this space; we can never recurse into the upper left. This results in the grow_node routine being awkward to write. When we grow the root we either want to extend to the right or extend down, if the upper left can be a node and not image data this is a simple matter of creating a new node and making the the existing root its upper or left child. Anyway, Jake Gordon’s implementation results in a packing tree that cannot both grow right and grow down simultaneously because it would have been complicated to implement this. This limitation is not a problem practically as long as you sort the images from largest to smallest before running the packing algorithm — a standard heuristic from the bin packing literature. I however wanted to see if the standard sorting heuristic is really accomplishing anything. I wanted to be able to pack rectangles in random order. I therefore simplified the trinary node structure of the Javascript implementation into true binary nodes either oriented horizontally or vertically like this: Further now only leafs can contain images and if a node is not a leaf it always has two valid, that is non-null, children. Using this type of tree structure makes the full grow_node routine more or less trivial. Beyond that, I’m using the following heuristics: • If the orientation (horizontally or vertically) of a split is not forced, split with the orientation that will result in the new empty node having the largest area • If the orientation of growing the root rect is not forced, grow in the direction that leads to the smallest increase in the maximum side length of the root rectangle. (This heuristic enforces squarishness and is extremely important. Without doing this the grow version of the algorithm is basically unusable, and in this sense this grow heuristic can be considered part of the algorithm rather than a heuristic that can be swapped out) Sorting by size (max side length) turns out be about a 6% improvement with this algorithm. Here’s 500 rects packed with sorting (top) and without (bottom): # The Cocos2d-x Device Orientation Bug… This took me all morning to figure out. I’m posting here so there is clear information on this subject in at least one place on the internet. The situation is this: when targeting iOS6 using Cocos2d-x v2.0.2, there is a bug in the auto-generated “Hello, World” code that shows up in a fresh project such that the compiled game will not display in landscape orientation even after following the steps in this item from Cocos2d-x documentation (such that it exists). The solution is to follow the steps enumerated in this note from Walzer Wang. This fix is probably already in v2.1 but 2.1 is still beta, as far as I know, so this issue is probably still in a lot of code out there… # Final Lock Spot Picture Round Since I just moved to Los Angeles we’re obviously not hosting trivia at the Lock Spot anymore, but I want to put up the final picture round I did, because (1) it’s probably the best one I ever did in terms of how hard it was to put together and (2) apparently the main people who are finding this blog via Google are pub trivia hosts in Australia, which is oddly fitting given that the old trivia host at the Lock Spot in Ballard, back when Nousheen and I used to play on a team, was Australian … I think pub trivia is big in Australia. (Anyone? Lurkers?) So this one is a chain of phrases where the if item n is a name or phrase that ends with some word say “box” then item n+1 will begin with the word “box”, etc. Also it loops around such that the chain continues across the last and first items. # Picture Round – 3/19/12 Our picture round from Lockspot Trivia last Monday … Another one of these movie chain deals. # Thoughts on porting Syzygy to iOS I’ve started trying to figure out the way in which I’m going to port Syzygy to iOS. I don’t actually own a Mac — though I may get one this weekend — so this is all theoretical at this point. What I have right now is an implementation written in C++ to the Win32 API. Part of this implementation is a very basic 2D game framework. This 2D framework has an abstract widget class that has render and update methods. I didn’t call this class “sprite” because it is more general (and basic) than a sprite class : it can be implemented as anything that knows how to update and draw itself. For example, I have text widgets, that call the Win32 DrawText function in the draw method. Widgets are contained in GamePhase objects; GamePhases have a vector of lists of widgets, where each widget list represents a layer, so the order of a GamePhases’s list vector is effectively enforcing a z-order. GamePhases have update and render functions that can do phase specific rendering (e.g. draw a background) and then call the render and update methods of the widgets contained in the layers. GamePhases also have a predicate “IsPhaseComplete” and an accessor “GetNextPhase” which are used along with update and render to implement the game loop. That’s basically it as far as a game engine goes. This code clearly is not logically platform-dependent. In practice Windows leaked into the implementation in the Render method which takes an HDC as a formal parameter and elsewhere where I wasn’t being careful in avoiding Win32 types. So my initial plan on porting to iOS was to refactor the 2D game framework part of the codebase to be truly platform independent and then to find an open source 2D drawing library that someone else implemented on top of OpenGL ES and reimplement the rendering code in terms of that 2d library. The trouble is the 2D OpenGl-based library surprisingly doesn’t seem to exist. There is a project that someone did called Gles2d which is what I want but it is orphaned and never adapted to iOS anyway. It was implemented for the GamePark32 hardware, I believe. So my options are (1) Stick with the original plan and adapt the Gles2D codebase to iOS myself. (2) Stick with the original plan and write my own 2D graphics in OpenGL ES layer. (3) Throw out everything and reimplement the application to Cocos2d in ObjectiveC. (4) Keep whatever I can of my code and re-factor to use the Marmalade framework in C++. (5) Keep whatever I can of my code and re-factor to use Cocos2d-x in C++. (6) Stick with the original plan and write the platform dependent drawing stuff to SDL 1.3. Long story short, I think I’m going to do (5). (1) and (2) are just not work I feel like doing at this time. (3) would be a good solution but I’d be locked into iOS and would have to gain more competence at ObjectiveC development than I feel like investing time-wise at this point — however, I may end up doing things this way if it becomes clear that it is the easiest approach. (4) is out because I don’t think I need a very powerful game engine, Marmalade costs money, and I wouldn’t be using most of it. I’m ruling out (6) because I don’t really trust SDL 1.3 on iOS; maybe I’m wrong about this but SDL doesn’t officially support iOS and it just seems like there would be problems. So (5) … Cocos2d-x is a reimplementation of the Cocos2d API but to C++ rather than ObjectiveC. It is designed for cross-platform (i.e. across iOS and Android specifically) and the project looks alive and well. There is even a Win32 build of it that uses PowerVR’s GLES emulator for windows so I could in theory start work without actually owning a Macintosh. The only problem I see with Cocos2d-x is that documentation seems to be non-existent and it is being developed by guys who are clearly speaking English as a second language so I may have trouble finding answers to questions and so forth … we will see. Anyway, … thoughts? # Picture Round – 3/12/2012 Our picture round from trivia at the Lockspot this evening … Famous cats. PDF Answers # Syzygy for Win32, pre-pre-alpha release I’m releasing a prototype version of a puzzle game, Syzygy, that I eventually intend to port to iOS and possibly Android. The prototype is written to the Win32 API and should run on basically any Windows system without installing anything. Syzygy can be downloaded here. Just unzip these three files into a directory and run the executable. I have the Syzygy prototype parametrized such that a single XML file defines its gameplay. I’m looking for play testers who are interested in abstract puzzle games to play the game and provide feedback regarding good values for the definable parameters. If I get multiple helpful submissions I’ll give$60 via paypal to whoever has the best revised XML file. Here’s a brief explanation of the XML file.

The game is a Scrabble-like word game re-imagined as a one-player action puzzle game. Here’s a screenshot (click on the image for a full-sized version):

Basically the game works as follows:

• The bar on the left is the game timer. When it is empty the game is over.
• Letter tiles randomly appear and the player must position the tiles in a legal crossword-style crossword grid by dragging them with the mouse pointer.
• When the player has positioned tiles such that they form two or more legal connected words, the player can double-click on one of the tiles to “lock them in” and the two or more words are then scored as follows (This is a modified version the scoring used in the game Literaxx, which is the public domain Scrabble variant):
• Yellow tiles are 1 point, green tiles are 2, blue tiles are 3, and red tiles are 5
• A tile on the a board cell of matching color receives triple its point value.
• The 2x and 3x board cells are double and triple word scores.
• There are two levels of parameter controlled bonuses for long words (see the readme file in the game directory)
• The remaining time in the game timer is increased proportionally to the point value earned by a successful lock in and the player’s score is increased by the score value of a successful lock-in times a level multiplier.
• Locked in tiles can be played off of but cannot be moved.
• Each tile has a bar timer widget on its right. When this timer expire, the tile disappears negatively effecting the global timer if the tile that expires is not locked in.
• There are three kinds of special tiles
• Random tiles: Random tiles look like gray transparent letter tiles (the weird looking ‘M’ tile above is one). They cycle through the alphabet until they are dragged the first time at which point they behave like normal letter tiles with no point value.
• Bomb tiles: (pictured above) When the user drags a bomb tile onto a group of connected locked-in or non-locked-in letter tiles, the target tiles will be destroyed without effecting the user’s score or game timer.
• Juice tiles: (appear as lightening bolt icons, not shown above) When user drags a juice tile onto a group of connected locked-in or non-locked-in letter tiles, the tiles’ local timer widgets receive additional time.
• The game levels up after a certain number of tiles are locked in. The game timer is re-filled at level transitions.