Most of my games are based on a similar canvas, be it a 2D platformer or a top-down game. Actually, it's interesting to note that a platformer is nothing more than a top-down engine with gravity applied to the player on every frame.
In this article, I will use the Haxe language: if you don't know it yet, it's an amazing language that can compile to many targets, including Flash, C, or iOS/Android (using NME). However, the principles here are very generic and simple, meaning that you can easily adapt to any other language.
![]() |
| Those 3 games share the exact same engine. |
I use a simple, lightweight, Entity class which does all the basics and I extend it. Pretty classic, but there are a few tricks.
Here is a simple version of this class:
class Entity {
// Graphical object
public var sprite : flash.display.Sprite;
// Base coordinates
public var cx : Int;
public var cy : Int;
public var xr : Float;
public var yr : Float;
// Resulting coordinates
public var xx : Float;
public var yy : Float;
// Movements
public var dx : Float;
public var dy : Float;
public function new() {
//...
}
public function update() {
//...
}
}
Coordinates system
First thing, I use a coordinate system focused on ease of use.I usually have a grid based logic: the level, for example, is a grid of empty cells (where the player can walk) and wall cells.
Therefore,
cx,cy are the grid coordinates. xr,yr are ratios (0 to 1.0) that represent the position inside a grid cell. Finally, xx,yy are resulting coordinates from cx,cy + xr,yr.Thinking with this system makes lots of things much easier. For example, checking collisions on the right side of an entity is trivial: just read
cx+1,cy coordinate. You can also use the xr value to check if the Entity is on the right side of its cell.We will consider from now on that with have a method
hasCollision(cx,cy) in our class that returns true if their his a collision at a given coordinate, false otherwise.if( hasCollision(cx+1,cy) && xr>=0.7 ) {
xr = 0.7; // cap xr
// ...
}
The
xx,yy coordinates are only updated at the end of the update loop. Note: in Flash, updating
sprite.x and sprite.y has a small cost: lots of things are updated internally when you change these values. That means each time you access them, matrices are updated, objects are rendered..etc. So you probably don't want to work on sprite.x directly, that's the reason I always use an intermediary: xx.It also makes cross platform dev easier as the Entity class is more about logic than graphics.
// assuming the cell size of your grid system is 16px xx = Std.int( (cx+xr) * 16 ); yy = Std.int( (cy+yr) * 16 ); sprite.x = xx; sprite.y = yy;
Also, sometimes you will need to initialize
cx,cy and xr,yr based on a xx,yy coordinate :public function setCoordinates(x,y) {
xx = x;
yy = y;
cx = Std.int(xx/16);
cy = Std.int(yy/16);
xr = (xx-cx*16) / 16;
yr = (yy-cy*16) / 16;
}
X movements
On every frame, the value dx is added toxr. If
xr becomes greater than 1 or lower than 0 (ie. the Entity is beyond the bounds of its current cell), the cx coordinate is updated accordingly.while( xr>1 ) {
xr -= 1;
cx ++;
}
while( xr<0 ) {
xr += 1;
cx --;
}
You should always apply friction to dx, to smoothly cap its value (much better results than a simple if).
dx *= 0.96;In your main loop, when the appropriate event is fired (key press or anything), you can simply change
dx to move your entity accordingly.
// hero being an Entity hero.dx = 0.1; // or hero.dx += 0.05;
X collisions
Checking and managing collisions is pretty simple:if( hasCollision(cx+1,cy) && xr>=0.7 ) {
xr = 0.7;
dx = 0; // stop movement
}
if( hasCollision(cx-1,cy) && xr<=0.3 ) {
xr = 0.3;
dx = 0;
}
X complete !
Here is the complete source code for X management. Couldn't be simpler :)xr+=dx;
dx*=0.96;
if( hasCollision(cx-1,cy) && xr<=0.3 ) {
dx = 0;
xr = 0.3;
}
if( hasCollision(cx+1,cy) && xr>=0.7 ) {
dx = 0;
xr = 0.7;
}
while( xr<0 ) {
cx--;
xr++;
}
while( xr>1 ) {
cx++;
xr--;
}
What about Y?
Mostly copy and paste. There could be a few differences though, depending on the kind of game you're making. For example, in a platformer, you may want theyr value to cap at 0.5 instead of 0.7 when a collision is detected underneath Entity feet.
yr+=dy;
dy+=0.05;
dy*=0.96;
if( hasCollision(cx,cy-1) && yr<=0.4 ) {
dy = 0;
yr = 0.4;
}
if( hasCollision(cx,cy+1) && yr>=0.5 ) {
dy = 0;
yr = 0.5;
}
while( yr<0 ) {
cy--;
yr++;
}
while( yr>1 ) {
cy++;
yr--;
}
Here is a demo of this simple engine running (source code):
import flash.display.Sprite;
class Entity {
var man : Manager;
public var sprite : Sprite;
public var cx : Int;
public var cy : Int;
public var xr : Float;
public var yr : Float;
public var dx : Float;
public var dy : Float;
public var xx : Float;
public var yy : Float;
public function new() {
man = Manager.ME;
cx = 5;
cy = 0;
xr = yr = 0.5;
dx = dy = 0;
sprite = new Sprite();
sprite.graphics.beginFill(0xFFFF00,1);
sprite.graphics.drawCircle(0,0,Const.GRID*0.5);
}
public inline function hasCollision(cx,cy) {
return
if( cx<0 || cx>=man.level.length || cy>=man.level[cx].length )
true; // out of bounds
else
man.level[cx][cy]; // check level data
}
public function update() {
var frictX = 0.92;
var frictY = 0.94;
var gravity = 0.04;
// X component
xr+=dx;
dx*=frictX;
if( hasCollision(cx-1,cy) && xr<=0.3 ) {
dx = 0;
xr = 0.3;
}
if( hasCollision(cx+1,cy) && xr>=0.7 ) {
dx = 0;
xr = 0.7;
}
while( xr<0 ) {
cx--;
xr++;
}
while( xr>1 ) {
cx++;
xr--;
}
// Y component
dy+=gravity;
yr+=dy;
dy*=frictY;
if( hasCollision(cx,cy-1) && yr<=0.4 ) {
dy = 0;
yr = 0.4;
}
if( hasCollision(cx,cy+1) && yr>=0.5 ) {
dy = 0;
yr = 0.5;
}
while( yr<0 ) {
cy--;
yr++;
}
while( yr>1 ) {
cy++;
yr--;
}
xx = Std.int((cx+xr)*Const.GRID);
yy = Std.int((cy+yr)*Const.GRID);
sprite.x = xx;
sprite.y = yy;
}
}
Don't hesitate to leave a comment if you have any question :)
Read the second part of this article.


