{"id":683,"date":"2012-08-09T15:30:56","date_gmt":"2012-08-09T14:30:56","guid":{"rendered":"http:\/\/www.aymericlamboley.fr\/blog\/?p=683"},"modified":"2014-11-01T14:53:03","modified_gmt":"2014-11-01T13:53:03","slug":"an-entity-component-systems-attempt-using-box2d","status":"publish","type":"post","link":"http:\/\/www.aymericlamboley.fr\/blog\/an-entity-component-systems-attempt-using-box2d\/","title":{"rendered":"An entity\/component system&#8217;s attempt using Box2D"},"content":{"rendered":"<p><a href=\"http:\/\/www.aymericlamboley.fr\/blog\/doubly-linked-list-and-object-pooling\/\" target=\"_blank\">One year ago<\/a>, I heard about the Entity\/Component Model for the first time. It seemed to be awesome for game design \/ development. This is 3 links\/resources for a good introduction : <a href=\"http:\/\/www.gamasutra.com\/blogs\/MeganFox\/20101208\/6590\/Game_Engines_101_The_EntityComponent_Model.php\" target=\"_blank\">Gamasutra the Entity Component Model<\/a>, <a href=\"http:\/\/t-machine.org\/index.php\/2007\/09\/03\/entity-systems-are-the-future-of-mmog-development-part-1\/\" target=\"_blank\">Entity Systems are the future of MMOG development<\/a>, <a href=\"http:\/\/www.tomseysdavies.com\/2011\/01\/23\/entity-systems\/\" target=\"_blank\">Entity Systems<\/a>. Then Richard Lord has made a very good <a href=\"http:\/\/www.richardlord.net\/blog\/what-is-an-entity-framework\" target=\"_blank\">blog post<\/a> with code example using his own framework : <a href=\"https:\/\/github.com\/richardlord\/Ash\" target=\"_blank\">Ash<\/a>. And finally, Shaun Smith&#8217;s <a href=\"http:\/\/shaun.boyblack.co.za\/blog\/2012\/08\/04\/games-and-entity-systems\/\" target=\"_blank\">experience<\/a> using Ash.<\/p>\n<p><em>All the code example is available on the new <a href=\"https:\/\/github.com\/alamboley\/Citrus-Engine\" target=\"_blank\">Citrus Engine&#8217;s GitHub<\/a> repository. Compile the src\/entity\/Main.as<\/em><\/p>\n<p>During this year, entity\/component system has always been something I wanted to try&#8230; but that was scary : this is not OOP, that&#8217;s a new way of programming. I wanted to add this system to the <a href=\"http:\/\/citrusengine.com\/\" target=\"_blank\">Citrus Engine<\/a> as an option to handle complex object. For basic object OOP is great and easy to set up, but for complex game object which may have new abilities on the fly, you&#8217;ve quickly a Class with 700 lines of code and conditions. Not easy to maintain some weeks later. The Citrus Engine handles physics engine (Box2D &amp; Nape) &amp; a simple math-based collision-detection system. However it comes built-in with a &#8220;platformer&#8221; starter-kit based on Box2D, so I made my entity\/component system&#8217;s experiments using Box2D. And that was not simple.<\/p>\n<p><!--more--><\/p>\n<p>When I heard for the first time of entity\/component system, I quickly understand that you can add behaviors very easily :<\/p>\n<pre lang=\"actionscript3\">var heroEntity:Entity = new Entity();\r\nheroEntity.add(New PhysicsComponent({x:200, y:270, width:40, height:60)).add(new InputComponent({right:Keyboard.RIGHT})).add(new ViewComponent({view:\"hero.swf\"}));<\/pre>\n<p>And add new ability in a magic way :<\/p>\n<pre lang=\"actionscript3\">heroEntity.add(new LadderAbilityComponent());<\/pre>\n<p>But what happen when you want to add to your hero the ability to climb on a ladder? You&#8217;ve to specify the new key input, an animation, hero behaviors, collision, physics&#8230; it has an impact on all your previous component! Using a physics engine like Box2D doesn&#8217;t simplify that. The <a href=\"https:\/\/github.com\/richardlord\/Ash\/tree\/master\/examples\/no-dependencies\/asteroids\/net\/richardlord\/asteroids\" target=\"_blank\">Asteroids<\/a> game example offers with Ash is quite simple. I&#8217;ve understood how it works, but using physics engine makes it all much more complex.<\/p>\n<p>Now it&#8217;s time for some code. I&#8217;ve worked on a Hero&#8217;s entity, it is able to walk, jump, duck, get hurt, give damage&#8230; with animations. That&#8217;s the hero on the <a href=\"http:\/\/citrusengine.com\/\" target=\"_blank\">Citrus Engine<\/a> site. Play with it.<br \/>\nIts class code, programmed the &#8220;classic way&#8221; based on OOP (source on <a href=\"https:\/\/github.com\/alamboley\/Citrus-Engine\/blob\/master\/libs\/com\/citrusengine\/objects\/platformer\/box2d\/Hero.as\" target=\"_blank\">GitHub<\/a> too).<\/p>\n<pre lang=\"actionscript3\">package com.citrusengine.objects.platformer.box2d\r\n{\r\n\r\n\timport Box2DAS.Common.V2;\r\n\timport Box2DAS.Dynamics.ContactEvent;\r\n\timport Box2DAS.Dynamics.b2Fixture;\r\n\r\n\timport com.citrusengine.math.MathVector;\r\n\timport com.citrusengine.objects.Box2DPhysicsObject;\r\n\timport com.citrusengine.physics.Box2DCollisionCategories;\r\n\timport com.citrusengine.utils.Box2DShapeMaker;\r\n\r\n\timport org.osflash.signals.Signal;\r\n\r\n\timport flash.display.MovieClip;\r\n\timport flash.ui.Keyboard;\r\n\timport flash.utils.clearTimeout;\r\n\timport flash.utils.getDefinitionByName;\r\n\timport flash.utils.setTimeout;\r\n\r\n\t\/**\r\n\t * This is a common, simple, yet solid implementation of a side-scrolling Hero. \r\n\t * The hero can run, jump, get hurt, and kill enemies. It dispatches signals\r\n\t * when significant events happen. The game state's logic should listen for those signals\r\n\t * to perform game state updates (such as increment coin collections).\r\n\t * \r\n\t * Don't store data on the hero object that you will need between two or more levels (such\r\n\t * as current coin count). The hero should be re-created each time a state is created or reset.\r\n\t *\/\t\r\n\tpublic class Hero extends Box2DPhysicsObject\r\n\t{\r\n\t\t\/\/properties\r\n\t\t\/**\r\n\t\t * This is the rate at which the hero speeds up when you move him left and right. \r\n\t\t *\/\r\n\t\t[Inspectable(defaultValue=\"1\")]\r\n\t\tpublic var acceleration:Number = 1;\r\n\r\n\t\t\/**\r\n\t\t * This is the fastest speed that the hero can move left or right. \r\n\t\t *\/\r\n\t\t[Inspectable(defaultValue=\"8\")]\r\n\t\tpublic var maxVelocity:Number = 8;\r\n\r\n\t\t\/**\r\n\t\t * This is the initial velocity that the hero will move at when he jumps.\r\n\t\t *\/\r\n\t\t[Inspectable(defaultValue=\"11\")]\r\n\t\tpublic var jumpHeight:Number = 11;\r\n\r\n\t\t\/**\r\n\t\t * This is the amount of \"float\" that the hero has when the player holds the jump button while jumping. \r\n\t\t *\/\r\n\t\t[Inspectable(defaultValue=\"0.3\")]\r\n\t\tpublic var jumpAcceleration:Number = 0.3;\r\n\r\n\t\t\/**\r\n\t\t * This is the y velocity that the hero must be travelling in order to kill a Baddy.\r\n\t\t *\/\r\n\t\t[Inspectable(defaultValue=\"3\")]\r\n\t\tpublic var killVelocity:Number = 3;\r\n\r\n\t\t\/**\r\n\t\t * The y velocity that the hero will spring when he kills an enemy. \r\n\t\t *\/\r\n\t\t[Inspectable(defaultValue=\"8\")]\r\n\t\tpublic var enemySpringHeight:Number = 8;\r\n\r\n\t\t\/**\r\n\t\t * The y velocity that the hero will spring when he kills an enemy while pressing the jump button. \r\n\t\t *\/\r\n\t\t[Inspectable(defaultValue=\"9\")]\r\n\t\tpublic var enemySpringJumpHeight:Number = 9;\r\n\r\n\t\t\/**\r\n\t\t * How long the hero is in hurt mode for. \r\n\t\t *\/\r\n\t\t[Inspectable(defaultValue=\"1000\")]\r\n\t\tpublic var hurtDuration:Number = 1000;\r\n\r\n\t\t\/**\r\n\t\t * The amount of kick-back that the hero jumps when he gets hurt. \r\n\t\t *\/\r\n\t\t[Inspectable(defaultValue=\"6\")]\r\n\t\tpublic var hurtVelocityX:Number = 6;\r\n\r\n\t\t\/**\r\n\t\t * The amount of kick-back that the hero jumps when he gets hurt. \r\n\t\t *\/\r\n\t\t[Inspectable(defaultValue=\"10\")]\r\n\t\tpublic var hurtVelocityY:Number = 10;\r\n\r\n\t\t\/**\r\n\t\t * Determines whether or not the hero's ducking ability is enabled.\r\n\t\t *\/\r\n\t\t[Inspectable(defaultValue=\"true\")]\r\n\t\tpublic var canDuck:Boolean = true;\r\n\r\n\t\t\/\/events\r\n\t\t\/**\r\n\t\t * Dispatched whenever the hero jumps. \r\n\t\t *\/\r\n\t\tpublic var onJump:Signal;\r\n\r\n\t\t\/**\r\n\t\t * Dispatched whenever the hero gives damage to an enemy. \r\n\t\t *\/\t\t\r\n\t\tpublic var onGiveDamage:Signal;\r\n\r\n\t\t\/**\r\n\t\t * Dispatched whenever the hero takes damage from an enemy. \r\n\t\t *\/\t\t\r\n\t\tpublic var onTakeDamage:Signal;\r\n\r\n\t\t\/**\r\n\t\t * Dispatched whenever the hero's animation changes. \r\n\t\t *\/\t\t\r\n\t\tpublic var onAnimationChange:Signal;\r\n\r\n\t\tprotected var _groundContacts:Array = [];\/\/Used to determine if he's on ground or not.\r\n\t\tprotected var _enemyClass:Class = Baddy;\r\n\t\tprotected var _onGround:Boolean = false;\r\n\t\tprotected var _springOffEnemy:Number = -1;\r\n\t\tprotected var _hurtTimeoutID:Number;\r\n\t\tprotected var _hurt:Boolean = false;\r\n\t\tprotected var _friction:Number = 0.75;\r\n\t\tprotected var _playerMovingHero:Boolean = false;\r\n\t\tprotected var _controlsEnabled:Boolean = true;\r\n\t\tprotected var _ducking:Boolean = false;\r\n\t\tprotected var _combinedGroundAngle:Number = 0;\r\n\r\n\t\tpublic static function Make(name:String, x:Number, y:Number, width:Number, height:Number, view:* = null):Hero\r\n\t\t{\r\n\t\t\tif (view == null) view = MovieClip;\r\n\t\t\treturn new Hero(name, { x: x, y: y, width: width, height: height, view: view } );\r\n\t\t}\r\n\r\n\t\t\/**\r\n\t\t * Creates a new hero object.\r\n\t\t *\/\t\t\r\n\t\tpublic function Hero(name:String, params:Object = null)\r\n\t\t{\r\n\t\t\tsuper(name, params);\r\n\r\n\t\t\tonJump = new Signal();\r\n\t\t\tonGiveDamage = new Signal();\r\n\t\t\tonTakeDamage = new Signal();\r\n\t\t\tonAnimationChange = new Signal();\r\n\t\t}\r\n\r\n\t\toverride public function destroy():void\r\n\t\t{\r\n\t\t\t_fixture.removeEventListener(ContactEvent.PRE_SOLVE, handlePreSolve);\r\n\t\t\t_fixture.removeEventListener(ContactEvent.BEGIN_CONTACT, handleBeginContact);\r\n\t\t\t_fixture.removeEventListener(ContactEvent.END_CONTACT, handleEndContact);\r\n\t\t\tclearTimeout(_hurtTimeoutID);\r\n\t\t\tonJump.removeAll();\r\n\t\t\tonGiveDamage.removeAll();\r\n\t\t\tonTakeDamage.removeAll();\r\n\t\t\tonAnimationChange.removeAll();\r\n\t\t\tsuper.destroy();\r\n\t\t}\r\n\r\n\t\t\/**\r\n\t\t * Whether or not the player can move and jump with the hero. \r\n\t\t *\/\t\r\n\t\tpublic function get controlsEnabled():Boolean\r\n\t\t{\r\n\t\t\treturn _controlsEnabled;\r\n\t\t}\r\n\r\n\t\tpublic function set controlsEnabled(value:Boolean):void\r\n\t\t{\r\n\t\t\t_controlsEnabled = value;\r\n\r\n\t\t\tif (!_controlsEnabled)\r\n\t\t\t\t_fixture.SetFriction(_friction);\r\n\t\t}\r\n\r\n\t\t\/**\r\n\t\t * Returns true if the hero is on the ground and can jump. \r\n\t\t *\/\t\t\r\n\t\tpublic function get onGround():Boolean\r\n\t\t{\r\n\t\t\treturn _onGround;\r\n\t\t}\r\n\r\n\t\t\/**\r\n\t\t * The Hero uses the enemyClass parameter to know who he can kill (and who can kill him).\r\n\t\t * Use this setter to to pass in which base class the hero's enemy should be, in String form\r\n\t\t * or Object notation.\r\n\t\t * For example, if you want to set the \"Baddy\" class as your hero's enemy, pass\r\n\t\t * \"com.citrusengine.objects.platformer.Baddy\", or Baddy (with no quotes). Only String\r\n\t\t * form will work when creating objects via a level editor.\r\n\t\t *\/\r\n\t\t[Inspectable(defaultValue=\"com.citrusengine.objects.platformer.box2d.Baddy\",type=\"String\")]\r\n\t\tpublic function set enemyClass(value:*):void\r\n\t\t{\r\n\t\t\tif (value is String)\r\n\t\t\t\t_enemyClass = getDefinitionByName(value as String) as Class;\r\n\t\t\telse if (value is Class)\r\n\t\t\t\t_enemyClass = value;\r\n\t\t}\r\n\r\n\t\t\/**\r\n\t\t * This is the amount of friction that the hero will have. Its value is multiplied against the\r\n\t\t * friction value of other physics objects.\r\n\t\t *\/\t\r\n\t\tpublic function get friction():Number\r\n\t\t{\r\n\t\t\treturn _friction;\r\n\t\t}\r\n\r\n\t\t[Inspectable(defaultValue=\"0.75\")]\r\n\t\tpublic function set friction(value:Number):void\r\n\t\t{\r\n\t\t\t_friction = value;\r\n\r\n\t\t\tif (_fixture)\r\n\t\t\t{\r\n\t\t\t\t_fixture.SetFriction(_friction);\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\toverride public function update(timeDelta:Number):void\r\n\t\t{\r\n\t\t\tsuper.update(timeDelta);\r\n\r\n\t\t\tvar velocity:V2 = _body.GetLinearVelocity();\r\n\r\n\t\t\tif (controlsEnabled)\r\n\t\t\t{\r\n\t\t\t\tvar moveKeyPressed:Boolean = false;\r\n\r\n\t\t\t\t_ducking = (_ce.input.isDown(Keyboard.DOWN) &amp;&amp; _onGround &amp;&amp; canDuck);\r\n\r\n\t\t\t\tif (_ce.input.isDown(Keyboard.RIGHT) &amp;&amp; !_ducking)\r\n\t\t\t\t{\r\n\t\t\t\t\tvelocity = V2.add(velocity, getSlopeBasedMoveAngle());\r\n\t\t\t\t\tmoveKeyPressed = true;\r\n\t\t\t\t}\r\n\r\n\t\t\t\tif (_ce.input.isDown(Keyboard.LEFT) &amp;&amp; !_ducking)\r\n\t\t\t\t{\r\n\t\t\t\t\tvelocity = V2.subtract(velocity, getSlopeBasedMoveAngle());\r\n\t\t\t\t\tmoveKeyPressed = true;\r\n\t\t\t\t}\r\n\r\n\t\t\t\t\/\/If player just started moving the hero this tick.\r\n\t\t\t\tif (moveKeyPressed &amp;&amp; !_playerMovingHero)\r\n\t\t\t\t{\r\n\t\t\t\t\t_playerMovingHero = true;\r\n\t\t\t\t\t_fixture.SetFriction(0); \/\/Take away friction so he can accelerate.\r\n\t\t\t\t}\r\n\t\t\t\t\/\/Player just stopped moving the hero this tick.\r\n\t\t\t\telse if (!moveKeyPressed &amp;&amp; _playerMovingHero)\r\n\t\t\t\t{\r\n\t\t\t\t\t_playerMovingHero = false;\r\n\t\t\t\t\t_fixture.SetFriction(_friction); \/\/Add friction so that he stops running\r\n\t\t\t\t}\r\n\r\n\t\t\t\tif (_onGround &amp;&amp; _ce.input.justPressed(Keyboard.SPACE) &amp;&amp; !_ducking)\r\n\t\t\t\t{\r\n\t\t\t\t\tvelocity.y = -jumpHeight;\r\n\t\t\t\t\tonJump.dispatch();\r\n\t\t\t\t}\r\n\r\n\t\t\t\tif (_ce.input.isDown(Keyboard.SPACE) &amp;&amp; !_onGround &amp;&amp; velocity.y &lt; 0) \t\t\t\t{ \t\t\t\t\tvelocity.y -= jumpAcceleration; \t\t\t\t} \t\t\t\t \t\t\t\tif (_springOffEnemy != -1) \t\t\t\t{ \t\t\t\t\tif (_ce.input.isDown(Keyboard.SPACE)) \t\t\t\t\t\tvelocity.y = -enemySpringJumpHeight; \t\t\t\t\telse \t\t\t\t\t\tvelocity.y = -enemySpringHeight; \t\t\t\t\t_springOffEnemy = -1; \t\t\t\t} \t\t\t\t \t\t\t\t\/\/Cap velocities \t\t\t\tif (velocity.x &gt; (maxVelocity))\r\n\t\t\t\t\tvelocity.x = maxVelocity;\r\n\t\t\t\telse if (velocity.x &lt; (-maxVelocity))\r\n\t\t\t\t\tvelocity.x = -maxVelocity;\r\n\r\n\t\t\t\t\/\/update physics with new velocity\r\n\t\t\t\t_body.SetLinearVelocity(velocity);\r\n\t\t\t}\r\n\r\n\t\t\tupdateAnimation();\r\n\t\t}\r\n\r\n\t\t\/**\r\n\t\t * Returns the absolute walking speed, taking moving platforms into account.\r\n\t\t * Isn't super performance-light, so use sparingly.\r\n\t\t *\/\r\n\t\tpublic function getWalkingSpeed():Number\r\n\t\t{\r\n\t\t\tvar groundVelocityX:Number = 0;\r\n\t\t\tfor each (var groundContact:b2Fixture in _groundContacts)\r\n\t\t\t{\r\n\t\t\t\tgroundVelocityX += groundContact.GetBody().GetLinearVelocity().x;\r\n\t\t\t}\r\n\r\n\t\t\treturn _body.GetLinearVelocity().x - groundVelocityX;\r\n\t\t}\r\n\r\n\t\t\/**\r\n\t\t * Hurts the hero, disables his controls for a little bit, and dispatches the onTakeDamage signal. \r\n\t\t *\/\t\t\r\n\t\tpublic function hurt():void\r\n\t\t{\r\n\t\t\t_hurt = true;\r\n\t\t\tcontrolsEnabled = false;\r\n\t\t\t_hurtTimeoutID = setTimeout(endHurtState, hurtDuration);\r\n\t\t\tonTakeDamage.dispatch();\r\n\r\n\t\t\t\/\/Makes sure that the hero is not frictionless while his control is disabled\r\n\t\t\tif (_playerMovingHero)\r\n\t\t\t{\r\n\t\t\t\t_playerMovingHero = false;\r\n\t\t\t\t_fixture.SetFriction(_friction);\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\toverride protected function defineBody():void\r\n\t\t{\r\n\t\t\tsuper.defineBody();\r\n\t\t\t_bodyDef.fixedRotation = true;\r\n\t\t\t_bodyDef.allowSleep = false;\r\n\t\t}\r\n\r\n\t\toverride protected function createShape():void\r\n\t\t{\r\n\t\t\t_shape = Box2DShapeMaker.BeveledRect(_width, _height, 0.1);\r\n\t\t}\r\n\r\n\t\toverride protected function defineFixture():void\r\n\t\t{\r\n\t\t\tsuper.defineFixture();\r\n\t\t\t_fixtureDef.friction = _friction;\r\n\t\t\t_fixtureDef.restitution = 0;\r\n\t\t\t_fixtureDef.filter.categoryBits = Box2DCollisionCategories.Get(\"GoodGuys\");\r\n\t\t\t_fixtureDef.filter.maskBits = Box2DCollisionCategories.GetAll();\r\n\t\t}\r\n\r\n\t\toverride protected function createFixture():void\r\n\t\t{\r\n\t\t\tsuper.createFixture();\r\n\t\t\t_fixture.m_reportPreSolve = true;\r\n\t\t\t_fixture.m_reportBeginContact = true;\r\n\t\t\t_fixture.m_reportEndContact = true;\r\n\t\t\t_fixture.addEventListener(ContactEvent.PRE_SOLVE, handlePreSolve);\r\n\t\t\t_fixture.addEventListener(ContactEvent.BEGIN_CONTACT, handleBeginContact);\r\n\t\t\t_fixture.addEventListener(ContactEvent.END_CONTACT, handleEndContact);\r\n\t\t}\r\n\r\n\t\tprotected function handlePreSolve(e:ContactEvent):void \r\n\t\t{\r\n\t\t\tif (!_ducking)\r\n\t\t\t\treturn;\r\n\r\n\t\t\tvar other:Box2DPhysicsObject = e.other.GetBody().GetUserData() as Box2DPhysicsObject;\r\n\r\n\t\t\tvar heroTop:Number = y;\r\n\t\t\tvar objectBottom:Number = other.y + (other.height \/ 2);\r\n\r\n\t\t\tif (objectBottom &lt; heroTop)\r\n\t\t\t\te.contact.Disable();\r\n\t\t}\r\n\r\n\t\tprotected function handleBeginContact(e:ContactEvent):void\r\n\t\t{\r\n\t\t\tvar collider:Box2DPhysicsObject = e.other.GetBody().GetUserData();\r\n\r\n\t\t\tif (_enemyClass &amp;&amp; collider is _enemyClass)\r\n\t\t\t{\r\n\t\t\t\tif (_body.GetLinearVelocity().y &lt; killVelocity &amp;&amp; !_hurt) \t\t\t\t{ \t\t\t\t\thurt(); \t\t\t\t\t \t\t\t\t\t\/\/fling the hero \t\t\t\t\tvar hurtVelocity:V2 = _body.GetLinearVelocity(); \t\t\t\t\thurtVelocity.y = -hurtVelocityY; \t\t\t\t\thurtVelocity.x = hurtVelocityX; \t\t\t\t\tif (collider.x &gt; x)\r\n\t\t\t\t\t\thurtVelocity.x = -hurtVelocityX;\r\n\t\t\t\t\t_body.SetLinearVelocity(hurtVelocity);\r\n\t\t\t\t}\r\n\t\t\t\telse\r\n\t\t\t\t{\r\n\t\t\t\t\t_springOffEnemy = collider.y - height;\r\n\t\t\t\t\tonGiveDamage.dispatch();\r\n\t\t\t\t}\r\n\t\t\t}\r\n\r\n\t\t\t\/\/Collision angle\r\n\t\t\tif (e.normal) \/\/The normal property doesn't come through all the time. I think doesn't come through against sensors.\r\n\t\t\t{\r\n\t\t\t\tvar collisionAngle:Number = new MathVector(e.normal.x, e.normal.y).angle * 180 \/ Math.PI;\r\n\t\t\t\tif (collisionAngle &gt; 45 &amp;&amp; collisionAngle &lt; 135)\r\n\t\t\t\t{\r\n\t\t\t\t\t_groundContacts.push(e.other);\r\n\t\t\t\t\t_onGround = true;\r\n\t\t\t\t\tupdateCombinedGroundAngle();\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tprotected function handleEndContact(e:ContactEvent):void\r\n\t\t{\r\n\t\t\t\/\/Remove from ground contacts, if it is one.\r\n\t\t\tvar index:int = _groundContacts.indexOf(e.other);\r\n\t\t\tif (index != -1)\r\n\t\t\t{\r\n\t\t\t\t_groundContacts.splice(index, 1);\r\n\t\t\t\tif (_groundContacts.length == 0)\r\n\t\t\t\t\t_onGround = false;\r\n\t\t\t\tupdateCombinedGroundAngle();\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tprotected function getSlopeBasedMoveAngle():V2\r\n\t\t{\r\n\t\t\treturn new V2(acceleration, 0).rotate(_combinedGroundAngle);\r\n\t\t}\r\n\r\n\t\tprotected function updateCombinedGroundAngle():void\r\n\t\t{\r\n\t\t\t_combinedGroundAngle = 0;\r\n\r\n\t\t\tif (_groundContacts.length == 0)\r\n\t\t\t\treturn;\r\n\r\n\t\t\tfor each (var contact:b2Fixture in _groundContacts)\r\n\t\t\t\tvar angle:Number = contact.GetBody().GetAngle();\r\n\r\n\t\t\tvar turn:Number = 45 * Math.PI \/ 180;\r\n\t\t\tangle = angle % turn;\r\n\t\t\t_combinedGroundAngle += angle;\r\n\t\t\t_combinedGroundAngle \/= _groundContacts.length;\r\n\t\t}\r\n\r\n\t\tprotected function endHurtState():void\r\n\t\t{\r\n\t\t\t_hurt = false;\r\n\t\t\tcontrolsEnabled = true;\r\n\t\t}\r\n\r\n\t\tprotected function updateAnimation():void\r\n\t\t{\r\n\t\t\tvar prevAnimation:String = _animation;\r\n\r\n\t\t\tvar velocity:V2 = _body.GetLinearVelocity();\r\n\t\t\tif (_hurt)\r\n\t\t\t{\r\n\t\t\t\t_animation = \"hurt\";\r\n\t\t\t}\r\n\t\t\telse if (!_onGround)\r\n\t\t\t{\r\n\t\t\t\t_animation = \"jump\";\r\n\t\t\t}\r\n\t\t\telse if (_ducking)\r\n\t\t\t{\r\n\t\t\t\t_animation = \"duck\";\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\tvar walkingSpeed:Number = getWalkingSpeed();\r\n\t\t\t\tif (walkingSpeed &lt; -acceleration) \t\t\t\t{ \t\t\t\t\t_inverted = true; \t\t\t\t\t_animation = \"walk\"; \t\t\t\t} \t\t\t\telse if (walkingSpeed &gt; acceleration)\r\n\t\t\t\t{\r\n\t\t\t\t\t_inverted = false;\r\n\t\t\t\t\t_animation = \"walk\";\r\n\t\t\t\t}\r\n\t\t\t\telse\r\n\t\t\t\t{\r\n\t\t\t\t\t_animation = \"idle\";\r\n\t\t\t\t}\r\n\t\t\t}\r\n\r\n\t\t\tif (prevAnimation != _animation)\r\n\t\t\t{\r\n\t\t\t\tonAnimationChange.dispatch();\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n}<\/pre>\n<p>You&#8217;ve certainly noticed that the Physics code is the most important and long part. It is very difficult to separate it into different components.<\/p>\n<p>Let&#8217;s show some code of my attempt to add an entity\/component system to the Citrus Engine using the Hero as an example :<br \/>\nThe GameState class :<\/p>\n<pre lang=\"actionscript3\">package entity {\r\n\r\n\timport com.citrusengine.core.State;\r\n\timport com.citrusengine.objects.platformer.box2d.Platform;\r\n\timport com.citrusengine.physics.Box2D;\r\n\timport com.citrusengine.system.Entity;\r\n\timport com.citrusengine.system.components.InputComponent;\r\n\timport com.citrusengine.system.components.box2d.hero.HeroCollisionComponent;\r\n\timport com.citrusengine.system.components.box2d.hero.HeroMovementComponent;\r\n\timport com.citrusengine.system.components.box2d.hero.HeroPhysicsComponent;\r\n\timport com.citrusengine.system.components.box2d.hero.HeroViewComponent;\r\n\r\n\t\/**\r\n\t * @author Aymeric\r\n\t *\/\r\n\tpublic class EntityGameState extends State {\r\n\r\n\t\tprivate var heroEntity : Entity;\r\n\t\tprivate var input : InputComponent;\r\n\t\tprivate var _view : HeroViewComponent;\r\n\t\tprivate var physics : HeroPhysicsComponent;\r\n\r\n\t\tpublic function EntityGameState() {\r\n\r\n\t\t\tsuper();\r\n\t\t}\r\n\r\n\t\toverride public function initialize():void {\r\n\r\n\t\t\tsuper.initialize();\r\n\r\n\t\t\tvar box2d:Box2D = new Box2D(\"box2D\");\r\n\t\t\t\/\/box2d.visible = true;\r\n\t\t\tadd(box2d);\r\n\r\n\t\t\theroEntity = new Entity(\"heroEntity\");\r\n\r\n\t\t\tphysics = new HeroPhysicsComponent(\"physics\", {x:200, y:270, width:40, height:60, entity:heroEntity});\r\n\t\t\tinput = new InputComponent(\"input\", {entity:heroEntity});\r\n\t\t\tvar collision:HeroCollisionComponent = new HeroCollisionComponent(\"collision\", {entity:heroEntity});\r\n\t\t\tvar move:HeroMovementComponent = new HeroMovementComponent(\"move\", {entity:heroEntity});\r\n\t\t\t_view = new HeroViewComponent(\"view\", {view:\"Patch.swf\", entity:heroEntity});\r\n\r\n\t\t\theroEntity.add(physics).add(input).add(collision).add(move).add(_view);\r\n\t\t\theroEntity.initialize();\r\n\r\n\t\t\taddEntity(heroEntity, _view);\r\n\r\n\t\t\tadd(new Platform(\"platform\", {x:600, y:350, width:1800, height:20}));\r\n\t\t}\r\n\r\n\t}\r\n}<\/pre>\n<p>A Hero is defined by its physics, input keys, collision management, movement, and a graphic view\/object. Then it is added to the state which manages update and object destruction.<\/p>\n<p>The Entity class :<\/p>\n<pre lang=\"actionscript3\">package com.citrusengine.system {\r\n\r\n\timport com.citrusengine.core.CitrusObject;\r\n\r\n\timport flash.utils.Dictionary;\r\n\r\n\t\/**\r\n\t * A game entity is compound by components. The entity serves as a link to communicate between components.\r\n\t * It extends the CitrusObject class to enjoy its params setter.\r\n\t *\/\r\n\tpublic class Entity extends CitrusObject {\r\n\r\n\t\tpublic var components:Dictionary;\r\n\r\n\t\tpublic function Entity(name:String, params:Object = null) {\r\n\r\n\t\t\tsuper(name, params);\r\n\r\n\t\t\tcomponents = new Dictionary();\r\n\t\t}\r\n\r\n\t\t\/**\r\n\t\t * Add a component to the entity.\r\n\t\t *\/\r\n\t\tpublic function add(component:Component):Entity {\r\n\r\n\t\t\tcomponents[component.name] = component;\r\n\r\n\t\t\treturn this;\r\n\t\t}\r\n\r\n\t\t\/**\r\n\t\t * Remove a component from the entity.\r\n\t\t *\/\r\n\t\tpublic function remove(component:Component):void {\r\n\r\n\t\t\tif (components[component.name]) {\r\n\t\t\t\tcomponent.destroy();\r\n\t\t\t\tdelete components[component.name];\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t\/**\r\n\t\t * After all the components have been added call this function to perform an init on them.\r\n\t\t * Mostly used if you want to access to other components through the entity.\r\n\t\t *\/\r\n\t\tpublic function initialize():void {\r\n\r\n\t\t\tfor each (var component:Component in components) {\r\n\t\t\t\tcomponent.initialize();\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t\/**\r\n\t\t * Destroy the entity and its components.\r\n\t\t *\/\r\n\t\toverride public function destroy():void {\r\n\r\n\t\t\tfor each (var component:Component in components) {\r\n\t\t\t\tcomponent.destroy();\r\n\t\t\t}\r\n\r\n\t\t\tcomponents = null;\r\n\r\n\t\t\tsuper.destroy();\r\n\t\t}\r\n\r\n\t\t\/**\r\n\t\t * Perform an update on all entity's components.\r\n\t\t *\/\r\n\t\toverride public function update(timeDelta:Number):void {\r\n\r\n\t\t\tfor each (var component:Component in components) {\r\n\t\t\t\tcomponent.update(timeDelta);\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n}<\/pre>\n<p>Using dictionary and for each loop is not very optimized, but it&#8217;s ok for this entity\/component system&#8217;s test.<br \/>\nThe Component class :<\/p>\n<pre lang=\"actionscript3\">package com.citrusengine.system {\r\n\r\n\timport com.citrusengine.core.CitrusEngine;\r\n\timport com.citrusengine.core.CitrusObject;\r\n\r\n\t\/**\r\n\t * A component is an object dedicate to a (single) task for an entity : physics, collision, inputs, view, movement... management.\r\n\t * You will use an entity when your object become too much complex to manage into a single class.\r\n\t * Preferably if you use a physics engine, create at first the entity's physics component.\r\n\t * It extends the CitrusObject class to enjoy its params setter.\r\n\t *\/\r\n\tpublic class Component extends CitrusObject {\r\n\r\n\t\tpublic var entity:Entity;\r\n\r\n\t\tprotected var _ce:CitrusEngine;\r\n\r\n\t\tpublic function Component(name:String, params:Object = null) {\r\n\r\n\t\t\tsuper(name, params);\r\n\r\n\t\t\t_ce = CitrusEngine.getInstance();\r\n\t\t}\r\n\r\n\t\t\/**\r\n\t\t * Register other components in your component class in this function.\r\n\t\t * It should be call after all components have been added to an entity.\r\n\t\t *\/\r\n\t\tpublic function initialize():void {\r\n\r\n\t\t}\r\n\r\n\t\t\/**\r\n\t\t * Destroy the component, most of the time called by its entity.\r\n\t\t *\/\r\n\t\toverride public function destroy():void {\r\n\r\n\t\t\tsuper.destroy();\r\n\t\t}\r\n\r\n\t\t\/**\r\n\t\t * Perform an update on the component, called by its entity.\r\n\t\t *\/\r\n\t\toverride public function update(timeDelta:Number):void {\r\n\t\t}\r\n\r\n\t}\r\n}<\/pre>\n<p>Wait! In Ash, a component only stores data. It doesn&#8217;t have logic, but System does. At the moment, I&#8217;ve chosen to add logic in components. I may separate them later.<\/p>\n<p>The Input &amp; View components (don&#8217;t depend directly of Starling, flash display list, Box2D, Nape&#8230;) :<\/p>\n<pre lang=\"actionscript3\">package com.citrusengine.system.components {\r\n\r\n\timport com.citrusengine.system.Component;\r\n\r\n\timport flash.ui.Keyboard;\r\n\r\n\t\/**\r\n\t * An input component, it will inform if the key is down, just pressed or just released.\r\n\t *\/\r\n\tpublic class InputComponent extends Component {\r\n\r\n\t\tpublic var rightKeyIsDown:Boolean = false;\r\n\t\tpublic var leftKeyIsDown:Boolean = false;\r\n\t\tpublic var downKeyIsDown:Boolean = false;\r\n\t\tpublic var spaceKeyIsDown:Boolean = false;\r\n\t\tpublic var spaceKeyJustPressed:Boolean = false;\r\n\r\n\t\tpublic function InputComponent(name:String, params:Object = null) {\r\n\t\t\tsuper(name, params);\r\n\t\t}\r\n\r\n\t\toverride public function update(timeDelta:Number):void {\r\n\r\n\t\t\tsuper.update(timeDelta);\r\n\r\n\t\t\trightKeyIsDown = _ce.input.isDown(Keyboard.RIGHT);\r\n\t\t\tleftKeyIsDown = _ce.input.isDown(Keyboard.LEFT);\r\n\t\t\tdownKeyIsDown = _ce.input.isDown(Keyboard.DOWN);\r\n\t\t\tspaceKeyIsDown = _ce.input.isDown(Keyboard.SPACE);\r\n\t\t\tspaceKeyJustPressed = _ce.input.justPressed(Keyboard.SPACE);\r\n\t\t}\r\n\t}\r\n}<\/pre>\n<pre lang=\"actionscript3\">package com.citrusengine.system.components {\r\n\r\n\timport com.citrusengine.system.Component;\r\n\timport com.citrusengine.view.ISpriteView;\r\n\r\n\timport org.osflash.signals.Signal;\r\n\r\n\timport flash.display.MovieClip;\r\n\r\n\t\/**\r\n\t * The view component, it manages everything to set the view.\r\n\t * Extend it to handle animation.\r\n\t *\/\r\n\tpublic class ViewComponent extends Component implements ISpriteView {\r\n\r\n\t\tpublic var onAnimationChange:Signal;\r\n\r\n\t\tprotected var _x:Number = 0;\r\n\t\tprotected var _y:Number = 0;\r\n\t\tprotected var _rotation:Number = 0;\r\n\t\tprotected var _inverted:Boolean = false;\r\n\t\tprotected var _parallax:Number = 1;\r\n\t\tprotected var _animation:String = \"\";\r\n\t\tprotected var _visible:Boolean = true;\r\n\t\tprotected var _view:* = MovieClip;\r\n\r\n\t\tprivate var _group:Number = 0;\r\n\t\tprivate var _offsetX:Number = 0;\r\n\t\tprivate var _offsetY:Number = 0;\r\n\t\tprivate var _registration:String = \"center\";\r\n\r\n\t\tpublic function ViewComponent(name:String, params:Object = null) {\r\n\t\t\tsuper(name, params);\r\n\r\n\t\t\tonAnimationChange = new Signal();\r\n\t\t}\r\n\r\n\t\toverride public function update(timeDelta:Number):void {\r\n\r\n\t\t\tsuper.update(timeDelta);\r\n\t\t}\r\n\r\n\t\toverride public function destroy():void {\r\n\r\n\t\t\tonAnimationChange.removeAll();\r\n\r\n\t\t\tsuper.destroy();\r\n\t\t}\r\n\r\n\t\tpublic function get x():Number {\r\n\t\t\treturn _x;\r\n\t\t}\r\n\r\n\t\tpublic function set x(value:Number):void {\r\n\t\t\t_x = value;\r\n\t\t}\r\n\r\n\t\tpublic function get y():Number {\r\n\t\t\treturn _y;\r\n\t\t}\r\n\r\n\t\tpublic function set y(value:Number):void {\r\n\t\t\t_y = value;\r\n\t\t}\r\n\r\n\t\tpublic function get rotation():Number {\r\n\t\t\treturn _rotation;\r\n\t\t}\r\n\r\n\t\tpublic function set rotation(value:Number):void {\r\n\t\t\t_rotation = value;\r\n\t\t}\r\n\r\n\t\tpublic function get parallax():Number {\r\n\t\t\treturn _parallax;\r\n\t\t}\r\n\r\n\t\tpublic function set parallax(value:Number):void {\r\n\t\t\t_parallax = value;\r\n\t\t}\r\n\r\n\t\tpublic function get group():Number\r\n\t\t{\r\n\t\t\treturn _group;\r\n\t\t}\r\n\r\n\t\tpublic function set group(value:Number):void\r\n\t\t{\r\n\t\t\t_group = value;\r\n\t\t}\r\n\r\n\t\tpublic function get visible():Boolean\r\n\t\t{\r\n\t\t\treturn _visible;\r\n\t\t}\r\n\r\n\t\tpublic function set visible(value:Boolean):void\r\n\t\t{\r\n\t\t\t_visible = value;\r\n\t\t}\r\n\r\n\t\tpublic function get view():*\r\n\t\t{\r\n\t\t\treturn _view;\r\n\t\t}\r\n\r\n\t\tpublic function set view(value:*):void\r\n\t\t{\r\n\t\t\t_view = value;\r\n\t\t}\r\n\r\n\t\tpublic function get animation():String\r\n\t\t{\r\n\t\t\treturn _animation;\r\n\t\t}\r\n\r\n\t\tpublic function get inverted():Boolean\r\n\t\t{\r\n\t\t\treturn _inverted;\r\n\t\t}\r\n\r\n\t\tpublic function get offsetX():Number\r\n\t\t{\r\n\t\t\treturn _offsetX;\r\n\t\t}\r\n\r\n\t\tpublic function set offsetX(value:Number):void\r\n\t\t{\r\n\t\t\t_offsetX = value;\r\n\t\t}\r\n\r\n\t\tpublic function get offsetY():Number\r\n\t\t{\r\n\t\t\treturn _offsetY;\r\n\t\t}\r\n\r\n\t\tpublic function set offsetY(value:Number):void\r\n\t\t{\r\n\t\t\t_offsetY = value;\r\n\t\t}\r\n\r\n\t\tpublic function get registration():String\r\n\t\t{\r\n\t\t\treturn _registration;\r\n\t\t}\r\n\r\n\t\tpublic function set registration(value:String):void\r\n\t\t{\r\n\t\t\t_registration = value;\r\n\t\t}\r\n\t}\r\n}<\/pre>\n<p>Now some Box2D components : Box2D physics, collision handler, movement :<\/p>\n<pre lang=\"actionscript3\">package com.citrusengine.system.components.box2d {\r\n\r\n\timport Box2DAS.Collision.Shapes.b2CircleShape;\r\n\timport Box2DAS.Collision.Shapes.b2PolygonShape;\r\n\timport Box2DAS.Collision.Shapes.b2Shape;\r\n\timport Box2DAS.Common.V2;\r\n\timport Box2DAS.Dynamics.b2Body;\r\n\timport Box2DAS.Dynamics.b2BodyDef;\r\n\timport Box2DAS.Dynamics.b2Fixture;\r\n\timport Box2DAS.Dynamics.b2FixtureDef;\r\n\r\n\timport com.citrusengine.core.CitrusEngine;\r\n\timport com.citrusengine.physics.Box2D;\r\n\timport com.citrusengine.physics.Box2DCollisionCategories;\r\n\timport com.citrusengine.system.Component;\r\n\r\n\t\/**\r\n\t * The base's physics Box2D Component. Manage (just) the physics creation.\r\n\t *\/\r\n\tpublic class Box2DComponent extends Component {\r\n\r\n\t\tprotected var _box2D:Box2D;\r\n\t\tprotected var _bodyDef:b2BodyDef;\r\n\t\tprotected var _body:b2Body;\r\n\t\tprotected var _shape:b2Shape;\r\n\t\tprotected var _fixtureDef:b2FixtureDef;\r\n\t\tprotected var _fixture:b2Fixture;\r\n\r\n\t\tprotected var _x:Number = 0;\r\n\t\tprotected var _y:Number = 0;\r\n\t\tprotected var _rotation:Number = 0;\r\n\t\tprotected var _width:Number = 1;\r\n\t\tprotected var _height:Number = 1;\r\n\t\tprotected var _radius:Number = 0;\r\n\r\n\t\tpublic function Box2DComponent(name:String, params:Object = null) {\r\n\r\n\t\t\t_box2D = CitrusEngine.getInstance().state.getFirstObjectByType(Box2D) as Box2D;\r\n\r\n\t\t\tsuper(name, params);\r\n\r\n\t\t\tdefineBody();\r\n\t\t\tcreateBody();\r\n\t\t\tcreateShape();\r\n\t\t\tdefineFixture();\r\n\t\t\tcreateFixture();\r\n\t\t\tdefineJoint();\r\n\t\t\tcreateJoint();\r\n\t\t}\r\n\r\n\t\toverride public function destroy():void {\r\n\r\n\t\t\t_body.destroy();\r\n\t\t\t_fixtureDef.destroy();\r\n\t\t\t_shape.destroy();\r\n\t\t\t_bodyDef.destroy();\r\n\r\n\t\t\tsuper.destroy();\r\n\t\t}\r\n\r\n\t\tpublic function get x():Number\r\n\t\t{\r\n\t\t\tif (_body)\r\n\t\t\t\treturn _body.GetPosition().x * _box2D.scale;\r\n\t\t\telse\r\n\t\t\t\treturn _x * _box2D.scale;\r\n\t\t}\r\n\r\n\t\tpublic function set x(value:Number):void\r\n\t\t{\r\n\t\t\t_x = value \/ _box2D.scale;\r\n\r\n\t\t\tif (_body)\r\n\t\t\t{\r\n\t\t\t\tvar pos:V2 = _body.GetPosition();\r\n\t\t\t\tpos.x = _x;\r\n\t\t\t\t_body.SetTransform(pos, _body.GetAngle());\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tpublic function get y():Number\r\n\t\t{\r\n\t\t\tif (_body)\r\n\t\t\t\treturn _body.GetPosition().y * _box2D.scale;\r\n\t\t\telse\r\n\t\t\t\treturn _y * _box2D.scale;\r\n\t\t}\r\n\r\n\t\tpublic function set y(value:Number):void\r\n\t\t{\r\n\t\t\t_y = value \/ _box2D.scale;\r\n\r\n\t\t\tif (_body)\r\n\t\t\t{\r\n\t\t\t\tvar pos:V2 = _body.GetPosition();\r\n\t\t\t\tpos.y = _y;\r\n\t\t\t\t_body.SetTransform(pos, _body.GetAngle());\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tpublic function get rotation():Number\r\n\t\t{\r\n\t\t\tif (_body)\r\n\t\t\t\treturn _body.GetAngle() * 180 \/ Math.PI;\r\n\t\t\telse\r\n\t\t\t\treturn _rotation * 180 \/ Math.PI;\r\n\t\t}\r\n\r\n\t\tpublic function set rotation(value:Number):void\r\n\t\t{\r\n\t\t\t_rotation = value * Math.PI \/ 180;\r\n\r\n\t\t\tif (_body)\r\n\t\t\t\t_body.SetTransform(_body.GetPosition(), _rotation); \r\n\t\t}\r\n\r\n\t\t\/**\r\n\t\t * This can only be set in the constructor parameters. \r\n\t\t *\/\t\t\r\n\t\tpublic function get width():Number\r\n\t\t{\r\n\t\t\treturn _width * _box2D.scale;\r\n\t\t}\r\n\r\n\t\tpublic function set width(value:Number):void\r\n\t\t{\r\n\t\t\t_width = value \/ _box2D.scale;\r\n\r\n\t\t\tif (_initialized)\r\n\t\t\t{\r\n\t\t\t\ttrace(\"Warning: You cannot set \" + this + \" width after it has been created. Please set it in the constructor.\");\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t\/**\r\n\t\t * This can only be set in the constructor parameters. \r\n\t\t *\/\t\r\n\t\tpublic function get height():Number\r\n\t\t{\r\n\t\t\treturn _height * _box2D.scale;\r\n\t\t}\r\n\r\n\t\tpublic function set height(value:Number):void\r\n\t\t{\r\n\t\t\t_height = value \/ _box2D.scale;\r\n\r\n\t\t\tif (_initialized)\r\n\t\t\t{\r\n\t\t\t\ttrace(\"Warning: You cannot set \" + this + \" height after it has been created. Please set it in the constructor.\");\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t\/**\r\n\t\t * This can only be set in the constructor parameters. \r\n\t\t *\/\t\r\n\t\tpublic function get radius():Number\r\n\t\t{\r\n\t\t\treturn _radius * _box2D.scale;\r\n\t\t}\r\n\r\n\t\t\/**\r\n\t\t * The object has a radius or a width &amp; height. It can't have both.\r\n\t\t *\/\r\n\t\tpublic function set radius(value:Number):void\r\n\t\t{\r\n\t\t\t_radius = value \/ _box2D.scale;\r\n\r\n\t\t\tif (_initialized)\r\n\t\t\t{\r\n\t\t\t\ttrace(\"Warning: You cannot set \" + this + \" radius after it has been created. Please set it in the constructor.\");\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t\/**\r\n\t\t * A direction reference to the Box2D body associated with this object.\r\n\t\t *\/\r\n\t\tpublic function get body():b2Body\r\n\t\t{\r\n\t\t\treturn _body;\r\n\t\t}\r\n\r\n\t\t\/**\r\n\t\t * This method will often need to be overriden to provide additional definition to the Box2D body object. \r\n\t\t *\/\t\t\r\n\t\tprotected function defineBody():void\r\n\t\t{\r\n\t\t\t_bodyDef = new b2BodyDef();\r\n\t\t\t_bodyDef.type = b2Body.b2_dynamicBody;\r\n\t\t\t_bodyDef.position.v2 = new V2(_x, _y);\r\n\t\t\t_bodyDef.angle = _rotation;\r\n\t\t}\r\n\r\n\t\t\/**\r\n\t\t * This method will often need to be overriden to customize the Box2D body object. \r\n\t\t *\/\t\r\n\t\tprotected function createBody():void\r\n\t\t{\r\n\t\t\t_body = _box2D.world.CreateBody(_bodyDef);\r\n\t\t\t_body.SetUserData(this);\r\n\t\t}\r\n\r\n\t\t\/**\r\n\t\t * This method will often need to be overriden to customize the Box2D shape object.\r\n\t\t * The PhysicsObject creates a rectangle by default if the radius it not defined, but you can replace this method's\r\n\t\t * definition and instead create a custom shape, such as a line or circle.\r\n\t\t *\/\t\r\n\t\tprotected function createShape():void\r\n\t\t{\r\n\t\t\tif (_radius != 0) {\r\n\t\t\t\t_shape = new b2CircleShape();\r\n\t\t\t\tb2CircleShape(_shape).m_radius = _radius;\r\n\t\t\t} else {\r\n\t\t\t\t_shape = new b2PolygonShape();\r\n\t\t\t\tb2PolygonShape(_shape).SetAsBox(_width \/ 2, _height \/ 2);\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t\/**\r\n\t\t * This method will often need to be overriden to provide additional definition to the Box2D fixture object. \r\n\t\t *\/\t\r\n\t\tprotected function defineFixture():void\r\n\t\t{\r\n\t\t\t_fixtureDef = new b2FixtureDef();\r\n\t\t\t_fixtureDef.shape = _shape;\r\n\t\t\t_fixtureDef.density = 1;\r\n\t\t\t_fixtureDef.friction = 0.6;\r\n\t\t\t_fixtureDef.restitution = 0.3;\r\n\t\t\t_fixtureDef.filter.categoryBits = Box2DCollisionCategories.Get(\"Level\");\r\n\t\t\t_fixtureDef.filter.maskBits = Box2DCollisionCategories.GetAll();\r\n\t\t}\r\n\r\n\t\t\/**\r\n\t\t * This method will often need to be overriden to customize the Box2D fixture object. \r\n\t\t *\/\t\r\n\t\tprotected function createFixture():void\r\n\t\t{\r\n\t\t\t_fixture = _body.CreateFixture(_fixtureDef);\r\n\t\t}\r\n\r\n\t\t\/**\r\n\t\t * This method will often need to be overriden to provide additional definition to the Box2D joint object.\r\n\t\t * A joint is not automatically created, because joints require two bodies. Therefore, if you need to create a joint,\r\n\t\t * you will also need to create additional bodies, fixtures and shapes, and then also instantiate a new b2JointDef\r\n\t\t * and b2Joint object.\r\n\t\t *\/\t\r\n\t\tprotected function defineJoint():void\r\n\t\t{\r\n\r\n\t\t}\r\n\r\n\t\t\/**\r\n\t\t * This method will often need to be overriden to customize the Box2D joint object. \r\n\t\t * A joint is not automatically created, because joints require two bodies. Therefore, if you need to create a joint,\r\n\t\t * you will also need to create additional bodies, fixtures and shapes, and then also instantiate a new b2JointDef\r\n\t\t * and b2Joint object.\r\n\t\t *\/\t\t\r\n\t\tprotected function createJoint():void\r\n\t\t{\r\n\r\n\t\t}\r\n\t}\r\n}<\/pre>\n<pre lang=\"actionscript3\">package com.citrusengine.system.components.box2d {\r\n\r\n\timport Box2DAS.Dynamics.ContactEvent;\r\n\r\n\timport com.citrusengine.system.Component;\r\n\r\n\t\/**\r\n\t * The Box2D collision component, extends it to handle collision.\r\n\t *\/\r\n\tpublic class CollisionComponent extends Component {\r\n\r\n\t\tpublic function CollisionComponent(name:String, params:Object = null) {\r\n\r\n\t\t\tsuper(name, params);\r\n\t\t}\r\n\r\n\t\tpublic function handlePreSolve(e:ContactEvent):void {\r\n\r\n\t\t}\r\n\r\n\t\tpublic function handleBeginContact(e:ContactEvent):void {\r\n\r\n\t\t}\r\n\r\n\t\tpublic function handleEndContact(e:ContactEvent):void {\r\n\r\n\t\t}\r\n\t}\r\n}<\/pre>\n<pre lang=\"actionscript3\">package com.citrusengine.system.components.box2d {\r\n\r\n\timport Box2DAS.Common.V2;\r\n\r\n\timport com.citrusengine.system.Component;\r\n\r\n\t\/**\r\n\t * The Box2D movement component, we've to know the Box2D physics component to be able to move it.\r\n\t *\/\r\n\tpublic class MovementComponent extends Component {\r\n\r\n\t\tprotected var _physicsComponent:Box2DComponent;\r\n\r\n\t\tprotected var _velocity:V2;\r\n\r\n\t\tpublic function MovementComponent(name:String, params:Object = null) {\r\n\t\t\tsuper(name, params);\r\n\t\t}\r\n\r\n\t\toverride public function initialize():void {\r\n\r\n\t\t\tsuper.initialize();\r\n\r\n\t\t\t_physicsComponent = entity.components[\"physics\"];\r\n\t\t}\r\n\r\n\t\toverride public function update(timeDelta:Number):void {\r\n\r\n\t\t\tsuper.update(timeDelta);\r\n\r\n\t\t\tif (_physicsComponent) {\r\n\r\n\t\t\t\t_velocity = _physicsComponent.body.GetLinearVelocity();\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t}\r\n}<\/pre>\n<p>You can already see that there are directly some interactions between them. If you would put those somewhere else, you will have a class managing all the logic. Not very different from our first single class Hero base.<\/p>\n<p>Now the Hero specific components. H\u00e9 wait!!! OOP!? Yep. Most of the physics properties are already defined in our Box2DComponent class, but our Hero has specific physics! It must handle collision as well, animations, movement&#8230; So its physics, movement, view, collision components class :<\/p>\n<pre lang=\"actionscript3\">package com.citrusengine.system.components.box2d.hero {\r\n\r\n\timport Box2DAS.Dynamics.ContactEvent;\r\n\r\n\timport com.citrusengine.physics.Box2DCollisionCategories;\r\n\timport com.citrusengine.system.components.box2d.Box2DComponent;\r\n\timport com.citrusengine.system.components.box2d.CollisionComponent;\r\n\timport com.citrusengine.utils.Box2DShapeMaker;\r\n\r\n\t\/**\r\n\t * The Box2D Hero physics component add the fixture listener, change its friction, restitution...\r\n\t *\/\r\n\tpublic class HeroPhysicsComponent extends Box2DComponent {\r\n\r\n\t\tprotected var _collisionComponent:CollisionComponent;\r\n\r\n\t\tprotected var _friction:Number = 0.75;\r\n\r\n\t\tpublic function HeroPhysicsComponent(name:String, params:Object = null) {\r\n\t\t\tsuper(name, params);\r\n\t\t}\r\n\r\n\t\toverride public function destroy():void {\r\n\r\n\t\t\t_fixture.removeEventListener(ContactEvent.PRE_SOLVE, _collisionComponent.handlePreSolve);\r\n\t\t\t_fixture.removeEventListener(ContactEvent.BEGIN_CONTACT, _collisionComponent.handleBeginContact);\r\n\t\t\t_fixture.removeEventListener(ContactEvent.END_CONTACT, _collisionComponent.handleEndContact);\r\n\r\n\t\t\tsuper.destroy();\r\n\t\t}\r\n\r\n\t\toverride public function initialize():void {\r\n\r\n\t\t\t_collisionComponent = entity.components[\"collision\"];\r\n\r\n\t\t\t_fixture.addEventListener(ContactEvent.PRE_SOLVE, _collisionComponent.handlePreSolve);\r\n\t\t\t_fixture.addEventListener(ContactEvent.BEGIN_CONTACT, _collisionComponent.handleBeginContact);\r\n\t\t\t_fixture.addEventListener(ContactEvent.END_CONTACT, _collisionComponent.handleEndContact);\r\n\t\t}\r\n\r\n\t\toverride protected function defineBody():void {\r\n\r\n\t\t\tsuper.defineBody();\r\n\r\n\t\t\t_bodyDef.fixedRotation = true;\r\n\t\t\t_bodyDef.allowSleep = false;\r\n\t\t}\r\n\r\n\t\toverride protected function createShape():void {\r\n\r\n\t\t\t_shape = Box2DShapeMaker.BeveledRect(_width, _height, 0.1);\r\n\t\t}\r\n\r\n\t\toverride protected function defineFixture():void {\r\n\r\n\t\t\tsuper.defineFixture();\r\n\r\n\t\t\t_fixtureDef.friction = _friction;\r\n\t\t\t_fixtureDef.restitution = 0;\r\n\t\t\t_fixtureDef.filter.categoryBits = Box2DCollisionCategories.Get(\"GoodGuys\");\r\n\t\t\t_fixtureDef.filter.maskBits = Box2DCollisionCategories.GetAll();\r\n\t\t}\r\n\r\n\t\toverride protected function createFixture():void {\r\n\r\n\t\t\tsuper.createFixture();\r\n\r\n\t\t\t_fixture.m_reportPreSolve = true;\r\n\t\t\t_fixture.m_reportBeginContact = true;\r\n\t\t\t_fixture.m_reportEndContact = true;\r\n\t\t}\r\n\r\n\t\tpublic function changeFixtureToZero():void {\r\n\t\t\t_fixture.SetFriction(0);\r\n\t\t}\r\n\r\n\t\tpublic function changeFixtureToItsInitialValue():void {\r\n\t\t\t_fixture.SetFriction(_friction);\r\n\t\t}\r\n\t}\r\n}<\/pre>\n<pre lang=\"actionscript3\">package com.citrusengine.system.components.box2d.hero {\r\n\r\n\timport Box2DAS.Common.V2;\r\n\r\n\timport com.citrusengine.objects.Box2DPhysicsObject;\r\n\timport com.citrusengine.system.components.InputComponent;\r\n\timport com.citrusengine.system.components.box2d.MovementComponent;\r\n\r\n\timport org.osflash.signals.Signal;\r\n\r\n\timport flash.utils.clearTimeout;\r\n\timport flash.utils.setTimeout;\r\n\r\n\t\/**\r\n\t * The Box2D Hero movement component. Most of its properties are here. It uses a lot of informations from the input component &amp; \r\n\t * some from the Box2D Hero collision component.\r\n\t *\/\r\n\tpublic class HeroMovementComponent extends MovementComponent {\r\n\r\n\t\t\/\/properties\r\n\t\t\/**\r\n\t\t * This is the rate at which the hero speeds up when you move him left and right. \r\n\t\t *\/\r\n\t\tpublic var acceleration:Number = 1;\r\n\r\n\t\t\/**\r\n\t\t * This is the fastest speed that the hero can move left or right. \r\n\t\t *\/\r\n\t\tpublic var maxVelocity:Number = 8;\r\n\r\n\t\t\/**\r\n\t\t * This is the initial velocity that the hero will move at when he jumps.\r\n\t\t *\/\r\n\t\tpublic var jumpHeight:Number = 11;\r\n\r\n\t\t\/**\r\n\t\t * This is the amount of \"float\" that the hero has when the player holds the jump button while jumping. \r\n\t\t *\/\r\n\t\tpublic var jumpAcceleration:Number = 0.3;\r\n\r\n\t\t\/**\r\n\t\t * This is the y velocity that the hero must be travelling in order to kill a Baddy.\r\n\t\t *\/\r\n\t\tpublic var killVelocity:Number = 3;\r\n\r\n\t\t\/**\r\n\t\t * The y velocity that the hero will spring when he kills an enemy. \r\n\t\t *\/\r\n\t\tpublic var enemySpringHeight:Number = 8;\r\n\r\n\t\t\/**\r\n\t\t * The y velocity that the hero will spring when he kills an enemy while pressing the jump button. \r\n\t\t *\/\r\n\t\tpublic var enemySpringJumpHeight:Number = 9;\r\n\r\n\t\t\/**\r\n\t\t * How long the hero is in hurt mode for. \r\n\t\t *\/\r\n\t\tpublic var hurtDuration:Number = 1000;\r\n\r\n\t\t\/**\r\n\t\t * The amount of kick-back that the hero jumps when he gets hurt. \r\n\t\t *\/\r\n\t\tpublic var hurtVelocityX:Number = 6;\r\n\r\n\t\t\/**\r\n\t\t * The amount of kick-back that the hero jumps when he gets hurt. \r\n\t\t *\/\r\n\t\tpublic var hurtVelocityY:Number = 10;\r\n\r\n\t\t\/**\r\n\t\t * Determines whether or not the hero's ducking ability is enabled.\r\n\t\t *\/\r\n\t\tpublic var canDuck:Boolean = true;\r\n\r\n\t\t\/\/events\r\n\t\t\/**\r\n\t\t * Dispatched whenever the hero jumps. \r\n\t\t *\/\r\n\t\tpublic var onJump:Signal;\r\n\r\n\t\t\/**\r\n\t\t * Dispatched whenever the hero gives damage to an enemy. \r\n\t\t *\/\t\t\r\n\t\tpublic var onGiveDamage:Signal;\r\n\r\n\t\t\/**\r\n\t\t * Dispatched whenever the hero takes damage from an enemy. \r\n\t\t *\/\t\t\r\n\t\tpublic var onTakeDamage:Signal;\r\n\r\n\t\tprotected var _inputComponent:InputComponent;\r\n\t\tprotected var _collisionComponent:HeroCollisionComponent;\r\n\r\n\t\tprotected var _onGround:Boolean = false;\r\n\t\tprotected var _springOffEnemy:Number = -1;\r\n\t\tprotected var _hurtTimeoutID:Number;\r\n\t\tprotected var _isHurt:Boolean = false;\r\n\t\tprotected var _controlsEnabled:Boolean = true;\r\n\t\tprotected var _playerMovingHero:Boolean = false;\r\n\t\tprotected var _ducking:Boolean = false;\r\n\r\n\t\tpublic function HeroMovementComponent(name:String, params:Object = null) {\r\n\r\n\t\t\tsuper(name, params);\r\n\r\n\t\t\tonJump = new Signal();\r\n\t\t\tonGiveDamage = new Signal();\r\n\t\t\tonTakeDamage = new Signal();\r\n\t\t}\r\n\r\n\t\toverride public function initialize():void {\r\n\r\n\t\t\tsuper.initialize();\r\n\r\n\t\t\t_inputComponent = entity.components[\"input\"];\r\n\t\t\t_collisionComponent = entity.components[\"collision\"];\r\n\t\t}\r\n\r\n\t\toverride public function destroy():void {\r\n\r\n\t\t\tonJump.removeAll();\r\n\t\t\tonGiveDamage.removeAll();\r\n\t\t\tonTakeDamage.removeAll();\r\n\r\n\t\t\tclearTimeout(_hurtTimeoutID);\r\n\r\n\t\t\tsuper.destroy();\r\n\t\t}\r\n\r\n\t\toverride public function update(timeDelta:Number):void {\r\n\r\n\t\t\tsuper.update(timeDelta);\r\n\r\n\t\t\tif (controlsEnabled &amp;&amp; _physicsComponent) {\r\n\r\n\t\t\t\tvar moveKeyPressed:Boolean = false;\r\n\r\n\t\t\t\t_ducking = (_inputComponent.downKeyIsDown &amp;&amp; _onGround &amp;&amp; canDuck);\r\n\r\n\t\t\t\tif (_inputComponent.rightKeyIsDown &amp;&amp; !_ducking) {\r\n\t\t\t\t\t_velocity = V2.add(_velocity, getSlopeBasedMoveAngle());\r\n\t\t\t\t\tmoveKeyPressed = true;\r\n\t\t\t\t}\r\n\r\n\t\t\t\tif (_inputComponent.leftKeyIsDown &amp;&amp; !_ducking) {\t\t\t\t\t\r\n\t\t\t\t\t_velocity = V2.subtract(_velocity, getSlopeBasedMoveAngle());\r\n\t\t\t\t\tmoveKeyPressed = true;\r\n\t\t\t\t}\r\n\r\n\t\t\t\t\/\/If player just started moving the hero this tick.\r\n\t\t\t\tif (moveKeyPressed &amp;&amp; !_playerMovingHero) {\r\n\t\t\t\t\t_playerMovingHero = true;\r\n\t\t\t\t\t(_physicsComponent as HeroPhysicsComponent).changeFixtureToZero(); \/\/Take away friction so he can accelerate.\r\n\t\t\t\t}\r\n\t\t\t\t\/\/Player just stopped moving the hero this tick.\r\n\t\t\t\telse if (!moveKeyPressed &amp;&amp; _playerMovingHero) {\r\n\t\t\t\t\t_playerMovingHero = false;\r\n\t\t\t\t\t(_physicsComponent as HeroPhysicsComponent).changeFixtureToItsInitialValue(); \/\/Add friction so that he stops running\r\n\t\t\t\t}\r\n\r\n\t\t\t\tif (_onGround &amp;&amp; _inputComponent.spaceKeyJustPressed &amp;&amp; !_ducking) {\r\n\t\t\t\t\t_velocity.y = -jumpHeight;\r\n\t\t\t\t\tonJump.dispatch();\r\n\t\t\t\t}\r\n\r\n\t\t\t\tif (_inputComponent.spaceKeyIsDown &amp;&amp; !_onGround &amp;&amp; _velocity.y &lt; 0) \t\t\t\t\t_velocity.y -= jumpAcceleration; \t\t\t\t\t \t\t\t\tif (_springOffEnemy != -1) { \t\t\t\t\tif (_inputComponent.spaceKeyIsDown) \t\t\t\t\t\t_velocity.y = -enemySpringJumpHeight; \t\t\t\t\telse \t\t\t\t\t\t_velocity.y = -enemySpringHeight; \t\t\t\t\t_springOffEnemy = -1; \t\t\t\t} \t\t\t\t \t\t\t\t\/\/Cap velocities \t\t\t\tif (_velocity.x &gt; maxVelocity)\r\n\t\t\t\t\t_velocity.x = maxVelocity;\r\n\t\t\t\telse if (_velocity.x &lt; -maxVelocity) \t\t\t\t\t_velocity.x = -maxVelocity; \t\t\t\t \t\t\t\t_physicsComponent.body.SetLinearVelocity(_velocity); \t\t\t} \t\t} \t\t \t\t\/** \t\t * The hero gives damage \t\t *\/ \t\t  \t\tpublic function giveDamage(collider:Box2DPhysicsObject):void { \t\t\t \t\t\t_springOffEnemy = collider.y - _physicsComponent.height; \t\t\tonGiveDamage.dispatch(); \t\t} \t\t \t\t\/** \t\t * Hurts and fling the hero, disables his controls for a little bit, and dispatches the onTakeDamage signal.  \t\t *\/\t\t \t\tpublic function hurt(collider:Box2DPhysicsObject):void { \t\t\t \t\t\t_isHurt = true; \t\t\tcontrolsEnabled = false; \t\t\t_hurtTimeoutID = setTimeout(endHurtState, hurtDuration); \t\t\tonTakeDamage.dispatch(); \t\t\t \t\t\tvar hurtVelocity:V2 = _physicsComponent.body.GetLinearVelocity(); \t\t\thurtVelocity.y = -hurtVelocityY; \t\t\thurtVelocity.x = hurtVelocityX; \t\t\tif (collider.x &gt; _physicsComponent.x)\r\n\t\t\t\thurtVelocity.x = -hurtVelocityX;\r\n\t\t\t_physicsComponent.body.SetLinearVelocity(hurtVelocity);\r\n\r\n\t\t\t\/\/Makes sure that the hero is not frictionless while his control is disabled\r\n\t\t\tif (_playerMovingHero) {\r\n\t\t\t\t_playerMovingHero = false;\r\n\t\t\t\t(_physicsComponent as HeroPhysicsComponent).changeFixtureToItsInitialValue();\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tprotected function endHurtState():void {\r\n\r\n\t\t\t_isHurt = false;\r\n\t\t\tcontrolsEnabled = true;\r\n\t\t}\r\n\r\n\t\tprotected function getSlopeBasedMoveAngle():V2 {\r\n\t\t\treturn new V2(acceleration, 0).rotate(_collisionComponent.combinedGroundAngle);\r\n\t\t}\r\n\r\n\t\t\/**\r\n\t\t * Whether or not the player can move and jump with the hero. \r\n\t\t *\/\t\r\n\t\tpublic function get controlsEnabled():Boolean\r\n\t\t{\r\n\t\t\treturn _controlsEnabled;\r\n\t\t}\r\n\r\n\t\tpublic function set controlsEnabled(value:Boolean):void\r\n\t\t{\r\n\t\t\t_controlsEnabled = value;\r\n\r\n\t\t\tif (!_controlsEnabled)\r\n\t\t\t\t(_physicsComponent as HeroPhysicsComponent).changeFixtureToItsInitialValue();\r\n\t\t}\r\n\r\n\t\t\/**\r\n\t\t * Returns true if the hero is on the ground and can jump. \r\n\t\t *\/\t\t\r\n\t\tpublic function get onGround():Boolean {\r\n\t\t\treturn _onGround;\r\n\t\t}\r\n\r\n\t\tpublic function set onGround(value:Boolean):void {\r\n\t\t\t_onGround = value;\r\n\t\t}\r\n\r\n\t\tpublic function get ducking():Boolean {\r\n\t\t\treturn _ducking;\r\n\t\t}\r\n\r\n\t\tpublic function get isHurt():Boolean {\r\n\t\t\treturn _isHurt;\r\n\t\t}\r\n\t}\r\n}<\/pre>\n<pre lang=\"actionscript3\">package com.citrusengine.system.components.box2d.hero {\r\n\r\n\timport Box2DAS.Dynamics.b2Fixture;\r\n\r\n\timport com.citrusengine.system.components.ViewComponent;\r\n\r\n\t\/**\r\n\t * The Box2D Hero view component. It manages the hero's view based on its physics component and depending its movement.\r\n\t *\/\r\n\tpublic class HeroViewComponent extends ViewComponent {\r\n\r\n\t\tpublic var groundContacts:Array = [];\/\/Used to determine if he's on ground or not.\r\n\r\n\t\tprotected var _physicsComponent:HeroPhysicsComponent;\r\n\t\tprotected var _movementComponent:HeroMovementComponent;\r\n\r\n\t\tpublic function HeroViewComponent(name:String, params:Object = null) {\r\n\r\n\t\t\tsuper(name, params);\r\n\t\t}\r\n\r\n\t\toverride public function initialize():void {\r\n\r\n\t\t\tsuper.initialize();\r\n\r\n\t\t\t_physicsComponent = entity.components[\"physics\"];\r\n\t\t\t_movementComponent = entity.components[\"move\"];\r\n\t\t}\r\n\r\n\t\toverride public function update(timeDelta:Number):void {\r\n\r\n\t\t\tsuper.update(timeDelta);\r\n\r\n\t\t\tvar prevAnimation:String = _animation;\r\n\r\n\t\t\tif (_physicsComponent &amp;&amp; _movementComponent) {\r\n\r\n\t\t\t\tif (_movementComponent.isHurt)\r\n\t\t\t\t\t_animation = \"hurt\";\r\n\t\t\t\telse if (!_movementComponent.onGround)\r\n\t\t\t\t\t_animation = \"jump\";\r\n\t\t\t\telse if (_movementComponent.ducking)\r\n\t\t\t\t\t_animation = \"duck\";\r\n\t\t\t\telse {\r\n\r\n\t\t\t\t\tvar walkingSpeed:Number = getWalkingSpeed();\r\n\r\n\t\t\t\t\tif (walkingSpeed &lt; -_movementComponent.acceleration) { \t\t\t\t\t\t_inverted = true; \t\t\t\t\t\t_animation = \"walk\"; \t\t\t\t\t} else if (walkingSpeed &gt; _movementComponent.acceleration) {\r\n\t\t\t\t\t\t_inverted = false;\r\n\t\t\t\t\t\t_animation = \"walk\";\r\n\t\t\t\t\t} else {\r\n\t\t\t\t\t\t_animation = \"idle\";\r\n\t\t\t\t\t}\r\n\t\t\t\t}\t\t\t\t\r\n\t\t\t}\r\n\r\n\t\t\tif (prevAnimation != _animation)\r\n\t\t\t\tonAnimationChange.dispatch();\r\n\t\t}\r\n\r\n\t\t\/**\r\n\t\t * Returns the absolute walking speed, taking moving platforms into account.\r\n\t\t * Isn't super performance-light, so use sparingly.\r\n\t\t *\/\r\n\t\tpublic function getWalkingSpeed():Number {\r\n\r\n\t\t\tvar groundVelocityX:Number = 0;\r\n\t\t\tfor each (var groundContact:b2Fixture in groundContacts) {\r\n\t\t\t\tgroundVelocityX += groundContact.GetBody().GetLinearVelocity().x;\r\n\t\t\t}\r\n\r\n\t\t\treturn _physicsComponent.body.GetLinearVelocity().x - groundVelocityX;\r\n\t\t}\r\n\t}\r\n}<\/pre>\n<pre lang=\"actionscript3\">package com.citrusengine.system.components.box2d.hero {\r\n\r\n\timport Box2DAS.Dynamics.ContactEvent;\r\n\timport Box2DAS.Dynamics.b2Fixture;\r\n\r\n\timport com.citrusengine.math.MathVector;\r\n\timport com.citrusengine.objects.Box2DPhysicsObject;\r\n\timport com.citrusengine.objects.platformer.box2d.Baddy;\r\n\timport com.citrusengine.system.components.box2d.CollisionComponent;\r\n\r\n\t\/**\r\n\t * The Box2D Hero collision component. We need to access informations of the hero view &amp; movement &amp; physics component.\r\n\t *\/\r\n\tpublic class HeroCollisionComponent extends CollisionComponent {\r\n\r\n\t\tprotected var _viewComponent:HeroViewComponent;\r\n\t\tprotected var _movementComponent:HeroMovementComponent;\r\n\t\tprotected var _physicsComponent:HeroPhysicsComponent;\r\n\r\n\t\tprotected var _enemyClass:Class = Baddy;\r\n\t\tprotected var _combinedGroundAngle:Number = 0;\r\n\r\n\t\tpublic function HeroCollisionComponent(name:String, params:Object = null) {\r\n\r\n\t\t\tsuper(name, params);\r\n\t\t}\r\n\r\n\t\toverride public function initialize():void {\r\n\r\n\t\t\tsuper.initialize();\r\n\r\n\t\t\t_viewComponent = entity.components[\"view\"];\r\n\t\t\t_movementComponent = entity.components[\"move\"];\r\n\t\t\t_physicsComponent = entity.components[\"physics\"];\r\n\t\t}\r\n\r\n\t\toverride public function destroy():void {\r\n\r\n\t\t\tsuper.destroy();\r\n\t\t}\r\n\r\n\t\toverride public function handlePreSolve(e:ContactEvent):void {\r\n\r\n\t\t\tsuper.handlePreSolve(e);\r\n\r\n\t\t\tif (!_movementComponent.ducking)\r\n\t\t\t\treturn;\r\n\r\n\t\t\tvar other:Box2DPhysicsObject = e.other.GetBody().GetUserData() as Box2DPhysicsObject;\r\n\r\n\t\t\tvar heroTop:Number = _physicsComponent.y;\r\n\t\t\tvar objectBottom:Number = other.y + (other.height \/ 2);\r\n\r\n\t\t\tif (objectBottom &lt; heroTop)\r\n\t\t\t\te.contact.Disable();\r\n\t\t}\r\n\r\n\t\toverride public function handleBeginContact(e:ContactEvent):void {\r\n\r\n\t\t\tsuper.handleBeginContact(e);\r\n\r\n\t\t\tvar collider:Box2DPhysicsObject = e.other.GetBody().GetUserData();\r\n\r\n\t\t\tif (_enemyClass &amp;&amp; collider is _enemyClass) {\r\n\r\n\t\t\t\tif (_physicsComponent.body.GetLinearVelocity().y &lt; _movementComponent.killVelocity &amp;&amp; !_movementComponent.isHurt) \t\t\t\t\t_movementComponent.hurt(collider); \t\t\t\telse \t\t\t\t\t_movementComponent.giveDamage(collider); \t\t\t} \t\t\t \t\t\t\/\/Collision angle \t\t\tif (e.normal) \/\/The normal property doesn't come through all the time. I think doesn't come through against sensors. \t\t\t{ \t\t\t\tvar collisionAngle:Number = new MathVector(e.normal.x, e.normal.y).angle * 180 \/ Math.PI; \t\t\t\tif (collisionAngle &gt; 45 &amp;&amp; collisionAngle &lt; 135)\r\n\t\t\t\t{\r\n\t\t\t\t\t_viewComponent.groundContacts.push(e.other);\r\n\t\t\t\t\t_movementComponent.onGround = true;\r\n\t\t\t\t\tupdateCombinedGroundAngle();\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\toverride public function handleEndContact(e:ContactEvent):void {\r\n\r\n\t\t\tsuper.handleEndContact(e);\r\n\r\n\t\t\t\/\/Remove from ground contacts, if it is one.\r\n\t\t\tvar index:int = _viewComponent.groundContacts.indexOf(e.other);\r\n\t\t\tif (index != -1)\r\n\t\t\t{\r\n\t\t\t\t_viewComponent.groundContacts.splice(index, 1);\r\n\t\t\t\tif (_viewComponent.groundContacts.length == 0)\r\n\t\t\t\t\t_movementComponent.onGround = false;\r\n\t\t\t\tupdateCombinedGroundAngle();\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tprotected function updateCombinedGroundAngle():void {\r\n\r\n\t\t\t_combinedGroundAngle = 0;\r\n\r\n\t\t\tif (_viewComponent.groundContacts.length == 0)\r\n\t\t\t\treturn;\r\n\r\n\t\t\tfor each (var contact:b2Fixture in _viewComponent.groundContacts)\r\n\t\t\t\tvar angle:Number = contact.GetBody().GetAngle();\r\n\r\n\t\t\tvar turn:Number = 45 * Math.PI \/ 180;\r\n\t\t\tangle = angle % turn;\r\n\t\t\t_combinedGroundAngle += angle;\r\n\t\t\t_combinedGroundAngle \/= _viewComponent.groundContacts.length;\r\n\t\t}\r\n\r\n\t\tpublic function get combinedGroundAngle():Number {\r\n\t\t\treturn _combinedGroundAngle;\r\n\t\t}\r\n\t}\r\n}<\/pre>\n<p>There are interactions between components, here the rubber hits the road. We don&#8217;t have separate components as it would be and data&#8217;s component may be changed from an other component, but you can easily add a new functionality (climb a ladder for example) and handle it. Easier to manage but, that&#8217;s just OOP, isn&#8217;t it?<\/p>\n<p>Oh and wait! An entity\/component system is designed to be modular. It means that you can add functionalities on the fly, and not programming everything into 4 &#8211; 6 components with lots of conditions if the ability\/behavior is added. Yep, I know. I failed. I didn&#8217;t find an easy way to do that with all this Box2D stuff.<\/p>\n<p>To conclude, this experiment was very rewarding even if I didn&#8217;t success to achieve it. <em>Don&#8217;t hesitate to comment to put me on the right direction!!<\/em><\/p>\n<p>Concerning the Citrus Engine, we are close to the 2nd beta for the V3 : lots of bug fixes, performance enhancement, Nape support, Flash Pro support as level editor is even better and this attempt of entity\/component system which might be useful&#8230; or not.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>One year ago, I heard about the Entity\/Component Model for the first time. It seemed to be awesome for game design \/ development. This is 3 links\/resources for a good introduction : Gamasutra the Entity Component Model, Entity Systems are the future of MMOG development, Entity Systems. Then Richard Lord has made a very good &hellip; <a href=\"http:\/\/www.aymericlamboley.fr\/blog\/an-entity-component-systems-attempt-using-box2d\/\" class=\"more-link\">Continue reading <span class=\"screen-reader-text\">An entity\/component system&#8217;s attempt using Box2D<\/span> <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0},"categories":[4,115,51,147,33,11,114,6],"tags":[27,52,153,74],"_links":{"self":[{"href":"http:\/\/www.aymericlamboley.fr\/blog\/wp-json\/wp\/v2\/posts\/683"}],"collection":[{"href":"http:\/\/www.aymericlamboley.fr\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/www.aymericlamboley.fr\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/www.aymericlamboley.fr\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/www.aymericlamboley.fr\/blog\/wp-json\/wp\/v2\/comments?post=683"}],"version-history":[{"count":12,"href":"http:\/\/www.aymericlamboley.fr\/blog\/wp-json\/wp\/v2\/posts\/683\/revisions"}],"predecessor-version":[{"id":1276,"href":"http:\/\/www.aymericlamboley.fr\/blog\/wp-json\/wp\/v2\/posts\/683\/revisions\/1276"}],"wp:attachment":[{"href":"http:\/\/www.aymericlamboley.fr\/blog\/wp-json\/wp\/v2\/media?parent=683"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/www.aymericlamboley.fr\/blog\/wp-json\/wp\/v2\/categories?post=683"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/www.aymericlamboley.fr\/blog\/wp-json\/wp\/v2\/tags?post=683"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}