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)
They can be easily customized to meet the unique requirements of your project.















(8 votes, average: 4.75 out of 5)









This post has 10 comments
ElOIl
hi, unfortunatelly I can´t open the totem.fla file. Seems to be damaged. Can you please check it!? :o)
Gabriel Bianconi
Nice prototype, but I got an error while using the example… Something with destroyBody function…
ThomasTan
LOL! THE SLIMY BLOCK IS DANCING!
Cool!
Patrick Pietens
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 :)
Devon
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!
zkkpos
It is flash 9+ only, right?
isasu
it says unexpected file format when i try to open it. Why?
www.webriders.pl
isasu -> this is a flash cs4 file. If you have older version you will open as files only.
Kaaba
How can I go to next level when, for example, totem hits the slimy block?
krisma
can anyone help me, I get error on the prototype : undifined method Block, Floor, Totem, unBreakable, and Slimy.