Install Circle Chain on your iPhone for free and get the source code!! 645 downloads to go - last updated: April 21, 2012

Develop a Flash game like Angry Birds using Box2D – Killing the pigs

Do you know why Angry Birds had such as a worldwide success? Because people love to kill pigs in videogames.

I got sick when I saw Babe movie and I want to kill pigs since then.

Today, you will learn how to do it thanks to the second part of the Angry Birds tutorial.

Before we start, let me explain how we are going to kill them. A pig dies when something, including the bird, the objects in the game, and even the ground, hits it too hard.

Various objects in the game also break when they hit something too hard.

You are already able to see when two objects collide thanks to tutorials like Create a Flash prototype of The Moops – Combos of Joy, but we’ve never seen how to determine the force of the collision.

You still have to create a custom contact listener class, but rather than using BeginContact function which just has an argument we can use to determine which objects collided, we will use PostSolve, which has another argument, a b2ContactImpulse variable.

With b2ContactImpulse we can know the amount of impulse Box2D has to give to bodies to avoid them from penetrating each other due to the collision. In other words, the collision force.

Once we know the strength of the collision, we can compare it with the maximum strength allowed before an object breaks (which can also be different according to the material, such as glass, wood, stone) and if the collision will break the object, we simply remove it from the Box2D world, and then we’ll show a cute animation.

This is what I made with this concept:

Throw the pigeon to the castle on the right to see how all blocks (and the pig) which are hit hard get destroyed.

In this prototype, world boundaries and the bird itself can’t be destroyed.

This is the Main class:

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
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
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:birdMc=new birdMc();
		private var birdSphere:b2Body;
		private var customContact=new customContactListener();
		public function Main() {
			world.SetContactListener(customContact);
			var bg:backgroundMc=new backgroundMc();
			addChild(bg);
			addChild(bird);
			debugDraw();
			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);
			for (var i:int=0; i<=4; i++) {
				addBlock(20,20,450,365-i*40);
				addBlock(20,20,550,365-i*40);
			}
			addBlock(80,20,500,165);
			addPig(20,500,125);
			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);
			floorBodyDef.userData={assetName:"wall",assetSprite:null,remove:false};
			var floor:b2Body=world.CreateBody(floorBodyDef);
			floor.CreateFixture(floorFixture);
		}
		private function addPig(r,px,py):void {
			var pigShape:b2CircleShape=new b2CircleShape(r/worldScale);
			var pigFixture:b2FixtureDef = new b2FixtureDef();
			pigFixture.density=1;
			pigFixture.friction=3;
			pigFixture.restitution=0.1;
			pigFixture.shape=pigShape;
			var pigBodyDef:b2BodyDef = new b2BodyDef();
			pigBodyDef.position.Set(px/worldScale,py/worldScale);
			pigBodyDef.type=b2Body.b2_dynamicBody;
			pigBodyDef.userData={assetName:"pig",assetSprite:null,remove:false};
			var pigSphere:b2Body=world.CreateBody(pigBodyDef);
			pigSphere.CreateFixture(pigFixture);
		}
		private function addBlock(w,h,px,py):void {
			var blockShape:b2PolygonShape = new b2PolygonShape();
			blockShape.SetAsBox(w/worldScale,h/worldScale);
			var blockFixture:b2FixtureDef = new b2FixtureDef();
			blockFixture.density=0.5;
			blockFixture.friction=10;
			blockFixture.restitution=0.1;
			blockFixture.shape=blockShape;
			var blockBodyDef:b2BodyDef = new b2BodyDef();
			blockBodyDef.position.Set(px/worldScale,py/worldScale);
			blockBodyDef.userData={assetName:"block",assetSprite:null,remove:false};
			blockBodyDef.type=b2Body.b2_dynamicBody;
			var block:b2Body=world.CreateBody(blockBodyDef);
			block.CreateFixture(blockFixture);
		}
		private function birdClicked(e:MouseEvent):void {
			addEventListener(MouseEvent.MOUSE_MOVE,birdMoved);
			addEventListener(MouseEvent.MOUSE_UP,birdReleased);
			bird.removeEventListener(MouseEvent.MOUSE_DOWN,birdClicked);
		}
		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.8);
			world.SetDebugDraw(worldDebugDraw);
		}
		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);
			}
		}
		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={assetName:"bird",assetSprite:bird,remove:false};
			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()) {
					if (currentBody.GetUserData().assetSprite!=null) {
						currentBody.GetUserData().assetSprite.x=currentBody.GetPosition().x*worldScale;
						currentBody.GetUserData().assetSprite.y=currentBody.GetPosition().y*worldScale;
						currentBody.GetUserData().assetSprite.rotation=currentBody.GetAngle()*(180/Math.PI);
					}
					if (currentBody.GetUserData().remove) {
						world.DestroyBody(currentBody);
					}
				}
			}
			world.ClearForces();
			world.DrawDebugData();
		}
	}
}

and this is the custom contact listener:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package {
	import Box2D.Dynamics.*;
	import Box2D.Collision.*;
	import Box2D.Dynamics.Contacts.*;
	class customContactListener extends b2ContactListener {
		private var MAX_IMPULSE:Number=10;
		override public function PostSolve(contact:b2Contact, impulse:b2ContactImpulse):void {
			if (impulse.normalImpulses[0]>MAX_IMPULSE) {
				var nameA:String=contact.GetFixtureA().GetBody().GetUserData().assetName.toString();
				var nameB:String=contact.GetFixtureB().GetBody().GetUserData().assetName.toString();
				if (nameA!="wall" && nameA!="bird") {
					contact.GetFixtureA().GetBody().GetUserData().remove=true;
				}
				if (nameB!="wall" && nameB!="bird") {
					contact.GetFixtureB().GetBody().GetUserData().remove=true;
				}
			}
		}
	}
}

Next time, I’ll add some scrolling, and you’ll see how you can have a working prototype of Angry Birds in a bunch of lines.

Download the source code.

Rate this post: 1 Star2 Stars3 Stars4 Stars5 Stars (16 votes, average: 4.75 out of 5)
Loading ... Loading ...
Flash Templates provided by Template Monster are pre-made web design products developed using Flash technology.
They can be easily customized to meet the unique requirements of your project.
Be my fan on Facebook and follow me on Twitter! Exclusive content for my Facebook fans and Twitter followers

This post has 7 comments

  1. Jes Fink-Jensen

    on October 21, 2011 at 7:31 pm

    This is very cool! Nice use of physics…

  2. tlearsakura

    on October 21, 2011 at 10:38 pm

    Very nice example !!

  3. luis

    on October 22, 2011 at 1:00 am

    excelente

  4. Kris

    on October 26, 2011 at 11:20 am

    Hello Sir, i just want to ask, how to removeChild the object inside the currentBody,
    example : i create a Box MovieClip for the blocks, and if the block is destroy i want it to be remove in stage also..

    thanks

  5. Ben Reynolds

    on October 29, 2011 at 6:28 pm

    Awesome addition to part 1 of this tutorial.

  6. Janitha

    on December 30, 2011 at 7:56 am

    I’m new to Box2D so pardon me, is the b2ContactListener class no longer available or has it been renamed to something different?

  7. prezire

    on March 31, 2012 at 12:47 pm

    awesome tutorial!

    @janitha
    it’s the structure of this tutorial’s package. just point your publish settings to the angrybirds2 directory and you should be fine.