Way of an Idea Box2D prototype – Step 3
Welcome to the 3rd part. In part 2 we allowed the player to draw the chalk track in a “paused” Box2D environment and then run the simulation.
Now it’s time to delete our chalk track.
A bit of theory: although the simulation is paused, the chalk bodies are already placed in the Box2D world. So we can easily select them with our old friend GetBodyAtMouse function.
Then we need to know whether the selected body is a chalk or not… we don’t want to delete other level assets or even the ball!!
So we must check if the userData of the selected body (that is the attached sprite) is a chalk. If true, we can remove the sprite from the stage and destroy the body.
This is the 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 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 | package { import flash.display.Sprite; import flash.events.Event; import flash.events.MouseEvent; import flash.events.KeyboardEvent; import Box2D.Dynamics.*; import Box2D.Collision.*; import Box2D.Collision.Shapes.*; import Box2D.Common.Math.*; public class draw extends Sprite { public var drawing:Boolean=false; public var canvas:Sprite = new Sprite(); public var points_array:Array; public var pixel_dist:int=20; public var saved_x:int; public var saved_y:int; public var m_world:b2World; public var body:b2Body; public var bodyDef:b2BodyDef = new b2BodyDef(); public var circleDef:b2CircleDef= new b2CircleDef(); public var mousePVec:b2Vec2 = new b2Vec2(); // radians to degrees conversion public var rad_to_deg:Number=57.2957795; // simulation timestep. We start at zero. public var timestep:Number=0; public function draw():void { // world creation var worldAABB:b2AABB = new b2AABB(); worldAABB.lowerBound.Set(-100.0, -100.0); worldAABB.upperBound.Set(100.0, 100.0); m_world=new b2World(worldAABB,new b2Vec2(0,10),true); // // *** IMPORTANT: NOTICE THERE ISN'T ANY DEBUG DRAW ROUTINE *** // // placing the ball bodyDef.position.Set(20/30,20/30); circleDef.radius=15/30; circleDef.density=1; circleDef.friction=0.5; circleDef.restitution=0.2; // ball's movieclip attachment bodyDef.userData = new sphere(); bodyDef.userData.x=20; bodyDef.userData.y=20; bodyDef.userData.width=30; bodyDef.userData.height=30; addChild(bodyDef.userData); // end ball's movieclip attachment body=m_world.CreateBody(bodyDef); body.CreateShape(circleDef); body.SetMassFromShapes(); // addChild(canvas); canvas.graphics.lineStyle(5); stage.addEventListener(MouseEvent.MOUSE_DOWN,mouse_pressed); stage.addEventListener(MouseEvent.MOUSE_MOVE,mouse_moved); stage.addEventListener(MouseEvent.MOUSE_UP,mouse_released); addEventListener(Event.ENTER_FRAME, Update, false, 0, true); stage.addEventListener(KeyboardEvent.KEY_DOWN, key_down); } public function mouse_pressed(e:MouseEvent):void { drawing=true; canvas.graphics.moveTo(mouseX,mouseY); points_array=new Array(); saved_x=mouseX; saved_y=mouseY; points_array.push(saved_x); points_array.push(saved_y); } public function mouse_moved(e:MouseEvent):void { if (drawing) { var dist_x:int=mouseX-saved_x; var dist_y:int=mouseY-saved_y; if (dist_x*dist_x+dist_y*dist_y>pixel_dist*pixel_dist) { canvas.graphics.lineTo(mouseX,mouseY); saved_x=mouseX; saved_y=mouseY; points_array.push(saved_x); points_array.push(saved_y); } } } public function mouse_released(e:MouseEvent):void { drawing=false; var sx:int; var ex:int; var sy:int; var ey:int; var dist_x:int; var dist_y:int; var dist:Number; var angle:Number; var segments:int=points_array.length/2-1; for (var i:int=0; i<segments; i++) { sx=points_array[i*2]; sy=points_array[i*2+1]; ex=points_array[i*2+2]; ey=points_array[i*2+3]; dist_x=sx-ex; dist_y=sy-ey; dist=Math.sqrt(dist_x*dist_x+dist_y*dist_y); angle=Math.atan2(dist_y,dist_x); create_box((sx+ex)/60,(sy+ey)/60,Math.abs(dist)/60,2/30,angle); } canvas.graphics.clear(); canvas.graphics.lineStyle(5); } // function to be called if the player presses space // switching timestep public function key_down(event:KeyboardEvent):void { // pressing SPACE if (event.keyCode==32) { if (timestep) { timestep=0; } else { timestep=1/30; } } // pressing "0" (zero) if (event.keyCode==48) { // finding the body under the mouse var body:b2Body=GetBodyAtMouse(true); // if there is an object under the mouse... if (body) { // if the body's userdata is a chalk if (body.m_userData is chalk) { // removing the Sprite removeChild(body.m_userData); // destroying the object m_world.DestroyBody(body); } } } } // this is our old friend GetBodyAtMouse 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].m_body.IsStatic()==false||includeStatic) { var tShape:b2Shape=shapes[i] as b2Shape; var inside:Boolean=tShape.TestPoint(tShape.m_body.GetXForm(),mousePVec); if (inside) { body=tShape.m_body; break; } } } return body; } public function create_box(px,py,w,h,a):void { var body:b2Body; var bodyDef:b2BodyDef; var boxDef:b2PolygonDef; bodyDef = new b2BodyDef(); bodyDef.position.Set(px,py); bodyDef.angle=a; boxDef = new b2PolygonDef(); boxDef.SetAsBox(w,h); boxDef.friction=0.3; boxDef.density=0; // box's movieclip attachment bodyDef.userData = new chalk(); bodyDef.userData.x=px*30; bodyDef.userData.y=py*30; bodyDef.userData.width=w*60; bodyDef.userData.height=h*60; bodyDef.userData.rotation=a*rad_to_deg; addChild(bodyDef.userData); // end box's movieclip attachment body=m_world.CreateBody(bodyDef); body.CreateShape(boxDef); body.SetMassFromShapes(); } public function Update(e:Event):void { // executing Step function and updating movieclips positions only // if timestep != 0 if (timestep) { m_world.Step(timestep, 10); 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); } } } } } } |
And this is the result:
Draw the track with your mouse, press SPACE to begin the simulation and delete the chalk pressing 0 (zero) and moving the mouse over the chalk.
No need to download anything since you can easily cut-paste this code on the Step 2 example
They can be easily customized to meet the unique requirements of your project.















(10 votes, average: 4.50 out of 5)









This post has 4 comments
Guest
nice…
shoot
So how would one go about doing dynamic objects in this way?
(if mass is changed, it will just fall apart and I haven’t found a way to make it compound myself)
Robin van der Vleuten
Very nice! Is it the same way linerider works?
August Björnberg
the original linerider was made long before box2d