Simulate radial gravity (also known as “planet gravity”) with Box2D as seen on Angry Birds Space
With the launch of Angry Birds Space I am sure you are wondering how to simulate planet gravity with Box2D.
And guess what… the basics are very easy.
First, in space there’s no gravity, so you will create a b2World world without gravity this way:
|
1 |
private var world:b2World=new b2World(new b2Vec2(0,0),true); |
Then, it’s just a matter of applying Forces according to bodies and planets position.
Look at this script:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 |
package { import flash.display.Sprite; import flash.events.Event; import flash.events.MouseEvent; 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,0),true); private var worldScale:Number=30; private var planetVector:Vector.<b2Body>=new Vector.<b2Body>(); private var debrisVector:Vector.<b2Body>=new Vector.<b2Body>(); private var orbitCanvas:Sprite=new Sprite(); public function Main() { addChild(orbitCanvas); orbitCanvas.graphics.lineStyle(1,0xff0000); debugDraw(); addPlanet(180,240,90); addPlanet(480,120,45); addEventListener(Event.ENTER_FRAME,update); stage.addEventListener(MouseEvent.CLICK,createDebris); } private function createDebris(e:MouseEvent):void { addBox(mouseX,mouseY,20,20); } private function addPlanet(pX:Number,pY:Number,r:Number):void { var fixtureDef:b2FixtureDef = new b2FixtureDef(); fixtureDef.restitution=0; fixtureDef.density=1; var circleShape:b2CircleShape=new b2CircleShape(r/worldScale); fixtureDef.shape=circleShape; var bodyDef:b2BodyDef=new b2BodyDef(); bodyDef.userData=new Sprite(); bodyDef.position.Set(pX/worldScale,pY/worldScale); var thePlanet:b2Body=world.CreateBody(bodyDef); planetVector.push(thePlanet); thePlanet.CreateFixture(fixtureDef); orbitCanvas.graphics.drawCircle(pX,pY,r*3); } private function addBox(pX:Number,pY:Number,w:Number,h:Number):void { var polygonShape:b2PolygonShape = new b2PolygonShape(); polygonShape.SetAsBox(w/worldScale/2,h/worldScale/2); var fixtureDef:b2FixtureDef = new b2FixtureDef(); fixtureDef.density=1; fixtureDef.friction=1; fixtureDef.restitution=0; fixtureDef.shape=polygonShape; var bodyDef:b2BodyDef = new b2BodyDef(); bodyDef.type=b2Body.b2_dynamicBody; bodyDef.position.Set(pX/worldScale,pY/worldScale); var box:b2Body=world.CreateBody(bodyDef); debrisVector.push(box); box.CreateFixture(fixtureDef); } 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 update(e:Event):void { world.Step(1/30, 10, 10); world.ClearForces(); for (var i:int=0; i<debrisVector.length; i++) { var debrisPosition:b2Vec2=debrisVector[i].GetWorldCenter(); for (var j:int=0; j<planetVector.length; j++) { var planetShape:b2CircleShape=planetVector[j].GetFixtureList().GetShape() as b2CircleShape; var planetRadius:Number=planetShape.GetRadius(); var planetPosition:b2Vec2=planetVector[j].GetWorldCenter(); var planetDistance:b2Vec2=new b2Vec2(0,0); planetDistance.Add(debrisPosition); planetDistance.Subtract(planetPosition); var finalDistance:Number=planetDistance.Length(); if (finalDistance<=planetRadius*3) { planetDistance.NegativeSelf(); var vecSum:Number=Math.abs(planetDistance.x)+Math.abs(planetDistance.y); planetDistance.Multiply((1/vecSum)*planetRadius/finalDistance); debrisVector[i].ApplyForce(planetDistance,debrisVector[i].GetWorldCenter()); } } } world.DrawDebugData(); } } } |
The whole code just create static bodies (planets) and let you place dynamic bodies (debris) with the click of the mouse.
The only interesting part of the script is the for loop in the update function, which I’ll explain line by line:
|
1 |
for (var i:int=0; i<debrisVector.length; i++) { |
Loop which scans for all debris previously stored in debrisVector Vector declared at line 14 and updated at line 54
|
1 |
var debrisPosition:b2Vec2=debrisVector[i].GetWorldCenter(); |
Gets debris position
|
1 |
for (var j:int=0; j<planetVector.length; j++) { |
Loop which scans for all planets previously stored in planetVector Vector declared at line 13 and updated at line 38
|
1 |
var planetShape:b2CircleShape=planetVector[j].GetFixtureList().GetShape() as b2CircleShape; |
I need to know the mass of the planet because the bigger the mass, the more intense the gravity attraction. Unfortunately Box2D static bodies do not have mass, so I need to get the circle shape of the planet…
|
1 |
var planetRadius:Number=planetShape.GetRadius(); |
… and get its radius. So in this case the bigger the radius, the more intense the gravity attraction
|
1 |
var planetPosition:b2Vec2=planetVector[j].GetWorldCenter(); |
Gets planet position
|
1 |
var planetDistance:b2Vec2=new b2Vec2(0,0); |
Creates a new b2Vec2 variable which will store the distance between the planet and the debris
|
1 |
planetDistance.Add(debrisPosition); |
Adds debris coordinates, then…
|
1 |
planetDistance.Subtract(planetPosition); |
… subtracts planet coordinates
|
1 |
var finalDistance:Number=planetDistance.Length(); |
Calculates the distance between the planet and the debris
|
1 |
if (finalDistance<=planetRadius*3) { |
Checks if the debris should be affected by planet gravity (in this case, the debris must be within a radius of three times the planet radius)
|
1 |
planetDistance.NegativeSelf(); |
Inverts planet distance, so that the force will move the debris in the direction of the planet origin
|
1 |
var vecSum:Number=Math.abs(planetDistance.x)+Math.abs(planetDistance.y); |
Gets the sum of distance vector components. I will need this to make gravity attraction weaker when the debris is far from the planet, and stronger when the debris is getting close to the planet
|
1 |
planetDistance.Multiply((1/vecSum)*planetRadius/finalDistance); |
This is the final formula to make the gravity weaker as we move far from the planet
|
1 |
debrisVector[i].ApplyForce(planetDistance,debrisVector[i].GetWorldCenter()); |
And finally the force can be applied to debris
This is the result:
Click to create debris.
They can be easily customized to meet the unique requirements of your project.















This post has 17 comments
Pierre Chamberlain
Nice tutorial Emanuel! I was just looking at some video trailers of Angry Birds Space today actually, great timing on your part ;)
Any guess if Rovio used Box2D in the game?
Emanuele Feronato
yes, it does
Asr JW
great tutorial!!.
if you could help me?! Can you make a tutorial to make a dress up game to be like the game MapleStory ^_^
Phil Harvey
Code works nicely, I converted it to use ND2D and build mobile (iOS and Android) builds, and they run at a very nice frame rate.
Luis
Excellent tutorial!!! Emanuel I would like to know how to use something similar to this: http://html5.yoyogames.com/tntbf_lite/index.html making the hero walk on little planets or something like that, and be able to walk upside down.
Behrouz
very Nice tutorial about Box2d
thanks a lot.
Fede
Grazie Emanuele!
What happens if the “planet” gets destroyed in the tick method? I get some strange behaviours from the “debris” bodies in a similar game but wanted to check if your version does the same.
??????Box2D???????? | GamerBoom.com ???
[...] is the result:?Source?emanueleferonato? ???? QQ?? ???? ??? [...]
Niverse
Might put some of this into my flash game. Very Interesting stuff!
Angry Birds
Very good tutorial ;-) i make a clone of angry birds space thanks
Angry Birds
Is possible create this tutorial for stencyl, construct 2 or multimedia fusion 2 ??
TNT
Hi there…
I am not really sure if this setup is the only thing to get it as accurate as possible. If you shoot such a box, (just simple apply force) it is very easy for the box to escape the planet. Instead, what I would want to try is; how can you get the box orbiting around the planet and land softly on the surface. This way, the box will orbit around the planet and the distance between surface and box is getting smaller and smaller.
How can you do something like that? Any ideas would be very helpful!
regards!
No, this is not going to be Angry Birds Space v2 ;-) « MV Fusion
[...] my research to create some very cool radial outer space gravity stuff I came across this link from Emanuele Feronato. That example was the basic to create this video, but I have added a lot of stuff to re-create that [...]
TNT
I think, I got something….
check it out here:
http://www.mvfusion.nl/no-this-is-not-going-to-be-angry-birds-space-v2/
What do you think?
regards!
Make a Flash Game
coo,l totrial
shanshan
Question:
sorry im very entry level, this could be a totally stupid question, but I could what’s this initializing doing here:
private var planetVector:Vector.=new Vector.();
So is planetVector an array?
If so: why not just declare it as an array?
If not: whats the difference between using Vector. and an array of b2body?
xlen
can you make turtorial for game like angry birds making