Talking about Actionscript 3, Box2D and Flash.
I am dealing with Box2D car for a long time, but I never published a tutorial about the making of a Box2D car, with working wheels and shocks.
If you want to see some posts about Box2D car you can have a look at two ways to make Box2D cars and create a terrain like the one in Tiny Wings with Flash and Box2D – Adding a car
We are going to build our car in 7 steps, so let’s start from the very beginning:
1 – World creation
In this basic step the environment to set up the simulation is created, and we also add the floor.
package {
import flash.display.Sprite;
import flash.events.Event;
import flash.events.KeyboardEvent;
import Box2D.Dynamics.*;
import Box2D.Collision.*;
import Box2D.Collision.Shapes.*;
import Box2D.Common.Math.*;
import Box2D.Dynamics.Joints.*;
public class Main extends Sprite {
private var world:b2World=new b2World(new b2Vec2(0,10),true);
private var worldScale:int=30;
public function Main():void {
debugDraw();
// ************************ THE FLOOR ************************ //
// shape
var floorShape:b2PolygonShape = new b2PolygonShape();
floorShape.SetAsBox(640/worldScale,10/worldScale);
// fixture
var floorFixture:b2FixtureDef = new b2FixtureDef();
floorFixture.density=0;
floorFixture.friction=3;
floorFixture.restitution=0;
floorFixture.shape=floorShape;
// body definition
var floorBodyDef:b2BodyDef = new b2BodyDef();
floorBodyDef.position.Set(320/worldScale,480/worldScale);
// the floor itself
var floor:b2Body=world.CreateBody(floorBodyDef);
floor.CreateFixture(floorFixture);
addEventListener(Event.ENTER_FRAME,updateWorld);
}
private function debugDraw():void {
var worldDebugDraw:b2DebugDraw=new b2DebugDraw();
var debugSprite:Sprite = new Sprite();
addChild(debugSprite);
worldDebugDraw.SetSprite(debugSprite);
worldDebugDraw.SetDrawScale(worldScale);
worldDebugDraw.SetFlags(b2DebugDraw.e_shapeBit|b2DebugDraw.e_jointBit);
worldDebugDraw.SetFillAlpha(0.5);
world.SetDebugDraw(worldDebugDraw);
}
private function updateWorld(e:Event):void {
world.Step(1/30,10,10);
world.ClearForces();
world.DrawDebugData();
}
}
}
There’s nothing you did not already see in this blog, so I’d jump straight to the result…
… and then move on to next step.
2 – Adding the car body
At first, the car will be just a rectangle, so that’s what I am adding:
package {
import flash.display.Sprite;
import flash.events.Event;
import flash.events.KeyboardEvent;
import Box2D.Dynamics.*;
import Box2D.Collision.*;
import Box2D.Collision.Shapes.*;
import Box2D.Common.Math.*;
import Box2D.Dynamics.Joints.*;
public class Main extends Sprite {
private var world:b2World=new b2World(new b2Vec2(0,10),true);
private var worldScale:int=30;
private var car:b2Body;
public function Main():void {
debugDraw();
// ************************ THE FLOOR ************************ //
// shape
var floorShape:b2PolygonShape = new b2PolygonShape();
floorShape.SetAsBox(640/worldScale,10/worldScale);
// fixture
var floorFixture:b2FixtureDef = new b2FixtureDef();
floorFixture.density=0;
floorFixture.friction=3;
floorFixture.restitution=0;
floorFixture.shape=floorShape;
// body definition
var floorBodyDef:b2BodyDef = new b2BodyDef();
floorBodyDef.position.Set(320/worldScale,480/worldScale);
// the floor itself
var floor:b2Body=world.CreateBody(floorBodyDef);
floor.CreateFixture(floorFixture);
// ************************ THE CAR ************************ //
// shape
var carShape:b2PolygonShape = new b2PolygonShape();
carShape.SetAsBox(120/worldScale,20/worldScale);
// fixture
var carFixture:b2FixtureDef = new b2FixtureDef();
carFixture.density=5;
carFixture.friction=3;
carFixture.restitution=0.3;
carFixture.filter.groupIndex=-1;
carFixture.shape=carShape;
// body definition
var carBodyDef:b2BodyDef = new b2BodyDef();
carBodyDef.type=b2Body.b2_dynamicBody;
carBodyDef.position.Set(320/worldScale,100/worldScale);
// the car itself
car=world.CreateBody(carBodyDef);
car.CreateFixture(carFixture);
addEventListener(Event.ENTER_FRAME,updateWorld);
}
private function debugDraw():void {
var worldDebugDraw:b2DebugDraw=new b2DebugDraw();
var debugSprite:Sprite = new Sprite();
addChild(debugSprite);
worldDebugDraw.SetSprite(debugSprite);
worldDebugDraw.SetDrawScale(worldScale);
worldDebugDraw.SetFlags(b2DebugDraw.e_shapeBit|b2DebugDraw.e_jointBit);
worldDebugDraw.SetFillAlpha(0.5);
world.SetDebugDraw(worldDebugDraw);
}
private function updateWorld(e:Event):void {
world.Step(1/30,10,10);
world.ClearForces();
world.DrawDebugData();
}
}
}
At lines 32-49 a rectangle is added on the world. Here it is:
Since the world is full of cars made by just one rectangle, it’s time to improve our car design.
3 – Adding car parts
Here comes into play Box2D’s capability of creating complex objects by joining simple primitives into compound objects. You can read the theory behind this at the magic of compound objects with Box2D.
package {
import flash.display.Sprite;
import flash.events.Event;
import flash.events.KeyboardEvent;
import Box2D.Dynamics.*;
import Box2D.Collision.*;
import Box2D.Collision.Shapes.*;
import Box2D.Common.Math.*;
import Box2D.Dynamics.Joints.*;
public class Main extends Sprite {
private var world:b2World=new b2World(new b2Vec2(0,10),true);
private var worldScale:int=30;
private var car:b2Body;
public function Main():void {
debugDraw();
// ************************ THE FLOOR ************************ //
// shape
var floorShape:b2PolygonShape = new b2PolygonShape();
floorShape.SetAsBox(640/worldScale,10/worldScale);
// fixture
var floorFixture:b2FixtureDef = new b2FixtureDef();
floorFixture.density=0;
floorFixture.friction=3;
floorFixture.restitution=0;
floorFixture.shape=floorShape;
// body definition
var floorBodyDef:b2BodyDef = new b2BodyDef();
floorBodyDef.position.Set(320/worldScale,480/worldScale);
// the floor itself
var floor:b2Body=world.CreateBody(floorBodyDef);
floor.CreateFixture(floorFixture);
// ************************ THE CAR ************************ //
// shape
var carShape:b2PolygonShape = new b2PolygonShape();
carShape.SetAsBox(120/worldScale,20/worldScale);
// fixture
var carFixture:b2FixtureDef = new b2FixtureDef();
carFixture.density=5;
carFixture.friction=3;
carFixture.restitution=0.3;
carFixture.filter.groupIndex=-1;
carFixture.shape=carShape;
// body definition
var carBodyDef:b2BodyDef = new b2BodyDef();
carBodyDef.type=b2Body.b2_dynamicBody;
carBodyDef.position.Set(320/worldScale,100/worldScale);
// ************************ THE TRUNK ************************ //
// shape
var trunkShape:b2PolygonShape = new b2PolygonShape();
trunkShape.SetAsOrientedBox(40/worldScale,40/worldScale,new b2Vec2(-80/worldScale,-60/worldScale));
// fixture
var trunkFixture:b2FixtureDef = new b2FixtureDef();
trunkFixture.density=1;
trunkFixture.friction=3;
trunkFixture.restitution=0.3;
trunkFixture.filter.groupIndex=-1;
trunkFixture.shape=trunkShape;
// ************************ THE HOOD ************************ //
// shape
var hoodShape:b2PolygonShape = new b2PolygonShape();
var cartVector:Vector.=new Vector.();
cartVector[0]=new b2Vec2(-40/worldScale,-20/worldScale);
cartVector[1]=new b2Vec2(-40/worldScale,-100/worldScale);
cartVector[2]=new b2Vec2(120/worldScale,-20/worldScale);
hoodShape.SetAsVector(cartVector,3);
// fixture
var hoodFixture:b2FixtureDef = new b2FixtureDef();
hoodFixture.density=1;
hoodFixture.friction=3;
hoodFixture.restitution=0.3;
hoodFixture.filter.groupIndex=-1;
hoodFixture.shape=hoodShape;
// ************************ MERGING ALL TOGETHER ************************ //
// the car itself
car=world.CreateBody(carBodyDef);
car.CreateFixture(carFixture);
car.CreateFixture(trunkFixture);
car.CreateFixture(hoodFixture);
addEventListener(Event.ENTER_FRAME,updateWorld);
}
private function debugDraw():void {
var worldDebugDraw:b2DebugDraw=new b2DebugDraw();
var debugSprite:Sprite = new Sprite();
addChild(debugSprite);
worldDebugDraw.SetSprite(debugSprite);
worldDebugDraw.SetDrawScale(worldScale);
worldDebugDraw.SetFlags(b2DebugDraw.e_shapeBit|b2DebugDraw.e_jointBit);
worldDebugDraw.SetFillAlpha(0.5);
world.SetDebugDraw(worldDebugDraw);
}
private function updateWorld(e:Event):void {
world.Step(1/30,10,10);
world.ClearForces();
world.DrawDebugData();
}
}
}
We just added a trunk and an hood to the car body. Look how lines 76-78 add car body, trunk and hood to the same b2Body
rather than having their own individual b2Body
. That’s how you make compound objects.
Also, the hood (a triangle) is created from a vector of points, in the same way slices are made in slicing, splitting and cutting objects with Box2D – part 2.
Now the car itself is ready.
4 – Adding shocks
To simulate shocks, first we need to create axles. Then wheels will be attached to axles, which will be attached to the car body, with some kind of spring effect.
So, at this time, axles are just a pair of boxes (actually they can have almost any shape, I am using boxes for the sake of simplicity).
package {
import flash.display.Sprite;
import flash.events.Event;
import flash.events.KeyboardEvent;
import Box2D.Dynamics.*;
import Box2D.Collision.*;
import Box2D.Collision.Shapes.*;
import Box2D.Common.Math.*;
import Box2D.Dynamics.Joints.*;
public class Main extends Sprite {
private var world:b2World=new b2World(new b2Vec2(0,10),true);
private var worldScale:int=30;
private var car:b2Body;
public function Main():void {
debugDraw();
// ************************ THE FLOOR ************************ //
// shape
var floorShape:b2PolygonShape = new b2PolygonShape();
floorShape.SetAsBox(640/worldScale,10/worldScale);
// fixture
var floorFixture:b2FixtureDef = new b2FixtureDef();
floorFixture.density=0;
floorFixture.friction=3;
floorFixture.restitution=0;
floorFixture.shape=floorShape;
// body definition
var floorBodyDef:b2BodyDef = new b2BodyDef();
floorBodyDef.position.Set(320/worldScale,480/worldScale);
// the floor itself
var floor:b2Body=world.CreateBody(floorBodyDef);
floor.CreateFixture(floorFixture);
// ************************ THE CAR ************************ //
// shape
var carShape:b2PolygonShape = new b2PolygonShape();
carShape.SetAsBox(120/worldScale,20/worldScale);
// fixture
var carFixture:b2FixtureDef = new b2FixtureDef();
carFixture.density=5;
carFixture.friction=3;
carFixture.restitution=0.3;
carFixture.filter.groupIndex=-1;
carFixture.shape=carShape;
// body definition
var carBodyDef:b2BodyDef = new b2BodyDef();
carBodyDef.type=b2Body.b2_dynamicBody;
carBodyDef.position.Set(320/worldScale,100/worldScale);
// ************************ THE TRUNK ************************ //
// shape
var trunkShape:b2PolygonShape = new b2PolygonShape();
trunkShape.SetAsOrientedBox(40/worldScale,40/worldScale,new b2Vec2(-80/worldScale,-60/worldScale));
// fixture
var trunkFixture:b2FixtureDef = new b2FixtureDef();
trunkFixture.density=1;
trunkFixture.friction=3;
trunkFixture.restitution=0.3;
trunkFixture.filter.groupIndex=-1;
trunkFixture.shape=trunkShape;
// ************************ THE HOOD ************************ //
// shape
var hoodShape:b2PolygonShape = new b2PolygonShape();
var carVector:Vector.=new Vector.();
carVector[0]=new b2Vec2(-40/worldScale,-20/worldScale);
carVector[1]=new b2Vec2(-40/worldScale,-100/worldScale);
carVector[2]=new b2Vec2(120/worldScale,-20/worldScale);
hoodShape.SetAsVector(carVector,3);
// fixture
var hoodFixture:b2FixtureDef = new b2FixtureDef();
hoodFixture.density=1;
hoodFixture.friction=3;
hoodFixture.restitution=0.3;
hoodFixture.filter.groupIndex=-1;
hoodFixture.shape=hoodShape;
// ************************ MERGING ALL TOGETHER ************************ //
// the car itself
car=world.CreateBody(carBodyDef);
car.CreateFixture(carFixture);
car.CreateFixture(trunkFixture);
car.CreateFixture(hoodFixture);
// ************************ THE AXLES ************************ //
// shape
var axleShape:b2PolygonShape = new b2PolygonShape();
axleShape.SetAsBox(20/worldScale,20/worldScale);
// fixture
var axleFixture:b2FixtureDef = new b2FixtureDef();
axleFixture.density=0.5;
axleFixture.friction=3;
axleFixture.restitution=0.3;
axleFixture.shape=axleShape;
axleFixture.filter.groupIndex=-1;
// body definition
var axleBodyDef:b2BodyDef = new b2BodyDef();
axleBodyDef.type=b2Body.b2_dynamicBody;
// the rear axle itself
axleBodyDef.position.Set(car.GetWorldCenter().x-(60/worldScale),car.GetWorldCenter().y+(65/worldScale));
var rearAxle:b2Body=world.CreateBody(axleBodyDef);
rearAxle.CreateFixture(axleFixture);
// the front axle itself
axleBodyDef.position.Set(car.GetWorldCenter().x+(75/worldScale),car.GetWorldCenter().y+(65/worldScale));
var frontAxle:b2Body=world.CreateBody(axleBodyDef);
frontAxle.CreateFixture(axleFixture);
addEventListener(Event.ENTER_FRAME,updateWorld);
}
private function debugDraw():void {
var worldDebugDraw:b2DebugDraw=new b2DebugDraw();
var debugSprite:Sprite = new Sprite();
addChild(debugSprite);
worldDebugDraw.SetSprite(debugSprite);
worldDebugDraw.SetDrawScale(worldScale);
worldDebugDraw.SetFlags(b2DebugDraw.e_shapeBit|b2DebugDraw.e_jointBit);
worldDebugDraw.SetFillAlpha(0.5);
world.SetDebugDraw(worldDebugDraw);
}
private function updateWorld(e:Event):void {
world.Step(1/30,10,10);
world.ClearForces();
world.DrawDebugData();
}
}
}
Although I just added a couple of boxes, I would like you to see the magic of this step:
Axles and car body do not collide among themselves because they all have the same groupIndex
(lines 41, 56, 71 and 89).
You can find the theory behind groupIndex
at Introducing Box2D filtering.
5 – Adding wheels
In the same way I added axles, I am adding wheels. All in all, they are just two circles with the same groupIndex
as the rest of the car parts.
package {
import flash.display.Sprite;
import flash.events.Event;
import flash.events.KeyboardEvent;
import Box2D.Dynamics.*;
import Box2D.Collision.*;
import Box2D.Collision.Shapes.*;
import Box2D.Common.Math.*;
import Box2D.Dynamics.Joints.*;
public class Main extends Sprite {
private var world:b2World=new b2World(new b2Vec2(0,10),true);
private var worldScale:int=30;
private var car:b2Body;
public function Main():void {
debugDraw();
// ************************ THE FLOOR ************************ //
// shape
var floorShape:b2PolygonShape = new b2PolygonShape();
floorShape.SetAsBox(640/worldScale,10/worldScale);
// fixture
var floorFixture:b2FixtureDef = new b2FixtureDef();
floorFixture.density=0;
floorFixture.friction=3;
floorFixture.restitution=0;
floorFixture.shape=floorShape;
// body definition
var floorBodyDef:b2BodyDef = new b2BodyDef();
floorBodyDef.position.Set(320/worldScale,480/worldScale);
// the floor itself
var floor:b2Body=world.CreateBody(floorBodyDef);
floor.CreateFixture(floorFixture);
// ************************ THE CAR ************************ //
// shape
var carShape:b2PolygonShape = new b2PolygonShape();
carShape.SetAsBox(120/worldScale,20/worldScale);
// fixture
var carFixture:b2FixtureDef = new b2FixtureDef();
carFixture.density=5;
carFixture.friction=3;
carFixture.restitution=0.3;
carFixture.filter.groupIndex=-1;
carFixture.shape=carShape;
// body definition
var carBodyDef:b2BodyDef = new b2BodyDef();
carBodyDef.type=b2Body.b2_dynamicBody;
carBodyDef.position.Set(320/worldScale,100/worldScale);
// ************************ THE TRUNK ************************ //
// shape
var trunkShape:b2PolygonShape = new b2PolygonShape();
trunkShape.SetAsOrientedBox(40/worldScale,40/worldScale,new b2Vec2(-80/worldScale,-60/worldScale));
// fixture
var trunkFixture:b2FixtureDef = new b2FixtureDef();
trunkFixture.density=1;
trunkFixture.friction=3;
trunkFixture.restitution=0.3;
trunkFixture.filter.groupIndex=-1;
trunkFixture.shape=trunkShape;
// ************************ THE HOOD ************************ //
// shape
var hoodShape:b2PolygonShape = new b2PolygonShape();
var carVector:Vector.=new Vector.();
carVector[0]=new b2Vec2(-40/worldScale,-20/worldScale);
carVector[1]=new b2Vec2(-40/worldScale,-100/worldScale);
carVector[2]=new b2Vec2(120/worldScale,-20/worldScale);
hoodShape.SetAsVector(carVector,3);
// fixture
var hoodFixture:b2FixtureDef = new b2FixtureDef();
hoodFixture.density=1;
hoodFixture.friction=3;
hoodFixture.restitution=0.3;
hoodFixture.filter.groupIndex=-1;
hoodFixture.shape=hoodShape;
// ************************ MERGING ALL TOGETHER ************************ //
// the car itself
car=world.CreateBody(carBodyDef);
car.CreateFixture(carFixture);
car.CreateFixture(trunkFixture);
car.CreateFixture(hoodFixture);
// ************************ THE AXLES ************************ //
// shape
var axleShape:b2PolygonShape = new b2PolygonShape();
axleShape.SetAsBox(20/worldScale,20/worldScale);
// fixture
var axleFixture:b2FixtureDef = new b2FixtureDef();
axleFixture.density=0.5;
axleFixture.friction=3;
axleFixture.restitution=0.3;
axleFixture.shape=axleShape;
axleFixture.filter.groupIndex=-1;
// body definition
var axleBodyDef:b2BodyDef = new b2BodyDef();
axleBodyDef.type=b2Body.b2_dynamicBody;
// the rear axle itself
axleBodyDef.position.Set(car.GetWorldCenter().x-(60/worldScale),car.GetWorldCenter().y+(65/worldScale));
var rearAxle:b2Body=world.CreateBody(axleBodyDef);
rearAxle.CreateFixture(axleFixture);
// the front axle itself
axleBodyDef.position.Set(car.GetWorldCenter().x+(75/worldScale),car.GetWorldCenter().y+(65/worldScale));
var frontAxle:b2Body=world.CreateBody(axleBodyDef);
frontAxle.CreateFixture(axleFixture);
// ************************ THE WHEELS ************************ //
// shape
var wheelShape:b2CircleShape=new b2CircleShape(40/worldScale);
// fixture
var wheelFixture:b2FixtureDef = new b2FixtureDef();
wheelFixture.density=1;
wheelFixture.friction=3;
wheelFixture.restitution=0.1;
wheelFixture.filter.groupIndex=-1;
wheelFixture.shape=wheelShape;
// body definition
var wheelBodyDef:b2BodyDef = new b2BodyDef();
wheelBodyDef.type=b2Body.b2_dynamicBody;
// the real wheel itself
wheelBodyDef.position.Set(car.GetWorldCenter().x-(60/worldScale),car.GetWorldCenter().y+(65/worldScale));
var rearWheel:b2Body=world.CreateBody(wheelBodyDef);
rearWheel.CreateFixture(wheelFixture);
// the front wheel itself
wheelBodyDef.position.Set(car.GetWorldCenter().x+(75/worldScale),car.GetWorldCenter().y+(65/worldScale));
var frontWheel:b2Body=world.CreateBody(wheelBodyDef);
frontWheel.CreateFixture(wheelFixture);
addEventListener(Event.ENTER_FRAME,updateWorld);
}
private function debugDraw():void {
var worldDebugDraw:b2DebugDraw=new b2DebugDraw();
var debugSprite:Sprite = new Sprite();
addChild(debugSprite);
worldDebugDraw.SetSprite(debugSprite);
worldDebugDraw.SetDrawScale(worldScale);
worldDebugDraw.SetFlags(b2DebugDraw.e_shapeBit|b2DebugDraw.e_jointBit);
worldDebugDraw.SetFillAlpha(0.5);
world.SetDebugDraw(worldDebugDraw);
}
private function updateWorld(e:Event):void {
world.Step(1/30,10,10);
world.ClearForces();
world.DrawDebugData();
}
}
}
And here is our car:
Now, it’s time to use joints to connect single car parts and motors to bring them to life:
6 – Attaching wheels to axles
Most of the new code in this script is used to manage keyboard interaction. Now with LEFT and RIGHT arrow keys you can make wheels “run”.
package {
import flash.display.Sprite;
import flash.events.Event;
import flash.events.KeyboardEvent;
import Box2D.Dynamics.*;
import Box2D.Collision.*;
import Box2D.Collision.Shapes.*;
import Box2D.Common.Math.*;
import Box2D.Dynamics.Joints.*;
public class Main extends Sprite {
private var world:b2World=new b2World(new b2Vec2(0,10),true);
private var worldScale:int=30;
private var car:b2Body;
private var rearWheelRevoluteJoint:b2RevoluteJoint;
private var frontWheelRevoluteJoint:b2RevoluteJoint;
private var left:Boolean=false;
private var right:Boolean=false;
private var motorSpeed:Number=0;
public function Main():void {
debugDraw();
// ************************ THE FLOOR ************************ //
// shape
var floorShape:b2PolygonShape = new b2PolygonShape();
floorShape.SetAsBox(640/worldScale,10/worldScale);
// fixture
var floorFixture:b2FixtureDef = new b2FixtureDef();
floorFixture.density=0;
floorFixture.friction=3;
floorFixture.restitution=0;
floorFixture.shape=floorShape;
// body definition
var floorBodyDef:b2BodyDef = new b2BodyDef();
floorBodyDef.position.Set(320/worldScale,480/worldScale);
// the floor itself
var floor:b2Body=world.CreateBody(floorBodyDef);
floor.CreateFixture(floorFixture);
// ************************ THE CAR ************************ //
// shape
var carShape:b2PolygonShape = new b2PolygonShape();
carShape.SetAsBox(120/worldScale,20/worldScale);
// fixture
var carFixture:b2FixtureDef = new b2FixtureDef();
carFixture.density=5;
carFixture.friction=3;
carFixture.restitution=0.3;
carFixture.filter.groupIndex=-1;
carFixture.shape=carShape;
// body definition
var carBodyDef:b2BodyDef = new b2BodyDef();
carBodyDef.type=b2Body.b2_dynamicBody;
carBodyDef.position.Set(320/worldScale,100/worldScale);
// ************************ THE TRUNK ************************ //
// shape
var trunkShape:b2PolygonShape = new b2PolygonShape();
trunkShape.SetAsOrientedBox(40/worldScale,40/worldScale,new b2Vec2(-80/worldScale,-60/worldScale));
// fixture
var trunkFixture:b2FixtureDef = new b2FixtureDef();
trunkFixture.density=1;
trunkFixture.friction=3;
trunkFixture.restitution=0.3;
trunkFixture.filter.groupIndex=-1;
trunkFixture.shape=trunkShape;
// ************************ THE HOOD ************************ //
// shape
var hoodShape:b2PolygonShape = new b2PolygonShape();
var carVector:Vector.=new Vector.();
carVector[0]=new b2Vec2(-40/worldScale,-20/worldScale);
carVector[1]=new b2Vec2(-40/worldScale,-100/worldScale);
carVector[2]=new b2Vec2(120/worldScale,-20/worldScale);
hoodShape.SetAsVector(carVector,3);
// fixture
var hoodFixture:b2FixtureDef = new b2FixtureDef();
hoodFixture.density=1;
hoodFixture.friction=3;
hoodFixture.restitution=0.3;
hoodFixture.filter.groupIndex=-1;
hoodFixture.shape=hoodShape;
// ************************ MERGING ALL TOGETHER ************************ //
// the car itself
car=world.CreateBody(carBodyDef);
car.CreateFixture(carFixture);
car.CreateFixture(trunkFixture);
car.CreateFixture(hoodFixture);
// ************************ THE AXLES ************************ //
// shape
var axleShape:b2PolygonShape = new b2PolygonShape();
axleShape.SetAsBox(20/worldScale,20/worldScale);
// fixture
var axleFixture:b2FixtureDef = new b2FixtureDef();
axleFixture.density=0.5;
axleFixture.friction=3;
axleFixture.restitution=0.3;
axleFixture.shape=axleShape;
axleFixture.filter.groupIndex=-1;
// body definition
var axleBodyDef:b2BodyDef = new b2BodyDef();
axleBodyDef.type=b2Body.b2_dynamicBody;
// the rear axle itself
axleBodyDef.position.Set(car.GetWorldCenter().x-(60/worldScale),car.GetWorldCenter().y+(65/worldScale));
var rearAxle:b2Body=world.CreateBody(axleBodyDef);
rearAxle.CreateFixture(axleFixture);
// the front axle itself
axleBodyDef.position.Set(car.GetWorldCenter().x+(75/worldScale),car.GetWorldCenter().y+(65/worldScale));
var frontAxle:b2Body=world.CreateBody(axleBodyDef);
frontAxle.CreateFixture(axleFixture);
// ************************ THE WHEELS ************************ //
// shape
var wheelShape:b2CircleShape=new b2CircleShape(40/worldScale);
// fixture
var wheelFixture:b2FixtureDef = new b2FixtureDef();
wheelFixture.density=1;
wheelFixture.friction=3;
wheelFixture.restitution=0.1;
wheelFixture.filter.groupIndex=-1;
wheelFixture.shape=wheelShape;
// body definition
var wheelBodyDef:b2BodyDef = new b2BodyDef();
wheelBodyDef.type=b2Body.b2_dynamicBody;
// the real wheel itself
wheelBodyDef.position.Set(car.GetWorldCenter().x-(60/worldScale),car.GetWorldCenter().y+(65/worldScale));
var rearWheel:b2Body=world.CreateBody(wheelBodyDef);
rearWheel.CreateFixture(wheelFixture);
// the front wheel itself
wheelBodyDef.position.Set(car.GetWorldCenter().x+(75/worldScale),car.GetWorldCenter().y+(65/worldScale));
var frontWheel:b2Body=world.CreateBody(wheelBodyDef);
frontWheel.CreateFixture(wheelFixture);
// ************************ REVOLUTE JOINTS ************************ //
// rear joint
var rearWheelRevoluteJointDef:b2RevoluteJointDef=new b2RevoluteJointDef();
rearWheelRevoluteJointDef.Initialize(rearWheel,rearAxle,rearWheel.GetWorldCenter());
rearWheelRevoluteJointDef.enableMotor=true;
rearWheelRevoluteJointDef.maxMotorTorque=10000;
rearWheelRevoluteJoint=world.CreateJoint(rearWheelRevoluteJointDef) as b2RevoluteJoint;
// front joint
var frontWheelRevoluteJointDef:b2RevoluteJointDef=new b2RevoluteJointDef();
frontWheelRevoluteJointDef.Initialize(frontWheel,frontAxle,frontWheel.GetWorldCenter());
frontWheelRevoluteJointDef.enableMotor=true;
frontWheelRevoluteJointDef.maxMotorTorque=10000;
frontWheelRevoluteJoint=world.CreateJoint(frontWheelRevoluteJointDef) as b2RevoluteJoint;
addEventListener(Event.ENTER_FRAME,updateWorld);
stage.addEventListener(KeyboardEvent.KEY_DOWN,keyPressed);
stage.addEventListener(KeyboardEvent.KEY_UP,keyReleased);
}
private function debugDraw():void {
var worldDebugDraw:b2DebugDraw=new b2DebugDraw();
var debugSprite:Sprite = new Sprite();
addChild(debugSprite);
worldDebugDraw.SetSprite(debugSprite);
worldDebugDraw.SetDrawScale(worldScale);
worldDebugDraw.SetFlags(b2DebugDraw.e_shapeBit|b2DebugDraw.e_jointBit);
worldDebugDraw.SetFillAlpha(0.5);
world.SetDebugDraw(worldDebugDraw);
}
private function keyPressed(e:KeyboardEvent):void {
switch (e.keyCode) {
case 37 :
left=true;
break;
case 39 :
right=true;
break;
}
}
private function keyReleased(e:KeyboardEvent):void {
switch (e.keyCode) {
case 37 :
left=false;
break;
case 39 :
right=false;
break;
}
}
private function updateWorld(e:Event):void {
if (left) {
motorSpeed+=0.5;
}
if (right) {
motorSpeed-=0.5;
}
motorSpeed*=0.99;
if (motorSpeed>100) {
motorSpeed=100;
}
rearWheelRevoluteJoint.SetMotorSpeed(motorSpeed);
frontWheelRevoluteJoint.SetMotorSpeed(motorSpeed);
world.Step(1/30,10,10);
world.ClearForces();
world.DrawDebugData();
}
}
}
Anyway, the interesting thing lies in the use of revolute joints (theory: Box2D joints: Revolute Joint) to attach the wheels to axles and motors (theory: Box2D joints: Revolute Joint – Building motors) to make wheels move.
maxMotorTorque
property defines the maximum motor torque used to achieve the desired motor speed, while SetMotorSpeed
method sets the motor speed in radians per second.
Press LEFT and RIGHT arrow keys to make wheels roll. Now, the last step.
7 – Attaching axles to car body
We will use prismatic joints to attach axles to the car. You can find the theory at Box2D joints: Prismatic Joints.
package {
import flash.display.Sprite;
import flash.events.Event;
import flash.events.KeyboardEvent;
import Box2D.Dynamics.*;
import Box2D.Collision.*;
import Box2D.Collision.Shapes.*;
import Box2D.Common.Math.*;
import Box2D.Dynamics.Joints.*;
public class Main extends Sprite {
private var world:b2World=new b2World(new b2Vec2(0,10),true);
private var worldScale:int=30;
private var car:b2Body;
private var rearWheelRevoluteJoint:b2RevoluteJoint;
private var frontWheelRevoluteJoint:b2RevoluteJoint;
private var left:Boolean=false;
private var right:Boolean=false;
private var motorSpeed:Number=0;
private var frontAxlePrismaticJoint:b2PrismaticJoint;
private var rearAxlePrismaticJoint:b2PrismaticJoint;
public function Main():void {
debugDraw();
// ************************ THE FLOOR ************************ //
// shape
var floorShape:b2PolygonShape = new b2PolygonShape();
floorShape.SetAsBox(640/worldScale,10/worldScale);
// fixture
var floorFixture:b2FixtureDef = new b2FixtureDef();
floorFixture.density=0;
floorFixture.friction=3;
floorFixture.restitution=0;
floorFixture.shape=floorShape;
// body definition
var floorBodyDef:b2BodyDef = new b2BodyDef();
floorBodyDef.position.Set(320/worldScale,480/worldScale);
// the floor itself
var floor:b2Body=world.CreateBody(floorBodyDef);
floor.CreateFixture(floorFixture);
// ************************ THE CAR ************************ //
// shape
var carShape:b2PolygonShape = new b2PolygonShape();
carShape.SetAsBox(120/worldScale,20/worldScale);
// fixture
var carFixture:b2FixtureDef = new b2FixtureDef();
carFixture.density=5;
carFixture.friction=3;
carFixture.restitution=0.3;
carFixture.filter.groupIndex=-1;
carFixture.shape=carShape;
// body definition
var carBodyDef:b2BodyDef = new b2BodyDef();
carBodyDef.type=b2Body.b2_dynamicBody;
carBodyDef.position.Set(320/worldScale,100/worldScale);
// ************************ THE TRUNK ************************ //
// shape
var trunkShape:b2PolygonShape = new b2PolygonShape();
trunkShape.SetAsOrientedBox(40/worldScale,40/worldScale,new b2Vec2(-80/worldScale,-60/worldScale));
// fixture
var trunkFixture:b2FixtureDef = new b2FixtureDef();
trunkFixture.density=1;
trunkFixture.friction=3;
trunkFixture.restitution=0.3;
trunkFixture.filter.groupIndex=-1;
trunkFixture.shape=trunkShape;
// ************************ THE HOOD ************************ //
// shape
var hoodShape:b2PolygonShape = new b2PolygonShape();
var carVector:Vector.=new Vector.();
carVector[0]=new b2Vec2(-40/worldScale,-20/worldScale);
carVector[1]=new b2Vec2(-40/worldScale,-100/worldScale);
carVector[2]=new b2Vec2(120/worldScale,-20/worldScale);
hoodShape.SetAsVector(carVector,3);
// fixture
var hoodFixture:b2FixtureDef = new b2FixtureDef();
hoodFixture.density=1;
hoodFixture.friction=3;
hoodFixture.restitution=0.3;
hoodFixture.filter.groupIndex=-1;
hoodFixture.shape=hoodShape;
// ************************ MERGING ALL TOGETHER ************************ //
// the car itself
car=world.CreateBody(carBodyDef);
car.CreateFixture(carFixture);
car.CreateFixture(trunkFixture);
car.CreateFixture(hoodFixture);
// ************************ THE AXLES ************************ //
// shape
var axleShape:b2PolygonShape = new b2PolygonShape();
axleShape.SetAsBox(20/worldScale,20/worldScale);
// fixture
var axleFixture:b2FixtureDef = new b2FixtureDef();
axleFixture.density=0.5;
axleFixture.friction=3;
axleFixture.restitution=0.3;
axleFixture.shape=axleShape;
axleFixture.filter.groupIndex=-1;
// body definition
var axleBodyDef:b2BodyDef = new b2BodyDef();
axleBodyDef.type=b2Body.b2_dynamicBody;
// the rear axle itself
axleBodyDef.position.Set(car.GetWorldCenter().x-(60/worldScale),car.GetWorldCenter().y+(65/worldScale));
var rearAxle:b2Body=world.CreateBody(axleBodyDef);
rearAxle.CreateFixture(axleFixture);
// the front axle itself
axleBodyDef.position.Set(car.GetWorldCenter().x+(75/worldScale),car.GetWorldCenter().y+(65/worldScale));
var frontAxle:b2Body=world.CreateBody(axleBodyDef);
frontAxle.CreateFixture(axleFixture);
// ************************ THE WHEELS ************************ //
// shape
var wheelShape:b2CircleShape=new b2CircleShape(40/worldScale);
// fixture
var wheelFixture:b2FixtureDef = new b2FixtureDef();
wheelFixture.density=1;
wheelFixture.friction=3;
wheelFixture.restitution=0.1;
wheelFixture.filter.groupIndex=-1;
wheelFixture.shape=wheelShape;
// body definition
var wheelBodyDef:b2BodyDef = new b2BodyDef();
wheelBodyDef.type=b2Body.b2_dynamicBody;
// the real wheel itself
wheelBodyDef.position.Set(car.GetWorldCenter().x-(60/worldScale),car.GetWorldCenter().y+(65/worldScale));
var rearWheel:b2Body=world.CreateBody(wheelBodyDef);
rearWheel.CreateFixture(wheelFixture);
// the front wheel itself
wheelBodyDef.position.Set(car.GetWorldCenter().x+(75/worldScale),car.GetWorldCenter().y+(65/worldScale));
var frontWheel:b2Body=world.CreateBody(wheelBodyDef);
frontWheel.CreateFixture(wheelFixture);
// ************************ REVOLUTE JOINTS ************************ //
// rear joint
var rearWheelRevoluteJointDef:b2RevoluteJointDef=new b2RevoluteJointDef();
rearWheelRevoluteJointDef.Initialize(rearWheel,rearAxle,rearWheel.GetWorldCenter());
rearWheelRevoluteJointDef.enableMotor=true;
rearWheelRevoluteJointDef.maxMotorTorque=10000;
rearWheelRevoluteJoint=world.CreateJoint(rearWheelRevoluteJointDef) as b2RevoluteJoint;
// front joint
var frontWheelRevoluteJointDef:b2RevoluteJointDef=new b2RevoluteJointDef();
frontWheelRevoluteJointDef.Initialize(frontWheel,frontAxle,frontWheel.GetWorldCenter());
frontWheelRevoluteJointDef.enableMotor=true;
frontWheelRevoluteJointDef.maxMotorTorque=10000;
frontWheelRevoluteJoint=world.CreateJoint(frontWheelRevoluteJointDef) as b2RevoluteJoint;
// ************************ PRISMATIC JOINTS ************************ //
// definition
var axlePrismaticJointDef:b2PrismaticJointDef=new b2PrismaticJointDef();
axlePrismaticJointDef.lowerTranslation=-20/worldScale;
axlePrismaticJointDef.upperTranslation=5/worldScale;
axlePrismaticJointDef.enableLimit=true;
axlePrismaticJointDef.enableMotor=true;
// front axle
axlePrismaticJointDef.Initialize(car,frontAxle,frontAxle.GetWorldCenter(),new b2Vec2(0,1));
frontAxlePrismaticJoint=world.CreateJoint(axlePrismaticJointDef) as b2PrismaticJoint;
// rear axle
axlePrismaticJointDef.Initialize(car,rearAxle,rearAxle.GetWorldCenter(),new b2Vec2(0,1));
rearAxlePrismaticJoint=world.CreateJoint(axlePrismaticJointDef) as b2PrismaticJoint;
addEventListener(Event.ENTER_FRAME,updateWorld);
stage.addEventListener(KeyboardEvent.KEY_DOWN,keyPressed);
stage.addEventListener(KeyboardEvent.KEY_UP,keyReleased);
}
private function debugDraw():void {
var worldDebugDraw:b2DebugDraw=new b2DebugDraw();
var debugSprite:Sprite = new Sprite();
addChild(debugSprite);
worldDebugDraw.SetSprite(debugSprite);
worldDebugDraw.SetDrawScale(worldScale);
worldDebugDraw.SetFlags(b2DebugDraw.e_shapeBit|b2DebugDraw.e_jointBit);
worldDebugDraw.SetFillAlpha(0.5);
world.SetDebugDraw(worldDebugDraw);
}
private function keyPressed(e:KeyboardEvent):void {
switch (e.keyCode) {
case 37 :
left=true;
break;
case 39 :
right=true;
break;
}
}
private function keyReleased(e:KeyboardEvent):void {
switch (e.keyCode) {
case 37 :
left=false;
break;
case 39 :
right=false;
break;
}
}
private function updateWorld(e:Event):void {
if (left) {
motorSpeed+=0.5;
}
if (right) {
motorSpeed-=0.5;
}
motorSpeed*=0.99;
if (motorSpeed>100) {
motorSpeed=100;
}
rearWheelRevoluteJoint.SetMotorSpeed(motorSpeed);
frontWheelRevoluteJoint.SetMotorSpeed(motorSpeed);
frontAxlePrismaticJoint.SetMaxMotorForce(Math.abs(600*frontAxlePrismaticJoint.GetJointTranslation()));
frontAxlePrismaticJoint.SetMotorSpeed((frontAxlePrismaticJoint.GetMotorSpeed()-2*frontAxlePrismaticJoint.GetJointTranslation()));
rearAxlePrismaticJoint.SetMaxMotorForce(Math.abs(600*rearAxlePrismaticJoint.GetJointTranslation()));
rearAxlePrismaticJoint.SetMotorSpeed((rearAxlePrismaticJoint.GetMotorSpeed()-2*rearAxlePrismaticJoint.GetJointTranslation()));
world.Step(1/30,10,10);
world.ClearForces();
world.DrawDebugData();
}
}
}
Prismatic joints can act like springs if you play with their motor properties. You will find how to create your custom car after this final result:
Here shocks bounce a lot because I wanted to show your how does everything work.
Customize your car
Here is a list of parameters you can change to create your custom car:
Car mass: modifying the density of the car body, the trunk and the hood (lines 45, 60 and 75) you will change the car mass, its weight and eventually its center of gravity if you make (example) an heavy trunk and a light hood.
Axles position: can be changed at lines 101 and 105. Normally a long distance between front and rear axle makes the car more stable.
Wheels radius: can be changed at line 110.
Wheels motor torque: you can change it at lines 134 and 140. The higher the torque, the lower the time to bring wheels to desired speed.
Wheels speed: at lines 200-201 you can set the speed of the wheels.
Shocks orientation: at lines 150 and 153 you can change shocks orientation. It’s the last argument.
Shocks excursion: changing lines 145-146 will affect prismatic joints upper and lower limits, modifying shocks excursion.
Shocks feedback: shocks motor speed and max force (just like wheels speed and max torque) are updated at lines 202-205 in real time at every frame to simulate the spring effect.
Can you find a good mix of these parameters to create a well responsive car?
Never miss an update! Subscribe, and I will bother you by email only when a new game or full source code comes out.