Talking about Roly Poly game, Actionscript 3, Box2D, Flash and Game development.
One of the first Nitrome games was Roly Poly.
It was a nice game based loosely based on physics, it was early 2006 and developers did not have that much physics engines to rely on.
Luckily in late 2012 we have Box2D which will help us to build our version of Roly Poly, even better than the original, cute graphics excluded.
Starting with some concepts about level design, I have to say you can reproduce most of the original levels using a tile based approach, and the aim of this first part is to create a level template using Box2D and a set of tiles.
This is what you’ll get at the end of the script:
Look at what we have: the circumference and a set of tiles ready to be activated or deactivated in order to generate a level.
And this is the script which generates it:
package {
import flash.display.Sprite;
import flash.events.Event;
import Box2D.Dynamics.*;
import Box2D.Collision.*;
import Box2D.Collision.Shapes.*;
import Box2D.Common.Math.*;
public class Main extends Sprite {
private var world:b2World=new b2World(new b2Vec2(0,10),true);
private var worldScale:Number=30;
private var rolyCenter:b2Vec2=new b2Vec2(320,240);
private var rolyRadius:Number=230;
private var rolyPieces:Number=24;
private var tileSize:Number=32;
public function Main() {
var centerAngle:Number=2*Math.PI/rolyPieces;
var rolySide:Number=rolyRadius*Math.tan(centerAngle/2)/worldScale;
rolyCenter.Multiply(1/worldScale);
debugDraw();
var bodyDef:b2BodyDef=new b2BodyDef();
bodyDef.position=rolyCenter;
var rolyLevel:b2Body=world.CreateBody(bodyDef);
var polygonShape:b2PolygonShape=new b2PolygonShape();
var fixtureDef:b2FixtureDef=new b2FixtureDef();
fixtureDef.shape=polygonShape;
fixtureDef.restitution=0;
fixtureDef.friction=1;
for (var i:Number=0; i<rolyPieces; i++) {
var angle:Number=2*Math.PI/rolyPieces*i;
polygonShape.SetAsOrientedBox(0.1,rolySide,new b2Vec2(rolyRadius/worldScale*Math.cos(angle),rolyRadius/worldScale*Math.sin(angle)),angle);
rolyLevel.CreateFixture(fixtureDef);
}
var numberOfTiles:Number=Math.ceil(rolyRadius*2/tileSize);
if (numberOfTiles%2==0) {
numberOfTiles++;
}
var tileSizePX:Number=tileSize/worldScale;
for (i=0; i<numberOfTiles; i++) {
for (var j:Number=0; j<numberOfTiles; j++) {
polygonShape.SetAsOrientedBox(tileSizePX/2,tileSizePX/2,new b2Vec2(tileSizePX*(numberOfTiles/2-i)-tileSizePX/2,tileSizePX*(numberOfTiles/2-j)-tileSizePX/2),0);
rolyLevel.CreateFixture(fixtureDef);
}
}
addEventListener(Event.ENTER_FRAME,updateWorld);
}
private function debugDraw():void {
var debugDraw:b2DebugDraw=new b2DebugDraw();
var debugSprite:Sprite=new Sprite();
addChild(debugSprite);
debugDraw.SetSprite(debugSprite);
debugDraw.SetDrawScale(worldScale);
debugDraw.SetFlags(b2DebugDraw.e_shapeBit|b2DebugDraw.e_jointBit);
debugDraw.SetFillAlpha(0.5);
world.SetDebugDraw(debugDraw);
}
private function updateWorld(e:Event):void {
world.Step(1/30,10,10);
world.ClearForces();
world.DrawDebugData();
}
}
}
Let me explain the most interesting lines:
Line 11: rolyCenter
is a b2Vec2 variable storing the center of the rolling maze. As you can see I am using pixel units to make level design easier, I will convert it to Box2D meters later on.
Line 12: rolyRadius
is the radius of the rolling maze, in pixels.
Line 13: rolyPieces
is the number of segments which will be used to approximate a circumference. Box2D does not allow us to create hollow circles, so rolling maze borders will be defined by a set of rectangles. I found 24 or 32 fit our needs, anyway feel free to experiment.
Line 14: tileSize
represents the size of the tiles, in pixels.
Line 16: assuming I am dividing a circumference in rolyPieces
slices, centerAngle
is the angle represented by each slice.
Line 17: knowing the angle and the radius of the circle, I am able to determine the length of each segment which will approximate the circumference thanks to trigonometry.
Line 18: this is where I convert rolyCenter
variable declared at line 11 from pixels to meters.
Lines 20-27: Creating a body along with its shape and fixture as seen a million times in this blog.
Lines 28-32: Placing all the boxes which will approximate the circumference as a compound object. For more information about compound objects refer to this post.
Line 33: Determining the number of tiles to be placed inside the circle, according to circle radius and tile size.
Lines 34-36: I don’t want to have an even number of tiles because I always want a tile to be placed exactly at the center of the circle.
Line 37: Temporary variable just to conver the tile size in meters
Lines 38-43: Placing all tiles, as part of the same compound object started with the circumference.
And that’s all for today, net time I’ll show you how to create and animate a real level.
</rolypieces;>
Never miss an update! Subscribe, and I will bother you by email only when a new game or full source code comes out.