Develop a Flash game like Angry Birds using Box2D – predictive trajectory line
This should have been the last step of the Angry Birds series, but I got an interesting request from a reader and I wanted to show you how to do it.
We will see how to create a predictive trajectory line. Very useful for beginner and unskilled players.
This is what you’ll get: I added the predictive trajectory line to the script published on step 1 to reduce the total amount of code displayed:
Drag and release the bird with the mouse.
Where’s the magic? Let’s look at the source code:
|
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 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 |
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,10),true); private var worldScale:int=30; private var bird:Bird=new Bird(); private var birdSphere:b2Body; private var preview:Sprite=new Sprite(); public function Main() { var bg:Background=new Background(); addChild(bg); addChild(bird); addChild(preview); bird.x=170; bird.y=270; bird.buttonMode=true; addWall(320,10,320,395); addWall(320,10,320,-5); addWall(10,320,-5,240); addWall(10,320,645,240); addEventListener(Event.ENTER_FRAME,updateWorld); bird.addEventListener(MouseEvent.MOUSE_DOWN,birdClicked); } private function addWall(w,h,px,py):void { var floorShape:b2PolygonShape = new b2PolygonShape(); floorShape.SetAsBox(w/worldScale,h/worldScale); var floorFixture:b2FixtureDef = new b2FixtureDef(); floorFixture.density=0; floorFixture.friction=10; floorFixture.restitution=0.5; floorFixture.shape=floorShape; var floorBodyDef:b2BodyDef = new b2BodyDef(); floorBodyDef.position.Set(px/worldScale,py/worldScale); var floor:b2Body=world.CreateBody(floorBodyDef); floor.CreateFixture(floorFixture); } private function birdClicked(e:MouseEvent):void { addEventListener(MouseEvent.MOUSE_MOVE,birdMoved); addEventListener(MouseEvent.MOUSE_UP,birdReleased); bird.removeEventListener(MouseEvent.MOUSE_DOWN,birdClicked); } private function birdMoved(e:MouseEvent):void { bird.x=mouseX; bird.y=mouseY; var distanceX:Number=bird.x-170; var distanceY:Number=bird.y-270; if (distanceX*distanceX+distanceY*distanceY>10000) { var birdAngle:Number=Math.atan2(distanceY,distanceX); bird.x=170+100*Math.cos(birdAngle); bird.y=270+100*Math.sin(birdAngle); } fakeRelease(); } private function fakeRelease():void { var sphereShape:b2CircleShape=new b2CircleShape(15/worldScale); var sphereFixture:b2FixtureDef = new b2FixtureDef(); sphereFixture.density=1; sphereFixture.friction=3; sphereFixture.restitution=0.1; sphereFixture.shape=sphereShape; var sphereBodyDef:b2BodyDef = new b2BodyDef(); sphereBodyDef.type=b2Body.b2_dynamicBody; sphereBodyDef.userData=bird; sphereBodyDef.position.Set(bird.x/worldScale,bird.y/worldScale); birdSphere=world.CreateBody(sphereBodyDef); birdSphere.CreateFixture(sphereFixture); var distanceX:Number=bird.x-170; var distanceY:Number=bird.y-270; var distance:Number=Math.sqrt(distanceX*distanceX+distanceY*distanceY); var birdAngle:Number=Math.atan2(distanceY,distanceX); birdSphere.SetLinearVelocity(new b2Vec2(-distance*Math.cos(birdAngle)/4,-distance*Math.sin(birdAngle)/4)); preview.graphics.clear(); preview.graphics.lineStyle(1,0x000000); preview.graphics.beginFill(0xff0000); for (var i:int=1; i<=150; i++) { world.Step(1/30,10,10); preview.graphics.drawCircle(birdSphere.GetPosition().x*worldScale,birdSphere.GetPosition().y*worldScale,2); world.ClearForces(); } preview.graphics.endFill(); world.DestroyBody(birdSphere); } private function birdReleased(e:MouseEvent):void { bird.buttonMode=false; removeEventListener(MouseEvent.MOUSE_MOVE,birdMoved); removeEventListener(MouseEvent.MOUSE_UP,birdReleased); var sphereShape:b2CircleShape=new b2CircleShape(15/worldScale); var sphereFixture:b2FixtureDef = new b2FixtureDef(); sphereFixture.density=1; sphereFixture.friction=3; sphereFixture.restitution=0.1; sphereFixture.shape=sphereShape; var sphereBodyDef:b2BodyDef = new b2BodyDef(); sphereBodyDef.type=b2Body.b2_dynamicBody; sphereBodyDef.userData=bird; sphereBodyDef.position.Set(bird.x/worldScale,bird.y/worldScale); birdSphere=world.CreateBody(sphereBodyDef); birdSphere.CreateFixture(sphereFixture); var distanceX:Number=bird.x-170; var distanceY:Number=bird.y-270; var distance:Number=Math.sqrt(distanceX*distanceX+distanceY*distanceY); var birdAngle:Number=Math.atan2(distanceY,distanceX); birdSphere.SetLinearVelocity(new b2Vec2(-distance*Math.cos(birdAngle)/4,-distance*Math.sin(birdAngle)/4)); } private function updateWorld(e:Event):void { world.Step(1/30,10,10); for (var currentBody:b2Body=world.GetBodyList(); currentBody; currentBody=currentBody.GetNext()) { if (currentBody.GetUserData()) { currentBody.GetUserData().x=currentBody.GetPosition().x*worldScale; currentBody.GetUserData().y=currentBody.GetPosition().y*worldScale; currentBody.GetUserData().rotation=currentBody.GetAngle()*(180/Math.PI); } } world.ClearForces(); world.DrawDebugData(); } } } |
When I drag the bird with birdMoved function (lines 49-60), at the end of such function I call another function called fakeRelease which will handle the trajectory line.
fakeRelease just adds a “fake” bird to the world and give it the linear velocity as if the player fired it, and you can see lines 62-78 are just a copy/paste of lines 95-111.
The difference is I simulate with a loop (line 82) a series of world steps and draw a circle in the position the bird would be at every step. The core of the function is at line 84.
And now you have the predictive trajectory line too. Download the source code.
They can be easily customized to meet the unique requirements of your project.





(44 votes, average: 4.86 out of 5)








This post has 35 comments
Reginis
Very useful for level design and testing.
ami
nice code, i love the simple of your explain , i Learning a lot from your blog.
but i have a one note, you must put a refresh button i the swf, It’s annoying to refresh the page every time that you want to See the example.
Marc
That’s pretty cool Emanuele! That must be the same kind of code they used for the unicorn in Peggle.
tambi jalouqa
Looks amazing,
But how are you incrementing the step through the same world without consequences. What if there was already some physics on the screen. do we have to create a new world?
checo
Maybe The line 94, removing the birdMoved Listener cancels the increment in the original world?
Danny
An interesting example, since the other objects will actually be asleep while you loop the world step shouldn’t be too much of a problem.
Although if you have some other stuff going on an identically set up world would fix that. Nice
mageec
thanks emmanuel you are a rock
Digitalic
I think you have done a good job of replicating the features of Angry Birds; it’s been good fun watching this tutorial series develop.
I have a suggestion for a refinement that I think would be great for beginners to AS3. How about showing the catapult elastic? I have noticed that in Angry Birds, the elastic gets thinner as you stretch it and I think this would be a great learning opportunity without too much coding.
Also, although I do appreciate that your code is for prototype purposes, I think that, for the benefit of beginners, it would be a good idea to avoid copy and pasting code into multiple functions as you have done with the predictive trajectory code. This could be placed in a function which reduces all sorts of ‘risks’ related to cut and paste and would set a good example to beginners.
umzug
wow 8 ball hi Emanuele pls http://www.miniclip.com/games/8-ball-pool-multiplayer/tr/ tutorials thanx for mach
Thomas Burleson
This is exactly what I want to help my 11yr son visualize trajectories and understand the relationships between graphics, physics, and code.
Nice tutorial. Thanks again,
ThomasB
James
Emanuele can you do a AS3 conversion of this game: http://www.emanueleferonato.com/2008/06/17/adding-a-new-twist-to-the-prototype-of-a-flash-game-like-poux/
you done a AS2 version of it :D
Will
Wow, Great tutorial man! All of your tutorials are awesome and you are too.
Thanks for your time making our life easier.
Anonymous
I’m getting this error while implementing it with the other code :
TypeError: Error #1010: A term is undefined and has no properties.
at customContactListener/PostSolve()
at Box2D.Dynamics::b2Island/Report()
at Box2D.Dynamics::b2Island/SolveTOI()
at Box2D.Dynamics::b2World/http://www.box2d.org/ns/b2internal::SolveTOI()
at Box2D.Dynamics::b2World/Step()
at Main/fakeRelease()
at Main/birdMoved()
What might be wrong?
Anonymous
Nevermind, fixed it. Had to make a couple of changes to the customContactListener and to the code.
jaydn
awesome stuff. I was wondering where i can find “Bird” class? it wasnt part of the download or code on page.
Thanks!
George
If you have other stuff moving in the Box2D world you should create a separate one for the trajectory… 150 world steps will mess up other bodies already in the world.
Nice tutorial … I had already done a similar implementation but with 2 b2D worlds.
Flex_Developer
Nice article.. can you please tell me what is Bird class exactly? In downloaded source it is not there.
Thanks.
Emanuele Feronato
Bird has no .as class, it’s just the bird movieclip
Flex_Developer
Hi, I am using your code in one application for touch mobile device. But this trajectory path drawing (fakeRelease()) is making touch movement of bird very slow. Commenting fakeRelease(),touch works normally. Can you please tell me what can be workaround for this?
dengsw
good
yasdar
Hi, I have a small question for you Emanuele.
how does the For loop in line 86 work?
Thank you
beatless
I have the same question with line 86.
Thank you
How draw I draw Trajectory Line in Box2DWeb? | PHP Developer Resource
[...] http://www.emanueleferonato.com/2011/11/03/develop-a-flash-game-like-angry-birds-using-box2d-predict... [...]
Julien
Hi,
When you simulate the launch, you actually fast-forward the world.
If there was other objects, they would have been fast-forwarded at the end of the fake launch.
How do you get around that without creating another instance of the game?
adam
I would like to make an Angry bird game please.
adam
I hope I could make a game.
Rajat De
Can you give the coding of making this in as2 , i am good at as 2 but not at as 3
gameDev
Can u plz make a turotial to get same output without box2d. it will really help me…thx
ic
In the name of GOD
fantastic
tnx
qurc
Are there any options for calculating the trajectories of objects without creating physics objects and using world.step()? In the case where we using a multiple objects (and mobile device) all works very slowly:(
Thanks
blacknotblack
Hello!
Could you write tutorial about flying arrow like this:
http://flashgameblogs.ru/blog/box2d/858.html ?
Viraj
Has anybody done it in cocos2d?
I have tried but not getting result..
Thank you,
Viraj
alex
Hello bro Nice example we are waiting your more
CitrusNoob
Hello
Nice tutorial, can you make this with CitrusEngine(with starling+Box2D)
With box2d it’s ok but with Citrus I don’t know how we can do that
thank’s
edi
hello
I made something similar in nape
here is a link:
http://toastermedia.net/matematyczna_sowa/MatematycznaSowa.html
regards
ed