Box2D flying arrows engine – first attempt
This is an uncommented and unoptimized attempt to make an arrow engine. I used the third example of my Flying arrows simulation with Box2D, added the lines to have the camera following the latest arrow fired taken from Develop a Flash game like Angry Birds using Box2D – Following bird with the camera and skinning crates and played with a custom contact listener to add different behaviors to arrow-wall collision, arrow-crate collision and arrow-arrow collision.
I got also inspired by iforce2D sticky projectiles, and this is what I made:
Click on the stage to shoot an arrow from x=50, y=240 according to the angle between such coordinate and mouse pointer.
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 |
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.Contacts.*; public class Main extends Sprite { private var world:b2World=new b2World(new b2Vec2(0,10),true); private var worldScale:int=30; private var arrowVector:Vector.<b2Body>=new Vector.<b2Body>(); private var customContact=new CustomContactListener(); public function Main():void { world.SetContactListener(customContact); debugDraw(); wall(640,470,1280,20); wall(1270,240,20,4800); for (var i:Number=1; i<=10; i++) { crate(Math.random()*500+640,Math.random()*400,80,80); } addEventListener(Event.ENTER_FRAME, update); stage.addEventListener(MouseEvent.CLICK,addArrow); } private function addArrow(e:MouseEvent):void { var angle:Number=Math.atan2(mouseY-240,mouseX-50); var vertices:Vector.<b2Vec2>=new Vector.<b2Vec2>(); vertices.push(new b2Vec2(-1.4,0)); vertices.push(new b2Vec2(0,-0.1)); vertices.push(new b2Vec2(0.6,0)); vertices.push(new b2Vec2(0,0.1)); var bodyDef:b2BodyDef= new b2BodyDef(); bodyDef.position.Set(50/worldScale,240/worldScale); bodyDef.type=b2Body.b2_dynamicBody; bodyDef.userData={name:"arrow",freeFlight:false,follow:true}; bodyDef.bullet=true; var polygonShape:b2PolygonShape = new b2PolygonShape(); polygonShape.SetAsVector(vertices,4); var fixtureDef:b2FixtureDef = new b2FixtureDef(); fixtureDef.shape=polygonShape; fixtureDef.density=1; fixtureDef.friction=0.5; fixtureDef.restitution=0.5; var body:b2Body=world.CreateBody(bodyDef); body.CreateFixture(fixtureDef); body.SetLinearVelocity(new b2Vec2(30*Math.cos(angle),30*Math.sin(angle))); body.SetAngle(angle); for (var i:Number=0; i<arrowVector.length; i++) { arrowVector[i].GetUserData().follow=false; } arrowVector.push(body); } private function debugDraw():void { var debugDraw:b2DebugDraw=new b2DebugDraw(); var debugSprite:Sprite=new Sprite(); addChild(debugSprite); debugDraw.SetSprite(debugSprite); debugDraw.SetDrawScale(worldScale); debugDraw.SetFlags(b2DebugDraw.e_shapeBit|b2DebugDraw.e_jointBit); debugDraw.SetFillAlpha(0.5); world.SetDebugDraw(debugDraw); } private function wall(pX:Number,pY:Number,w:Number,h:Number):void { var bodyDef:b2BodyDef=new b2BodyDef(); bodyDef.position.Set(pX/worldScale,pY/worldScale); bodyDef.userData={name:"wall"}; var polygonShape:b2PolygonShape=new b2PolygonShape(); polygonShape.SetAsBox(w/2/worldScale,h/2/worldScale); var fixtureDef:b2FixtureDef=new b2FixtureDef(); fixtureDef.shape=polygonShape; fixtureDef.density=1; fixtureDef.restitution=0.4; fixtureDef.friction=0.5; var theWall:b2Body=world.CreateBody(bodyDef); theWall.CreateFixture(fixtureDef); } private function crate(pX:Number,pY:Number,w:Number,h:Number):void { var bodyDef:b2BodyDef=new b2BodyDef(); bodyDef.position.Set(pX/worldScale,pY/worldScale); bodyDef.userData={name:"crate"}; bodyDef.type=b2Body.b2_dynamicBody; var polygonShape:b2PolygonShape=new b2PolygonShape(); polygonShape.SetAsBox(w/2/worldScale,h/2/worldScale); var fixtureDef:b2FixtureDef=new b2FixtureDef(); fixtureDef.shape=polygonShape; fixtureDef.density=0.2; fixtureDef.restitution=0.4; fixtureDef.friction=0.5; var theWall:b2Body=world.CreateBody(bodyDef); theWall.CreateFixture(fixtureDef); } private function update(e : Event):void { world.Step(1/30,5,5); world.ClearForces(); for (var i:Number=arrowVector.length-1; i>=0; i--) { var body:b2Body=arrowVector[i]; if (body.GetType()==b2Body.b2_dynamicBody) { if (! body.GetUserData().freeFlight) { var flyingAngle:Number=Math.atan2(body.GetLinearVelocity().y,body.GetLinearVelocity().x); body.SetAngle(flyingAngle); } } else { arrowVector.splice(i,1); body.SetBullet(false); body.GetUserData().follow=false; } if (body.GetUserData().follow) { var posX:Number=body.GetPosition().x*worldScale; posX=stage.stageWidth/2-posX; if (posX>0) { posX=0; } if (posX<-640) { posX=-640; } x=posX; } } 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 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 |
package { import Box2D.Dynamics.*; import Box2D.Collision.*; import Box2D.Dynamics.Joints.*; import Box2D.Dynamics.Contacts.*; import Box2D.Common.Math.b2Vec2; class CustomContactListener extends b2ContactListener { override public function PreSolve(contact:b2Contact, oldManifold:b2Manifold):void { var contactPoint:b2Vec2; var weldJointDef:b2WeldJointDef; if (contact.IsTouching()) { var bodyA:b2Body=contact.GetFixtureA().GetBody(); var bodyB:b2Body=contact.GetFixtureB().GetBody(); var objA:Object=bodyA.GetUserData(); var objB:Object=bodyB.GetUserData(); if (objA.name=="arrow"&&objB.name=="arrow") { for (var j:b2JointEdge=bodyA.GetJointList(); j; j=j.next) { bodyA.GetWorld().DestroyJoint(j.joint); } for (j=bodyB.GetJointList(); j; j=j.next) { bodyB.GetWorld().DestroyJoint(j.joint); } } if (objA.name=="wall"&&objB.name=="arrow") { if (! objB.freeFlight) { weldJointDef = new b2WeldJointDef(); weldJointDef.Initialize(bodyB,bodyA,bodyA.GetWorldCenter()); bodyB.GetWorld().CreateJoint(weldJointDef); } } if (objB.name=="wall"&&objA.name=="arrow") { if (! objA.freeFlight) { weldJointDef = new b2WeldJointDef(); weldJointDef.Initialize(bodyA,bodyB,bodyB.GetWorldCenter()); bodyA.GetWorld().CreateJoint(weldJointDef); } } if (objA.name=="crate"&&objB.name=="arrow") { contactPoint=contact.GetManifold().m_points[0].m_localPoint; if (! objB.freeFlight&&Math.round(contactPoint.x*10)==6) { weldJointDef = new b2WeldJointDef(); weldJointDef.Initialize(bodyB,bodyA,bodyA.GetWorldCenter()); bodyB.GetWorld().CreateJoint(weldJointDef); } } if (objB.name=="crate"&&objA.name=="arrow") { contactPoint=contact.GetManifold().m_points[0].m_localPoint; if (! objA.freeFlight&&Math.round(contactPoint.x*10)==6) { weldJointDef = new b2WeldJointDef(); weldJointDef.Initialize(bodyA,bodyB,bodyB.GetWorldCenter()); bodyA.GetWorld().CreateJoint(weldJointDef); } } if (objB.name=="arrow") { objB.freeFlight=true; } if (objA.name=="arrow") { objA.freeFlight=true; } } } } } |
Next time, I’ll add some improvements like a real bow firing arrow at different speed. Meanwhile feel free to give feedback or suggestions.
They can be easily customized to meet the unique requirements of your project.












This post has 9 comments
Cody
This is truly great, you are an inspiration!
André Fellipe
Awesome! Some little optimizations and it will be perfect!
[??]Box2D????-???? » Luo????
[...] ?????http://www.emanueleferonato.com/2012/12/14/box2d-flying-arrow-engine-first-attempt/ [...]
MC
Suggestions:
- zoom in / zoom out / cam-movement
- Control the arrow speed/power
- graphic textures
Box2D flying arrows engine – first attempt – Emanuele Feronato « eaflash
[...] on http://www.emanueleferonato.com Share this:TwitterFacebookLike this:LikeBe the first to like [...]
Shekhar Suman
Please check my website. I’ve tried to accomplish the same using Box2dWeb.
http://adf.ly/G5gMb
Husky
This is COOL!!!!!!!
Franklin
@MC plus one for the suggestions
Ray Kutro
Great stuff really… I’m waiting for the next part with real bow. Thanks