Way of an Idea prototype updated to Box2D 2.1a and Nape
Three years ago I published a three steps tutorial about the creation of a Flash game like Way of an Idea.
Following the same concepts I already explained in steps 1, 2 and 3, here is the updated Box2D 2.1a version running together with the Nape version of the game.
This is what we have:
Draw the track with your mouse, press SPACE to play/pause the simulation and delete the chalk pressing 0 (zero) and moving the mouse over the chalk.
You should be able to recognize both debug draws, Box2D and Nape.
This is the source code, with comments to show which part of the code belongs to Box2D and which to Nape:
|
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; // BOX2D import Box2D.Dynamics.*; import Box2D.Collision.*; import Box2D.Collision.Shapes.*; import Box2D.Common.Math.*; // NAPE import nape.geom.Vec2; import nape.phys.Body; import nape.phys.BodyList; import nape.phys.BodyType; import nape.shape.Polygon; import nape.shape.Circle; import nape.space.Space; import nape.util.ShapeDebug; public class Main extends Sprite { private var drawing:Boolean=false; private var canvas:Sprite = new Sprite(); private var pointsArray:Array; private var pixelDist:int=20; private var savedX:int; private var savedY:int; private var timeStep:Number=0; // BOX2D private var world:b2World=new b2World(new b2Vec2(0,10),true); private var worldScale:Number=30; private var debugSprite:Sprite=new Sprite(); // NAPE private var napeWorld:Space=new Space(new Vec2(0,500)); private var debug:ShapeDebug=new ShapeDebug(640,480,0x00ff00); private var napeDebugSprite:Sprite=new Sprite(); public function Main():void { addChild(canvas); canvas.graphics.lineStyle(5); stage.addEventListener(MouseEvent.MOUSE_DOWN,mousePressed); stage.addEventListener(MouseEvent.MOUSE_MOVE,mouseMoved); stage.addEventListener(MouseEvent.MOUSE_UP,mouseReleased); addEventListener(Event.ENTER_FRAME,update); stage.addEventListener(KeyboardEvent.KEY_DOWN,keyPressed); // BOX2D addChild(debugSprite); debugDraw(); // NAPE addChild(napeDebugSprite); napeDebugSprite.addChild(debug.display); // BOX2D; var bodyDef:b2BodyDef= new b2BodyDef(); bodyDef.type=b2Body.b2_dynamicBody; bodyDef.position.Set(20/worldScale,20/worldScale); var circleShape:b2CircleShape = new b2CircleShape(15/worldScale); var fixtureDef:b2FixtureDef = new b2FixtureDef(); fixtureDef.density=1; fixtureDef.friction=0.5; fixtureDef.restitution=0.5; fixtureDef.shape=circleShape; var body:b2Body=world.CreateBody(bodyDef); body.CreateFixture(fixtureDef); // NAPE var napeBody:Body=new Body(BodyType.DYNAMIC,new Vec2(20,20)); var circle:Circle=new Circle(15); circle.material.elasticity=0.5; circle.material.density=1; circle.material.staticFriction=0.5; napeBody.shapes.add(circle); napeBody.space=napeWorld; } private function mousePressed(e:MouseEvent):void { drawing=true; canvas.graphics.moveTo(mouseX,mouseY); pointsArray=new Array(); savedX=mouseX; savedY=mouseY; pointsArray.push(savedX); pointsArray.push(savedY); } private function mouseMoved(e:MouseEvent):void { if (drawing) { var distX:int=mouseX-savedX; var distY:int=mouseY-savedY; if (distX*distX+distY*distY>pixelDist*pixelDist) { canvas.graphics.lineTo(mouseX,mouseY); savedX=mouseX; savedY=mouseY; pointsArray.push(savedX); pointsArray.push(savedY); } } } // BOX2D private function debugDraw():void { var debugDraw:b2DebugDraw=new b2DebugDraw(); debugDraw.SetSprite(debugSprite); debugDraw.SetDrawScale(worldScale); debugDraw.SetFlags(b2DebugDraw.e_shapeBit|b2DebugDraw.e_jointBit); debugDraw.SetFillAlpha(0.5); world.SetDebugDraw(debugDraw); } private function mouseReleased(e:MouseEvent):void { drawing=false; var sx:int; var ex:int; var sy:int; var ey:int; var distX:int; var distY:int; var dist:Number; var angle:Number; var segments:int=pointsArray.length/2-1; for (var i:int=0; i<segments; i++) { sx=pointsArray[i*2]; sy=pointsArray[i*2+1]; ex=pointsArray[i*2+2]; ey=pointsArray[i*2+3]; distX=sx-ex; distY=sy-ey; dist=Math.sqrt(distX*distX+distY*distY); angle=Math.atan2(distY,distX); addPath((sx+ex)/2,(sy+ey)/2,Math.abs(dist),4,angle); } canvas.graphics.clear(); canvas.graphics.lineStyle(5); } private function keyPressed(event:KeyboardEvent):void { if (event.keyCode==32) { if (timeStep) { timeStep=0; } else { timeStep=1/30; } } if (event.keyCode==48) { // BOX2D world.QueryPoint(queryCallback,new b2Vec2(mouseX/worldScale,mouseY/worldScale)); // NAPE var bodies:BodyList=napeWorld.bodiesUnderPoint(new Vec2(mouseX,mouseY)); if (bodies.length>0) { for (var i:int = 0; i < bodies.length; i++) { var body:Body=bodies.at(i); if (body.type==BodyType.STATIC) { napeWorld.bodies.remove(body); } } } } } private function addPath(pX:Number,pY:Number,w:Number,h:Number,angle:Number):void { // BOX2D var bodyDef:b2BodyDef= new b2BodyDef(); var polygonShape:b2PolygonShape = new b2PolygonShape(); polygonShape.SetAsOrientedBox(w/2/worldScale, h/2/worldScale, new b2Vec2(pX/worldScale,pY/worldScale),angle); var fixtureDef:b2FixtureDef = new b2FixtureDef(); fixtureDef.density=1; fixtureDef.friction=0.5; fixtureDef.restitution=0.5; fixtureDef.shape=polygonShape; var body:b2Body=world.CreateBody(bodyDef); body.CreateFixture(fixtureDef); // NAPE var napeBody:Body=new Body(BodyType.STATIC,new Vec2(pX,pY)); var polygon:Polygon=new Polygon(Polygon.box(w,h)); polygon.rotate(angle); polygon.material.elasticity=0.5; polygon.material.density=1; polygon.material.staticFriction=0.5; napeBody.shapes.add(polygon); napeBody.space=napeWorld; } // BOX2D private function queryCallback(fixture:b2Fixture):Boolean { var touchedBody:b2Body=fixture.GetBody(); for (var f:b2Fixture=touchedBody.GetFixtureList(); f; f=f.GetNext()) { if (touchedBody.GetType()==b2Body.b2_staticBody) { world.DestroyBody(touchedBody); } } return false; } private function update(e:Event):void { if (timeStep) { // BOX2D world.Step(1/30,10,10); world.ClearForces(); // NAPE napeWorld.step(1/30,10,10); } // BOX2D world.DrawDebugData(); // NAPE debug.clear(); debug.draw(napeWorld); debug.flush(); } } } |
It would be interesting to know if you ever used Nape in some of your projects, meanwhile download the full source code of the prototype.
They can be easily customized to meet the unique requirements of your project.












This post has 5 comments
spets
Hi, nice work as always, i have managed to separate the circle from its fill somehow thought, while trying to erase a part using the 0 button.
Dan W
@spets
The circle with the fill is the Box2D’s debug draw… the open circle is the debug draw from the Nape physics engine. I was confused at first, until I read the article and then looked up just what Nape was and looked at the code.
ChrisM
Nice! I hadn’t even known about Nape prior to this post.
The samples on their site are very impressive! Destructible terrain, soft body physics with buoyancy, and all running very quickly.
Sucks that they don’t have C++/ Java ports yet, but I’m sure the principles in the samples could be applied to box2d as well.
Sergey
How create in box2d effect of crawling up the wall
example in this game:
http://fleshki.net/index.php?ev=7&g=881887
Arch
hello, can this code be combined with quickbox2d?
THANKS!