Full Totem Destroyer prototype

Some time ago I posted Create a Flash game like Totem Destroyer, and now I am showing you a complete prototype made by Collin Douch.

The prototype features slimy and non-removable blocks and collision detection between the totem and the ground.

Such detection was made customizing Box2D’s b2ContactListener class. For more information about this class refer to Understanding how Box2D manages collisions.

This is the main file:

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
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
package{
	import flash.events.*
	import flash.events.MouseEvent
	import flash.display.Sprite
	import flash.text.*
	import Box2D.Dynamics.*
	import Box2D.Collision.*
	import Box2D.Collision.Shapes.*
	import Box2D.Common.Math.*
 
	public class totem extends Sprite{
 
		var boxDef:b2PolygonDef
		var bodyDef:b2BodyDef
		var circleDef:b2CircleDef
		var body:b2Body
		var nx:Number
		var ny:Number
		var nn:String
		var mousePVec:b2Vec2 = new b2Vec2();
		var boxCount:Number = 0
		var bombCount = 3
		var foundTotem:Boolean = false
		var totemFound:b2Body = null
		var n_width:Number
		var n_height:Number
		var n_static:Boolean
		var n_name:String
		var n_destroyable:Boolean
		var n_restitution:Number
		var n_friction:Number
		public function totem(){
			addEventListener(Event.ENTER_FRAME,Update)
			stage.addEventListener(MouseEvent.MOUSE_DOWN,destroyBody)
 
			var worldAABB:b2AABB = new b2AABB();
			worldAABB.lowerBound.Set(-100.0,-100.0)
			worldAABB.upperBound.Set(100.0,100.0)
 
			var gravity:b2Vec2 = new b2Vec2(0.0,10.0)
			var doSleep:Boolean = true
			m_world = new b2World(worldAABB,gravity,doSleep)
 
			var totemContact:b2ContactListener = new b2ContactListener();
			m_world.SetContactListener(totemContact)
 
			findTotem();
			CreateLevel(new Array([275,400,550,100,true,"floor",false,0.3,0.3,"Floor"],[200,340,20,20,false,GetName("Box"),true,1,0.001,"Slimy"],[200,320,20,20,false,GetName("Box"),true,0.3,0.3,"Block"],[300,340,20,20,false,GetName("Box"),true,1,0.001,"Slimy"],[300,320,20,20,false,GetName("Box"),true,0.3,0.3,"Block"],[250,300,120,20,false,"BG",false,0.3,0.3,"unBreakable"],[200,280,20,20,false,GetName("Box"),true,0.3,0.3,"Block"],[200,260,20,20,false,GetName("Box"),true,0.3,0.3,"Block"],[200,240,20,20,false,GetName("Box"),true,0.3,0.3,"Block"],[300,280,20,20,false,GetName("Box"),true,0.3,0.3,"Block"],[300,260,20,20,false,GetName("Box"),true,0.3,0.3,"Block"],[300,240,20,20,false,GetName("Box"),true,0.3,0.3,"Block"],[250,220,120,20,false,"BB",false,0.3,0.3,"unBreakable"],[250,205,21.4,33.6,false,"Totem",false,0.3,0.3,"Totem"]))
		}
		public function GetName(possName:String){
			var nameing = "Box"+boxCount
			return "Box"+boxCount;
			boxCount++
		}
		public function CreateLevel(LevelArray:Array){
			for(var a:int=0;a<LevelArray.length;a++){
				for(var b:int = 0;b<10;b++){
					switch(b){
						case 0:
						nx = LevelArray[a][b]
						break;
 
						case 1:
						ny = LevelArray[a][b]
						break;
 
						case 2:
						n_width = LevelArray[a][b]
						break;
 
						case 3:
						n_height = LevelArray[a][b]
						break;
 
						case 4:
						n_static = LevelArray[a][b]
						break;
 
						case 5:
						if(LevelArray[a][b]=="Box0"){
							n_name = "Box"+boxCount
						}
						else{
						n_name = LevelArray[a][b]
						}
						boxCount++
						break;
 
						case 6:
						n_destroyable = LevelArray[a][b]
						break;
 
						case 7:
						n_restitution = LevelArray[a][b]
						break;
 
						case 8:
						n_friction = LevelArray[a][b]
						break;
 
						case 9:
						CreateBox(nx,ny,n_width,n_height,n_static,n_name,n_destroyable,n_restitution,n_friction,Decode(LevelArray[a][b],"iden"))
						break;
					}
 
				}
			}
		}
 
		public function CreateBox(x:Number,y:Number,width:Number,height:Number,static:Boolean,name:String,destroyable:Boolean,restitution:Number,friction:Number,Cover){
			boxDef = new b2PolygonDef();
			boxDef.SetAsBox(GetB2Nums(width,true),GetB2Nums(height,true))
			if(!static){
			boxDef.density = 1.0
			}
			else{
				boxDef.density = 0.0
			}
			boxDef.friction = friction
			boxDef.restitution = restitution
			bodyDef = new b2BodyDef();
			bodyDef.position.x = GetB2Nums(x,false)
			bodyDef.position.y = GetB2Nums(y,false)
			bodyDef.userData = Cover
			bodyDef.userData.name = name
			bodyDef.userData.destroyable = destroyable
			bodyDef.userData.width = 30 * 2 * GetB2Nums(width,true)
			bodyDef.userData.height = 30 * 2* GetB2Nums(height,true)
			body = m_world.CreateBody(bodyDef)
			body.CreateShape(boxDef)
			body.SetMassFromShapes();
			addChild(bodyDef.userData)
		}
 
		public function Decode(WTD:String,Encryption:String){
			switch(Encryption){
				case "iden":
				if(WTD =="Block"){
					return new Block();
				}
				if(WTD == "Floor"){
					return new Floor();
				}
				if(WTD == "Totem"){
					return new Totem();
				}
				if(WTD == "unBreakable"){
					return new unBreakable();
				}
				if(WTD == "Slimy"){
					return new Slimy();
				}
				break;
			}
		}
 
		public function CreateCircle(x:Number,y:Number,radius:Number,static:Boolean){
			circleDef = new b2CircleDef();
			circleDef.radius = GetB2Nums(radius,true)
			if(static){
			circleDef.density = 0.0
			}
			else{
				circleDef.density = 1.0
			}
			bodyDef = new b2BodyDef();
			bodyDef.position.Set(GetB2Nums(x,false),GetB2Nums(y,false))
			body = m_world.CreateBody(bodyDef)
			body.CreateShape(circleDef)
			body.SetMassFromShapes();
		}
 
		public function destroyBody(evt:MouseEvent):void{
			var selectedShape = GetBodyAtMouse();
			var user = selectedShape.GetUserData().name
			var susceptable = selectedShape.GetUserData().destroyable
			var Child = getChildByName(user)
				if(selectedShape){
					if(susceptable){
			m_world.DestroyBody(selectedShape)
			removeChild(Child)
			Child = null
			user = null
			selectedShape = null
					}
			}
		}
 
		public function GetBodyAtMouse(includeStatic:Boolean=false):b2Body {
			var mouseXWorldPhys = (mouseX)/30;
			var mouseYWorldPhys = (mouseY)/30;
			mousePVec.Set(mouseXWorldPhys, mouseYWorldPhys);
			var aabb:b2AABB = new b2AABB();
			aabb.lowerBound.Set(mouseXWorldPhys - 0.001, mouseYWorldPhys - 0.001);
			aabb.upperBound.Set(mouseXWorldPhys + 0.001, mouseYWorldPhys + 0.001);
			var k_maxCount:int=10;
			var shapes:Array = new Array();
			var count:int=m_world.Query(aabb,shapes,k_maxCount);
			var body:b2Body=null;
			for (var i:int = 0; i < count; ++i) {
				if (shapes[i].GetBody().IsStatic()==false||includeStatic) {
					var tShape:b2Shape=shapes[i] as b2Shape;
					var inside:Boolean=tShape.TestPoint(tShape.GetBody().GetXForm(),mousePVec);
					if (inside) {
						body=tShape.GetBody()
						break;
					}
				}
			}
			return body;
		}
			public function findTotem(includeStaticBodies:Boolean = false){
				for (var tm:b2Body = m_world.m_bodyList; tm; tm = tm.m_next){
				if (tm.m_userData is Sprite){
					if(tm.m_userData.name=="Totem" && foundTotem==false){
						totemFound==tm
						foundTotem=true
						break;
					}
				}
				}
			}
 
		public function GetB2Nums(Num:Number,WH:Boolean){
			switch(WH){
				case true:
				return Num/30/2
				break;
 
				case false:
				return Num/30
				break;
			}
		}
 
		public function Update(e:Event):void{
			m_world.Step(m_timeStep,m_iterations)
			for (var bb:b2Body = m_world.m_bodyList; bb; bb = bb.m_next){
				if (bb.m_userData is Sprite){
					bb.m_userData.x = bb.GetPosition().x * 30;
					bb.m_userData.y = bb.GetPosition().y * 30;
					bb.m_userData.rotation = bb.GetAngle() * (180/Math.PI);
				}
				}
 
		}
 
		public var m_world:b2World
		public var m_timeStep:Number = GetB2Nums(1.0,false)
		public var m_iterations:int = 10
		public var lose:Boolean = false
	}
}

and this is the custom b2ContactListener 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
/*
* Copyright (c) 2006-2007 Erin Catto http://www.gphysics.com
*
* This software is provided 'as-is', without any express or implied
* warranty.  In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
 
package Box2D.Dynamics{
 
 
import Box2D.Collision.*;
import Box2D.Collision.Shapes.*;
import Box2D.Dynamics.Contacts.*;
import Box2D.Dynamics.*;
import Box2D.Common.Math.*;
import Box2D.Common.*;
 
 
/// Implement this class to get collision results. You can use these results for
/// things like sounds and game logic. You can also get contact results by
/// traversing the contact lists after the time step. However, you might miss
/// some contacts because continuous physics leads to sub-stepping.
/// Additionally you may receive multiple callbacks for the same contact in a
/// single time step.
/// You should strive to make your callbacks efficient because there may be
/// many callbacks per time step.
/// @warning The contact separation is the last computed value.
/// @warning You cannot create/destroy Box2D entities inside these callbacks.
public class b2ContactListener
{
var lose:Boolean = false
	/// Called when a contact point is added. This includes the geometry
	/// and the forces.
	public virtual function Add(point:b2ContactPoint) : void{
		if(point.shape1.GetBody().GetUserData().name == "Totem" && point.shape2.GetBody().GetUserData().name == "floor" && lose==false){
			lose = true
			trace("lose")
			point.shape1.GetBody().GetUserData().gotoAndPlay("broken")
		}
		if(point.shape1.GetBody().GetUserData().name == "floor" && point.shape2.GetBody().GetUserData().name == "Totem" && lose==false){
			lose = true
			trace("lose")
			point.shape2.GetBody().GetUserData().gotoAndPlay("broken")
		}
		};
 
	/// Called when a contact point persists. This includes the geometry
	/// and the forces.
	public virtual function Persist(point:b2ContactPoint) : void{};
 
	/// Called when a contact point is removed. This includes the last
	/// computed geometry and forces.
	public virtual function Remove(point:b2ContactPoint) : void{};
 
	/// Called after a contact point is solved.
	public virtual function Result(point:b2ContactResult) : void{};
};
 
}

Download the full source code (Box2D excluded)

Rate this post: 1 Star2 Stars3 Stars4 Stars5 Stars (8 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 10 comments

  1. ElOIl

    on March 6, 2009 at 2:21 pm

    hi, unfortunatelly I can´t open the totem.fla file. Seems to be damaged. Can you please check it!? :o)

  2. Gabriel Bianconi

    on March 6, 2009 at 4:16 pm

    Nice prototype, but I got an error while using the example… Something with destroyBody function…

  3. ThomasTan

    on March 7, 2009 at 7:54 am

    LOL! THE SLIMY BLOCK IS DANCING!
    Cool!

  4. Patrick Pietens

    on March 9, 2009 at 3:36 pm

    Great tutorial!

    You’re implementing the custom b2ContactListener the wrong way though. It’s better practice to cache all collisions and wait till Box2D finished its timestep. Otherwise you may break your world.

    In this case it doesn’t really matter because the b2ContactListener only influences the graphics of the game. Not the bodies. But still, it’s better to do so :)

  5. Devon

    on March 30, 2009 at 4:03 am

    This does not seem to open up in Flash 8, would really love to be able to work with the source code, and was wondering if you would re-upload! Thanks!

  6. zkkpos

    on May 9, 2009 at 2:06 am

    It is flash 9+ only, right?

  7. isasu

    on August 2, 2009 at 10:50 pm

    it says unexpected file format when i try to open it. Why?

  8. www.webriders.pl

    on September 21, 2009 at 1:29 pm

    isasu -> this is a flash cs4 file. If you have older version you will open as files only.

  9. Kaaba

    on March 1, 2010 at 6:27 pm

    How can I go to next level when, for example, totem hits the slimy block?

  10. krisma

    on December 16, 2010 at 5:11 am

    can anyone help me, I get error on the prototype : undifined method Block, Floor, Totem, unBreakable, and Slimy.