PageRenderTime 51ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/src/couk/psyked/box2d/Box2DWorld.as

http://github.com/psyked/Simple-Box2D
ActionScript | 648 lines | 395 code | 54 blank | 199 comment | 33 complexity | 0183fccb675e255cf7dcd5b8b1b08d2b MD5 | raw file
  1. package couk.psyked.box2d
  2. {
  3. import Box2D.Collision.Shapes.b2CircleDef;
  4. import Box2D.Collision.Shapes.b2PolygonDef;
  5. import Box2D.Common.Math.b2Vec2;
  6. import Box2D.Dynamics.Joints.b2DistanceJointDef;
  7. import Box2D.Dynamics.Joints.b2MouseJoint;
  8. import Box2D.Dynamics.Joints.b2MouseJointDef;
  9. import Box2D.Dynamics.Joints.b2RevoluteJointDef;
  10. import Box2D.Dynamics.b2Body;
  11. import Box2D.Dynamics.b2BodyDef;
  12. import Box2D.Dynamics.b2World;
  13. import couk.psyked.box2d.utils.Box2DMouseUtils;
  14. import couk.psyked.box2d.utils.Box2DPointUtils;
  15. import couk.psyked.box2d.utils.Box2DUtils;
  16. import couk.psyked.box2d.utils.Box2DWorldOptions;
  17. import couk.psyked.box2d.utils.library.Box2DShapeParser;
  18. import couk.psyked.box2d.utils.shape.PolygonTool;
  19. import flash.display.Sprite;
  20. import flash.events.Event;
  21. import flash.events.IOErrorEvent;
  22. import flash.events.MouseEvent;
  23. import flash.events.SecurityErrorEvent;
  24. import flash.events.TimerEvent;
  25. import flash.geom.Point;
  26. import flash.net.URLLoader;
  27. import flash.net.URLLoaderDataFormat;
  28. import flash.net.URLRequest;
  29. import flash.utils.Timer;
  30. import wumedia.vector.VectorShapes;
  31. /**
  32. *
  33. * @author James
  34. */
  35. public class Box2DWorld extends Sprite
  36. {
  37. private var _animateOnEnterFrame:Boolean;
  38. private var _debugDraw:Boolean;
  39. private var _world:b2World;
  40. private var options:Box2DWorldOptions;
  41. private var worldSprite:Sprite;
  42. /**
  43. *
  44. * @param world
  45. * @param _options
  46. */
  47. public function Box2DWorld( world:b2World, _options:Box2DWorldOptions )
  48. {
  49. options = _options;
  50. _world = world;
  51. worldSprite = Box2DUtils.addDebugDraw( world );
  52. Box2DMouseUtils.initialise( _world, options.scale, worldSprite );
  53. Box2DPointUtils.initialise( _world, options.scale, worldSprite );
  54. addEventListener( Event.ADDED_TO_STAGE, onAddedToStage );
  55. }
  56. /**
  57. *
  58. * @param e
  59. */
  60. private function onAddedToStage( e:Event ):void
  61. {
  62. removeEventListener( Event.ADDED_TO_STAGE, onAddedToStage );
  63. mouseInteraction = _deferedMouseInteraction;
  64. }
  65. /**
  66. *
  67. * @return
  68. */
  69. public function get animateOnEnterFrame():Boolean
  70. {
  71. return _animateOnEnterFrame;
  72. }
  73. /**
  74. *
  75. * @param value
  76. */
  77. public function set animateOnEnterFrame( value:Boolean ):void
  78. {
  79. if ( value )
  80. {
  81. if ( !_animateOnEnterFrame )
  82. {
  83. addEventListener( Event.ENTER_FRAME, updateBox2D );
  84. }
  85. }
  86. else
  87. {
  88. if ( _animateOnEnterFrame )
  89. {
  90. removeEventListener( Event.ENTER_FRAME, updateBox2D );
  91. }
  92. }
  93. _animateOnEnterFrame = value;
  94. }
  95. private var _mouseInteraction:Boolean;
  96. private var _deferedMouseInteraction:Boolean;
  97. /**
  98. *
  99. * @param value
  100. */
  101. public function set mouseInteraction( value:Boolean ):void
  102. {
  103. if ( stage )
  104. {
  105. if ( value )
  106. {
  107. if ( !_mouseInteraction )
  108. {
  109. // excludeUserData is an array of userData types to exclude from this behaviour
  110. stage.addEventListener( MouseEvent.MOUSE_DOWN, onMouseDown );
  111. stage.addEventListener( MouseEvent.MOUSE_UP, onMouseUp );
  112. }
  113. }
  114. else
  115. {
  116. if ( _mouseInteraction )
  117. {
  118. // excludeUserData is an array of userData types to exclude from this behaviour
  119. stage.removeEventListener( MouseEvent.MOUSE_DOWN, onMouseDown );
  120. stage.removeEventListener( MouseEvent.MOUSE_UP, onMouseUp );
  121. }
  122. }
  123. _mouseInteraction = value;
  124. _deferedMouseInteraction = value;
  125. }
  126. else
  127. {
  128. _deferedMouseInteraction = value;
  129. }
  130. }
  131. private var mouseJoint:b2MouseJoint;
  132. private var mouseDefinition:b2MouseJointDef;
  133. internal function onMouseDown( e:Event ):void
  134. {
  135. //trace( Box2DMouseUtils.getTopBodyAtMouse());
  136. var body:b2Body = Box2DMouseUtils.getTopBodyAtMouse();
  137. if ( body )
  138. {
  139. var mouseJointDef:b2MouseJointDef = new b2MouseJointDef();
  140. mouseJointDef.body1 = _world.GetGroundBody();
  141. mouseJointDef.body2 = body;
  142. mouseJointDef.target.Set( mouseX / options.scale, mouseY / options.scale );
  143. mouseJointDef.maxForce = 30000;
  144. //mouseJointDef.timeStep = ( 1 / 30 );
  145. mouseJoint = _world.CreateJoint( mouseJointDef ) as b2MouseJoint;
  146. }
  147. }
  148. internal function onMouseUp( e:Event ):void
  149. {
  150. if ( mouseJoint )
  151. {
  152. _world.DestroyJoint( mouseJoint );
  153. mouseJoint = null;
  154. }
  155. }
  156. /**
  157. *
  158. * @return
  159. */
  160. public function get mouseInteraction():Boolean
  161. {
  162. return _mouseInteraction;
  163. }
  164. /**
  165. *
  166. * @default
  167. */
  168. public var mouseInteractExclusions:Array;
  169. private var _framerateIndependantAnimation:Boolean;
  170. private var fiaTimer:Timer;
  171. /**
  172. *
  173. * @return
  174. */
  175. public function get framerateIndependantAnimation():Boolean
  176. {
  177. return _framerateIndependantAnimation;
  178. }
  179. private var framerate:Number = ( 1000 / 60 );
  180. /**
  181. *
  182. * @param value
  183. */
  184. public function set framerateIndependantAnimation( value:Boolean ):void
  185. {
  186. if ( value )
  187. {
  188. if ( !_framerateIndependantAnimation )
  189. {
  190. fiaTimer = new Timer( framerate );
  191. fiaTimer.addEventListener( TimerEvent.TIMER, updateBox2D );
  192. fiaTimer.start();
  193. }
  194. }
  195. else
  196. {
  197. if ( _framerateIndependantAnimation )
  198. {
  199. fiaTimer.removeEventListener( TimerEvent.TIMER, updateBox2D );
  200. fiaTimer.stop();
  201. fiaTimer = null;
  202. }
  203. }
  204. _framerateIndependantAnimation = value;
  205. }
  206. /**
  207. *
  208. * @return
  209. */
  210. public function get debugDraw():Boolean
  211. {
  212. return _debugDraw;
  213. }
  214. /**
  215. *
  216. * @param value
  217. */
  218. public function set debugDraw( value:Boolean ):void
  219. {
  220. if ( value )
  221. {
  222. if ( !contains( worldSprite ))
  223. {
  224. addChild( worldSprite );
  225. }
  226. }
  227. else
  228. {
  229. if ( contains( worldSprite ))
  230. {
  231. removeChild( worldSprite );
  232. }
  233. }
  234. _debugDraw = value;
  235. }
  236. /**
  237. *
  238. * @param x
  239. * @param y
  240. * @param radius
  241. * @param rotation
  242. */
  243. public function createCircle( x:int, y:int, radius:int, rotation:int = 0 ):void
  244. {
  245. var bodyDef:b2BodyDef = new b2BodyDef();
  246. bodyDef.position.Set( x / options.scale, y / options.scale );
  247. bodyDef.linearDamping = 0.25;
  248. bodyDef.angularDamping = 0.25;
  249. var shapeDef:b2CircleDef = new b2CircleDef();
  250. shapeDef.radius = radius / options.scale;
  251. shapeDef.density = 1;
  252. shapeDef.friction = 5;
  253. var body:b2Body = _world.CreateBody( bodyDef );
  254. body.SetAngle(( rotation % 360 ) * ( Math.PI / 180 ));
  255. body.CreateShape( shapeDef );
  256. body.SetMassFromShapes();
  257. }
  258. /**
  259. *
  260. * @param x
  261. * @param y
  262. * @param width
  263. * @param height
  264. * @param rotation
  265. */
  266. public function createRectangle( x:int, y:int, width:int, height:int, rotation:int = 0 ):void
  267. {
  268. var bodyDef:b2BodyDef = new b2BodyDef();
  269. bodyDef.position.Set( x / options.scale, y / options.scale );
  270. bodyDef.linearDamping = 0.25;
  271. bodyDef.angularDamping = 0.25;
  272. var shapeDef:b2PolygonDef = new b2PolygonDef();
  273. shapeDef.SetAsBox( width / options.scale, height / options.scale );
  274. shapeDef.density = 1;
  275. shapeDef.friction = 5;
  276. var body:b2Body = _world.CreateBody( bodyDef );
  277. body.SetAngle(( rotation % 360 ) * ( Math.PI / 180 ));
  278. body.CreateShape( shapeDef );
  279. body.SetMassFromShapes();
  280. }
  281. /**
  282. *
  283. * @param body1
  284. * @param body2
  285. * @param point
  286. */
  287. public function createRevoluteJoint( body1:b2Body, body2:b2Body, point:Point ):void
  288. {
  289. var jointDef:b2RevoluteJointDef = new b2RevoluteJointDef();
  290. jointDef.Initialize( body1, body2, new b2Vec2( point.x / options.scale, point.y / options.scale ));
  291. _world.CreateJoint( jointDef );
  292. }
  293. /**
  294. *
  295. * @return
  296. */
  297. public function getb2world():b2World
  298. {
  299. return _world;
  300. }
  301. /**
  302. *
  303. * @param body1
  304. * @param body2
  305. * @param _a1
  306. * @param _a2
  307. */
  308. public function createDisanceJoint( body1:b2Body, body2:b2Body, _a1:Point = null, _a2:Point = null ):void
  309. {
  310. var a1:b2Vec2;
  311. var a2:b2Vec2;
  312. if ( !_a1 )
  313. {
  314. a1 = body1.GetWorldCenter();
  315. }
  316. else
  317. {
  318. a1 = new b2Vec2( _a1.x / options.scale, _a1.y / options.scale );
  319. }
  320. if ( !_a2 )
  321. {
  322. a2 = body2.GetWorldCenter();
  323. }
  324. else
  325. {
  326. a2 = new b2Vec2( _a2.x / options.scale, _a2.y / options.scale );
  327. }
  328. var jointDef:b2DistanceJointDef = new b2DistanceJointDef();
  329. jointDef.Initialize( body1, body2, a1, a2 );
  330. _world.CreateJoint( jointDef );
  331. }
  332. internal function makeSimpleBody( p_body:b2Body, p_vertices:Array ):void
  333. {
  334. var vertArray:Array = p_vertices.slice( 0 );
  335. vertArray.reverse();
  336. var polyDef:b2PolygonDef;
  337. polyDef = new b2PolygonDef();
  338. polyDef.friction = 0.5;
  339. polyDef.density = 1.0;
  340. polyDef.vertexCount = p_vertices.length;
  341. var i:int = 0;
  342. for each ( var vertex:Point in vertArray )
  343. {
  344. polyDef.vertices[ i ].Set( vertex.x / options.scale, vertex.y / options.scale );
  345. i++;
  346. }
  347. p_body.CreateShape( polyDef );
  348. p_body.SetMassFromShapes();
  349. }
  350. /*
  351. private function makeComplexBody(p_body:B2Body, p_vertices:Array) : Void
  352. {
  353. var processedVerts = _polyTool.getTriangulatedPoly(p_vertices);
  354. if(processedVerts != null) {
  355. var tcount = cast(processedVerts.length / 3, Int);
  356. for (i in 0...tcount) {
  357. var polyDef:B2PolygonDef;
  358. polyDef = new B2PolygonDef();
  359. polyDef.friction = 0.5;
  360. if(p_static)
  361. polyDef.density = 0.0;
  362. else
  363. polyDef.density = 1.0;
  364. polyDef.vertexCount = 3;
  365. var index:int = i * 3;
  366. polyDef.vertices[0].Set(processedVerts[index].x/30,processedVerts[index].y/30);
  367. polyDef.vertices[1].Set(processedVerts[index+1].x/30,processedVerts[index+1].y/30);
  368. polyDef.vertices[2].Set(processedVerts[index+2].x/30,processedVerts[index+2].y/30);
  369. p_body.CreateShape(polyDef);
  370. }
  371. p_body.SetMassFromShapes();
  372. _bodyCount++;
  373. } else {
  374. // The polygon is bad somehow. Probably overlapping segments.
  375. // So let's recurse with the convex hull of the bad poly.
  376. makeComplexBody(p_body, _polyTool.getConvexPoly(p_vertices), p_static);
  377. }
  378. } */
  379. /**
  380. *
  381. * @param x
  382. * @param y
  383. * @param points
  384. * @param rotation
  385. */
  386. public function createComplexPolygon( x:int, y:int, points:Array, rotation:int = 0 ):void
  387. {
  388. if ( points.length < 3 )
  389. {
  390. //throw new Error( "Complex Polygons must have at least 3 points." );
  391. trace( "Complex Polygons must have at least 3 points." );
  392. }
  393. //todo: Re-implement this - it's really quite good.
  394. /* graphics.lineStyle( 1, 0xff0000 );
  395. for each ( var point:Point in points )
  396. {
  397. graphics.lineTo( point.x, point.y );
  398. } */
  399. var bodyDef:b2BodyDef = new b2BodyDef();
  400. bodyDef.position.Set( x / options.scale, y / options.scale );
  401. bodyDef.linearDamping = 0.25;
  402. bodyDef.angularDamping = 0.25;
  403. var body:b2Body = _world.CreateBody( bodyDef );
  404. body.SetAngle(( rotation % 360 ) * ( Math.PI / 180 ));
  405. // If there are more than 8 vertices, its a complex body
  406. if ( points.length > 8 )
  407. {
  408. makeComplexBody( body, points );
  409. }
  410. else
  411. {
  412. if ( PolygonTool.isPolyConvex( points ) && PolygonTool.isPolyClockwise( points ))
  413. {
  414. makeSimpleBody( body, points );
  415. }
  416. else
  417. {
  418. makeComplexBody( body, points );
  419. }
  420. }
  421. /* var processedVerts:Array = PolygonTool.getTriangulatedPoly( points );
  422. if ( processedVerts != null )
  423. {
  424. makeComplexBody( body, processedVerts );
  425. }
  426. else
  427. {
  428. makeComplexBody( body, PolygonTool.getConvexPoly( points ));
  429. } */
  430. body.SetMassFromShapes();
  431. }
  432. /**
  433. * Loads the specified library file, and extracts the named library item from it, parsing it into a Box2D object.
  434. *
  435. * @param x
  436. * @param y
  437. * @param shapeName
  438. * @param libraryName
  439. * @param rotation
  440. * @param levelOfDetail
  441. */
  442. public function createPolyFromLibraryShape( x:int, y:int, shapeName:String, libraryName:String, rotation:int = 0, levelOfDetail:uint = 5 ):void
  443. {
  444. var loader:URLLoader = new URLLoader();
  445. loader.dataFormat = URLLoaderDataFormat.BINARY;
  446. loader.addEventListener( IOErrorEvent.IO_ERROR, onError );
  447. loader.addEventListener( SecurityErrorEvent.SECURITY_ERROR, onError );
  448. loader.addEventListener( Event.COMPLETE, onLoaded );
  449. loader.load( new URLRequest( libraryName ));
  450. function onError( e:Event = null ):void
  451. {
  452. loader.removeEventListener( IOErrorEvent.IO_ERROR, onError );
  453. loader.removeEventListener( SecurityErrorEvent.SECURITY_ERROR, onError );
  454. loader.removeEventListener( Event.COMPLETE, onLoaded );
  455. // supress the errors for now.
  456. }
  457. function onLoaded( e:Event = null ):void
  458. {
  459. loader.removeEventListener( IOErrorEvent.IO_ERROR, onError );
  460. loader.removeEventListener( SecurityErrorEvent.SECURITY_ERROR, onError );
  461. loader.removeEventListener( Event.COMPLETE, onLoaded );
  462. VectorShapes.extractFromLibrary( loader.data, [ shapeName ]);
  463. //var points:Array = Box2DShapeParser.getPoints( shapeName, 1, levelOfDetail * options.scale );
  464. var points:Array = Box2DShapeParser.getPoints( shapeName, 1, levelOfDetail );
  465. if ( points )
  466. {
  467. for each ( var shapePoints:Array in points )
  468. {
  469. if ( shapePoints.length > 2 )
  470. {
  471. createComplexPolygon( x, y, shapePoints, rotation );
  472. }
  473. else
  474. {
  475. trace( "Error getting points from Shape in library." );
  476. }
  477. }
  478. }
  479. if ( e )
  480. {
  481. dispatchEvent( e );
  482. }
  483. }
  484. }
  485. internal function makeComplexBody( body:b2Body, processedVerts:Array ):void
  486. {
  487. /* var tcount:int = int( processedVerts.length / 3 );
  488. for ( var i:int = 0; i < tcount; i++ )
  489. {
  490. var shapeDef:b2PolygonDef = new b2PolygonDef();
  491. shapeDef.density = 1;
  492. shapeDef.friction = 5;
  493. shapeDef.vertexCount = 3;
  494. var index:int = i * 3;
  495. shapeDef.vertices[ 0 ].Set( processedVerts[ index ].x / 30, processedVerts[ index ].y / 30 );
  496. shapeDef.vertices[ 1 ].Set( processedVerts[ index + 1 ].x / 30, processedVerts[ index + 1 ].y / 30 );
  497. shapeDef.vertices[ 2 ].Set( processedVerts[ index + 2 ].x / 30, processedVerts[ index + 2 ].y / 30 );
  498. body.CreateShape( shapeDef );
  499. }
  500. body.SetMassFromShapes();
  501. body.CreateShape( shapeDef ); */
  502. var vertArray:Array = processedVerts.slice( 0 );
  503. if ( !PolygonTool.isPolyClockwise( vertArray ))
  504. {
  505. vertArray.reverse();
  506. }
  507. var polys:Array = PolygonTool.earClip( vertArray );
  508. if ( polys != null )
  509. {
  510. for each ( var poly:Array in polys )
  511. {
  512. //if ( poly != null )
  513. //{
  514. var shapeDef:b2PolygonDef;
  515. shapeDef = new b2PolygonDef();
  516. shapeDef.friction = 0.5;
  517. shapeDef.density = 1.0;
  518. shapeDef.vertexCount = poly.length;
  519. //var i:int = 0;
  520. //for each ( vertex in poly.nVertices )
  521. for ( var i:int = 0; i < poly.length; i++ )
  522. {
  523. shapeDef.vertices[ i ].Set( poly[ i ].x / options.scale, poly[ i ].y / options.scale );
  524. //trace( poly[ i ].x / options.scale, poly[ i ].y / options.scale );
  525. //i++;
  526. }
  527. body.CreateShape( shapeDef );
  528. //}
  529. }
  530. body.SetMassFromShapes();
  531. }
  532. else
  533. {
  534. makeComplexBody( body, PolygonTool.getConvexPoly( processedVerts ));
  535. }
  536. }
  537. /**
  538. *
  539. * @param e
  540. */
  541. public function updateBox2D( e:Event = null ):void
  542. {
  543. _world.Step(( 1 / 30 ), 10, 10 );
  544. if ( mouseJoint )
  545. {
  546. var mouseXWorldPhys:Number = mouseX / options.scale;
  547. var mouseYWorldPhys:Number = mouseY / options.scale;
  548. var p2:b2Vec2 = new b2Vec2( mouseXWorldPhys, mouseYWorldPhys );
  549. mouseJoint.SetTarget( p2 );
  550. }
  551. }
  552. /**
  553. *
  554. * @param fn
  555. */
  556. public function forEachBody( fn:Function ):void
  557. {
  558. var node:b2Body = _world.GetBodyList();
  559. while ( node )
  560. {
  561. var b:b2Body = node;
  562. node = node.GetNext();
  563. fn.apply( this, [ b ]);
  564. }
  565. }
  566. /**
  567. *
  568. * @param point
  569. * @return
  570. */
  571. public function pointTob2Vec2( point:Point ):b2Vec2
  572. {
  573. return new b2Vec2( point.x / options.scale, point.y / options.scale );
  574. }
  575. /**
  576. *
  577. * @param vec
  578. * @return
  579. */
  580. public function b2Vec2ToPoint( vec:b2Vec2 ):Point
  581. {
  582. return new Point( vec.x * options.scale, vec.y * options.scale );
  583. }
  584. }
  585. }