{"id":330,"date":"2011-07-20T18:29:15","date_gmt":"2011-07-20T17:29:15","guid":{"rendered":"http:\/\/www.aymericlamboley.fr\/blog\/?p=330"},"modified":"2014-11-01T15:06:16","modified_gmt":"2014-11-01T14:06:16","slug":"create-a-ladder-for-the-citrus-engine","status":"publish","type":"post","link":"http:\/\/www.aymericlamboley.fr\/blog\/create-a-ladder-for-the-citrus-engine\/","title":{"rendered":"Create a ladder for the Citrus Engine"},"content":{"rendered":"<p>Hi, today a quick post about how to create a ladder for the Citrus Engine.<br \/>\nIt is a preview version, <a href=\"http:\/\/www.reactiongears.com\/\" target=\"_blank\">Michael Kerr<\/a> requested me for his flash game scripting class !<\/p>\n<p>I&#8217;m working with Eric to add it into the original package !!<\/p>\n<p>So here is the result : <a href=\"http:\/\/www.aymericlamboley.fr\/blog\/wp-content\/uploads\/2011\/07\/Ladder\/\" target=\"_blank\"><strong>the ladder in action<\/strong><\/a><\/p>\n<p><!--more--><\/p>\n<p>A ladder is a simple sensor object.<br \/>\nHere are the specifications :<br \/>\n&#8211; a ladder is a Sensor, on contact it will give the capacity for the hero to climb\/down. It removes it after contact.<br \/>\n&#8211; it will remove hero&#8217;s gravity when it will be on the ladder and restore it after contact.<br \/>\n&#8211; the hero can&#8217;t duck on a ladder, but it can always jump.<br \/>\n&#8211; the ladder mode should not be &#8220;enabled&#8221; until the player presses &#8220;up&#8221; or &#8220;down&#8221; depending on where they are vertically. That way they can jump past it.<br \/>\n&#8211; as soon as the player &#8220;connects to&#8221; the ladder, they should be translated smoother to the center of the ladder so they are climbing up\/down the center of it.<br \/>\n&#8211; when coming from the top to go down, there will be a platform, and the ladder over. I disable contact with this platform if the hero uses the ladder.<br \/>\n&#8211; there are 3 ladder animations (ladderIdle, ladderClimbUp, and ladderClimbDown)<\/p>\n<p>So I update the Sensor like that :<\/p>\n<pre lang=\"actionscript3\" line=\"1\">package com.citrusengine.objects.platformer\r\n{\r\n\timport Box2DAS.Dynamics.ContactEvent;\r\n\timport Box2DAS.Dynamics.b2Body;\r\n\timport flash.display.MovieClip;\r\n\t\r\n\timport com.citrusengine.objects.PhysicsObject;\r\n\t\r\n\timport org.osflash.signals.Signal;\r\n\t\r\n\t\/**\r\n\t * Sensors simply listen for when an object begins and ends contact with them. They disaptch a signal\r\n\t * when contact is made or ended, and this signal can be used to perform custom game logic such as\r\n\t * triggering a scripted event, ending a level, popping up a dialog box, and virtually anything else.\r\n\t * \r\n\t * Remember that signals dispatch events when ANY Box2D object collides with them, so you will want\r\n\t * your collision handler to ignore collisions with objects that it is not interested in, or extend\r\n\t * the sensor and use maskBits to ignore collisions altogether.  \r\n\t * \r\n\t * Events\r\n\t * onBeginContact - Dispatches on first contact with the sensor.\r\n\t * onEndContact - Dispatches when the object leaves the sensor.\r\n\t *\/\t\r\n\tpublic class Sensor extends PhysicsObject\r\n\t{\r\n\t\t\/**\r\n\t\t * Dispatches on first contact with the sensor.\r\n\t\t *\/\r\n\t\tpublic var onBeginContact:Signal;\r\n\t\t\/**\r\n\t\t * Dispatches when the object leaves the sensor.\r\n\t\t *\/\r\n\t\tpublic var onEndContact:Signal;\r\n\t\t\r\n\t\tpublic var isLadder:Boolean = false;\r\n\t\t\r\n\t\tpublic static function Make(name:String, x:Number, y:Number, width:Number, height:Number, view:* = null):Sensor\r\n\t\t{\r\n\t\t\tif (view == null) view = MovieClip;\r\n\t\t\treturn new Sensor(name, { x: x, y: y, width: width, height: height, view: view } );\r\n\t\t}\r\n\t\t\r\n\t\tpublic function Sensor(name:String, params:Object=null)\r\n\t\t{\r\n\t\t\tsuper(name, params);\r\n\t\t\tonBeginContact = new Signal(ContactEvent);\r\n\t\t\tonEndContact = new Signal(ContactEvent);\r\n\t\t}\r\n\t\t\r\n\t\toverride public function destroy():void\r\n\t\t{\r\n\t\t\tonBeginContact.removeAll();\r\n\t\t\tonEndContact.removeAll();\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\tsuper.destroy();\r\n\t\t}\r\n\t\t\r\n\t\toverride protected function defineBody():void\r\n\t\t{\r\n\t\t\tsuper.defineBody();\r\n\t\t\t_bodyDef.type = b2Body.b2_staticBody;\r\n\t\t}\r\n\t\t\r\n\t\toverride protected function defineFixture():void\r\n\t\t{\r\n\t\t\tsuper.defineFixture();\r\n\t\t\t_fixtureDef.isSensor = true;\r\n\t\t}\r\n\t\t\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_reportBeginContact = true;\r\n\t\t\t_fixture.m_reportEndContact = true;\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\t\t\r\n\t\tprotected function handleBeginContact(e:ContactEvent):void\r\n\t\t{\r\n\t\t\tonBeginContact.dispatch(e);\r\n\t\t}\r\n\t\t\r\n\t\tprotected function handleEndContact(e:ContactEvent):void\r\n\t\t{\r\n\t\t\tonEndContact.dispatch(e);\r\n\t\t}\r\n\t}\r\n}<\/pre>\n<p>Adding a proprety to it : <em>public var isLadder:Boolean = false;<\/em><\/p>\n<p>I update the hero class too:<\/p>\n<pre lang=\"actionscript3\" line=\"1\">package com.citrusengine.objects.platformer {\r\n\r\n\timport Box2DAS.Common.V2;\r\n\timport Box2DAS.Dynamics.ContactEvent;\r\n\timport Box2DAS.Dynamics.b2Body;\r\n\timport Box2DAS.Dynamics.b2Fixture;\r\n\r\n\timport com.citrusengine.math.MathVector;\r\n\timport com.citrusengine.objects.PhysicsObject;\r\n\timport com.citrusengine.physics.CollisionCategories;\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\t\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 PhysicsObject\r\n\t{\r\n\t\t\/\/properties\r\n\t\t\r\n\t\t\/\/ properties for ladder\r\n\t\tpublic var originalGravity:Number = 1.6;\r\n\t\tpublic var canClimb:Boolean = false;\r\n\t\tpublic var climbVelocity:Number = 5;\r\n\t\tpublic var ladder:Sensor;\r\n\t\tprotected var _onLadder:Boolean = false;\r\n\t\tprotected var _climbing:Boolean = false;\r\n\t\tprotected var _climbAnimation:String = \"ladderIdle\";\r\n\t\t\/\/end properties for ladder\r\n\t\t\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 *\/\t\t\r\n\t\tpublic var acceleration:Number = 1;\r\n\t\t\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 *\/\t\t\r\n\t\tpublic var maxVelocity:Number = 8;\r\n\t\t\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 *\/\t\t\r\n\t\tpublic var jumpHeight:Number = 14;\r\n\t\t\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 *\/\t\t\r\n\t\tpublic var jumpAcceleration:Number = 0.9;\r\n\t\t\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 *\/\t\t\r\n\t\tpublic var killVelocity:Number = 3;\r\n\t\t\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 *\/\t\t\r\n\t\tpublic var enemySpringHeight:Number = 10;\r\n\t\t\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 *\/\t\t\r\n\t\tpublic var enemySpringJumpHeight:Number = 12;\r\n\t\t\r\n\t\t\/**\r\n\t\t * How long the hero is in hurt mode for. \r\n\t\t *\/\t\t\r\n\t\tpublic var hurtDuration:Number = 1000;\r\n\t\t\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 *\/\t\t\r\n\t\tpublic var hurtVelocityX:Number = 6;\r\n\t\t\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 *\/\t\t\r\n\t\tpublic var hurtVelocityY:Number = 10;\r\n\t\t\r\n\t\t\/\/events\r\n\t\t\/**\r\n\t\t * Dispatched whenever the hero jumps. \r\n\t\t *\/\t\t\r\n\t\tpublic var onJump:Signal;\r\n\t\t\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\t\t\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\t\t\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\t\t\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\t\t\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\t\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\t\t\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\t\t\t\r\n\t\t\toriginalGravity = gravity;\r\n\t\t\t\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\t\t\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\t\t\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\t\t\r\n\t\tpublic function set controlsEnabled(value:Boolean):void\r\n\t\t{\r\n\t\t\t_controlsEnabled = value;\r\n\t\t\t\r\n\t\t\tif (!_controlsEnabled)\r\n\t\t\t\t_fixture.SetFriction(_friction);\r\n\t\t}\r\n\t\t\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\t\t\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 *\/\t\t\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\t\t\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\t\t\r\n\t\tpublic function set friction(value:Number):void\r\n\t\t{\r\n\t\t\t_friction = value;\r\n\t\t\t\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\t\t\r\n\t\toverride public function update(timeDelta:Number):void\r\n\t\t{\r\n\t\t\tsuper.update(timeDelta);\r\n\t\t\t\r\n\t\t\tvar velocity:V2 = _body.GetLinearVelocity();\r\n\t\t\t\r\n\t\t\tif (controlsEnabled)\r\n\t\t\t{\r\n\t\t\t\tvar moveKeyPressed:Boolean = false;\r\n\t\t\t\t\r\n\t\t\t\t_ducking = (_ce.input.isDown(Keyboard.DOWN) && _onGround && canDuck);\r\n\t\t\t\t\r\n\t\t\t\tif (_ce.input.isDown(Keyboard.RIGHT) && !_ducking)\r\n\t\t\t\t{\r\n\t\t\t\t\tif (canClimb) {\r\n\t\t\t\t\t\tgravity = originalGravity;\r\n\t\t\t\t\t\t_climbing = false;\r\n\t\t\t\t\t}\r\n\t\t\t\t\tvelocity.x += (acceleration);\r\n\t\t\t\t\tmoveKeyPressed = true;\r\n\t\t\t\t}\r\n\t\t\t\t\r\n\t\t\t\tif (_ce.input.isDown(Keyboard.LEFT) && !_ducking)\r\n\t\t\t\t{\r\n\t\t\t\t\tif (canClimb) {\r\n\t\t\t\t\t\tgravity = originalGravity;\r\n\t\t\t\t\t\t_climbing = false;\r\n\t\t\t\t\t}\r\n\t\t\t\t\tvelocity.x -= (acceleration);\r\n\t\t\t\t\tmoveKeyPressed = true;\r\n\t\t\t\t}\r\n\t\t\t\t\r\n\t\t\t\t_onLadder = ((_ce.input.isDown(Keyboard.DOWN) || _ce.input.isDown(Keyboard.UP)) && canClimb);\r\n\t\t\t\t\r\n\t\t\t\tif (_onLadder) {\r\n\t\t\t\t\tif (x < ladder.x) \r\n\t\t\t\t\t\tx++;\r\n\t\t\t\t\tif (x > ladder.x)\r\n\t\t\t\t\t\tx--;\r\n\t\t\t\t}\r\n\r\n\t\t\t\tif (_ce.input.isDown(Keyboard.UP) && canClimb) {\r\n\t\t\t\t\t_onLadder = _climbing = true;\r\n\t\t\t\t\t_climbAnimation = \"ladderClimbUp\";\r\n\t\t\t\t\tvelocity = new V2(0, 0);\r\n\t\t\t\t\tvelocity.y -= climbVelocity;\r\n\t\t\t\t\tgravity = 0;\r\n\t\t\t\t\tmoveKeyPressed = true;\r\n\t\t\t\t}\r\n\t\t\t\t\r\n\t\t\t\tif (_ce.input.isDown(Keyboard.DOWN) && canClimb) {\r\n\t\t\t\t\t_onLadder = _climbing = true;\r\n\t\t\t\t\t_climbAnimation = \"ladderClimbDown\";\r\n\t\t\t\t\tvelocity = new V2(0, 0);\r\n\t\t\t\t\tvelocity.y += climbVelocity;\r\n\t\t\t\t\tgravity = 0;\r\n\t\t\t\t\tmoveKeyPressed = true;\r\n\t\t\t\t}\r\n\t\t\t\t\r\n\t\t\t\t\/\/ Remove velocity if just stop climbing\r\n\t\t\t\tif ((_ce.input.justReleased(Keyboard.UP) || _ce.input.justReleased(Keyboard.DOWN)) && canClimb) {\r\n\t\t\t\t\t_climbAnimation = \"ladderIdle\";\r\n\t\t\t\t\tvelocity.y = 0;\r\n\t\t\t\t}\r\n\t\t\t\t\r\n\t\t\t\tif (canClimb && _ce.input.justPressed(Keyboard.SPACE)) {\r\n\t\t\t\t\t_climbing = false;\r\n\t\t\t\t\tgravity = originalGravity;\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\t\t\t\t\r\n\t\t\t\t\/\/If player just started moving the hero this tick.\r\n\t\t\t\tif (moveKeyPressed && !_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 && _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\t\t\t\t\r\n\t\t\t\tif (_onGround && _ce.input.justPressed(Keyboard.SPACE) && !_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\t\t\t\t\r\n\t\t\t\tif (_ce.input.isDown(Keyboard.SPACE) && !_onGround && velocity.y < 0)\r\n\t\t\t\t{\r\n\t\t\t\t\tvelocity.y -= jumpAcceleration;\r\n\t\t\t\t}\r\n\t\t\t\t\r\n\t\t\t\tif (_springOffEnemy != -1)\r\n\t\t\t\t{\r\n\t\t\t\t\tif (_ce.input.isDown(Keyboard.SPACE))\r\n\t\t\t\t\t\tvelocity.y = -enemySpringJumpHeight;\r\n\t\t\t\t\telse\r\n\t\t\t\t\t\tvelocity.y = -enemySpringHeight;\r\n\t\t\t\t\t_springOffEnemy = -1;\r\n\t\t\t\t}\r\n\t\t\t\t\r\n\t\t\t\t\/\/Cap velocities\r\n\t\t\t\tif (velocity.x > (maxVelocity))\r\n\t\t\t\t\tvelocity.x = maxVelocity;\r\n\t\t\t\telse if (velocity.x < (-maxVelocity))\r\n\t\t\t\t\tvelocity.x = -maxVelocity;\r\n\t\t\t\t\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\t\t\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\t\t\t\r\n\t\t\treturn _body.GetLinearVelocity().x - groundVelocityX;\r\n\t\t}\r\n\t\t\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\t\t\t\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\t\t\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\t\t\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 = CollisionCategories.Get(\"GoodGuys\");\r\n\t\t\t_fixtureDef.filter.maskBits = CollisionCategories.GetAll();\r\n\t\t}\r\n\t\t\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\t\t\r\n\t\tprotected function handlePreSolve(e:ContactEvent):void \r\n\t\t{\r\n\t\t\tif (e.other.GetBody().GetUserData() is Platform &#038;&#038; canClimb &#038;&#038; (_climbing || !_onGround)) {\r\n\t\t\t\tif ((y + height \/ 2) < (ladder.y + ladder.height \/ 2)) {\r\n\t\t\t\t\te.contact.Disable();\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\t\r\n\t\t\tif (!_ducking)\r\n\t\t\t\treturn;\r\n\t\t\t\t\r\n\t\t\tvar other:PhysicsObject = e.other.GetBody().GetUserData() as PhysicsObject;\r\n\t\t\t\r\n\t\t\tvar heroTop:Number = y;\r\n\t\t\tvar objectBottom:Number = other.y + (other.height \/ 2);\r\n\t\t\t\r\n\t\t\tif (objectBottom < heroTop)\r\n\t\t\t\te.contact.Disable();\r\n\t\t}\r\n\t\t\r\n\t\tprotected function handleBeginContact(e:ContactEvent):void\r\n\t\t{\r\n\t\t\tvar colliderBody:b2Body = e.other.GetBody();\r\n\t\t\t\r\n\t\t\tif (_enemyClass &#038;&#038; colliderBody.GetUserData() is _enemyClass)\r\n\t\t\t{\r\n\t\t\t\tif (_body.GetLinearVelocity().y < killVelocity &#038;&#038; !_hurt)\r\n\t\t\t\t{\r\n\t\t\t\t\thurt();\r\n\t\t\t\t\t\r\n\t\t\t\t\t\/\/fling the hero\r\n\t\t\t\t\tvar hurtVelocity:V2 = _body.GetLinearVelocity();\r\n\t\t\t\t\thurtVelocity.y = -hurtVelocityY;\r\n\t\t\t\t\thurtVelocity.x = hurtVelocityX;\r\n\t\t\t\t\tif (colliderBody.GetPosition().x > _body.GetPosition().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 = colliderBody.GetPosition().y * _box2D.scale - height;\r\n\t\t\t\t\tonGiveDamage.dispatch();\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\t\r\n\t\t\tif (colliderBody.GetUserData() is Sensor && colliderBody.GetUserData().isLadder) {\r\n\t\t\t\tcanClimb = true;\r\n\t\t\t\tcanDuck = false;\r\n\t\t\t\tladder = colliderBody.GetUserData();\r\n\t\t\t}\r\n\t\t\t\r\n\t\t\t\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 > 45 && collisionAngle < 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}\r\n\t\t\t}\r\n\t\t}\r\n\t\t\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}\r\n\t\t\t\r\n\t\t\tif (e.other.GetBody().GetUserData() is Sensor &#038;&#038; e.other.GetBody().GetUserData().isLadder) {\r\n\t\t\t\t_climbing = canClimb = false;\r\n\t\t\t\tcanDuck = true;\r\n\t\t\t\tgravity = originalGravity;\r\n\t\t\t}\r\n\t\t}\r\n\t\t\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\t\t\r\n\t\tprotected function updateAnimation():void\r\n\t\t{\r\n\t\t\tvar prevAnimation:String = _animation;\r\n\t\t\t\r\n\t\t\tif (_hurt)\r\n\t\t\t{\r\n\t\t\t\t_animation = \"hurt\";\r\n\t\t\t} else if (_climbing) {\r\n\t\t\t\t\r\n\t\t\t\t_animation = _climbAnimation;\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 < -acceleration)\r\n\t\t\t\t{\r\n\t\t\t\t\t_inverted = true;\r\n\t\t\t\t\t_animation = \"walk\";\r\n\t\t\t\t}\r\n\t\t\t\telse if (walkingSpeed > 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\t\t\t\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 can find my <a href=\"http:\/\/www.aymericlamboley.fr\/blog\/wp-content\/uploads\/2011\/07\/Ladder\/ladder.zip\" target=\"_blank\"><strong>zip<\/strong><\/a>.<\/p>\n<p>And now, I will upgrade on Mac OS X Lion \ud83d\ude42<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Hi, today a quick post about how to create a ladder for the Citrus Engine. It is a preview version, Michael Kerr requested me for his flash game scripting class ! I&#8217;m working with Eric to add it into the original package !! So here is the result : the ladder in action<\/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,33,11,114],"tags":[15,27,50,34,26,69],"_links":{"self":[{"href":"http:\/\/www.aymericlamboley.fr\/blog\/wp-json\/wp\/v2\/posts\/330"}],"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=330"}],"version-history":[{"count":9,"href":"http:\/\/www.aymericlamboley.fr\/blog\/wp-json\/wp\/v2\/posts\/330\/revisions"}],"predecessor-version":[{"id":1296,"href":"http:\/\/www.aymericlamboley.fr\/blog\/wp-json\/wp\/v2\/posts\/330\/revisions\/1296"}],"wp:attachment":[{"href":"http:\/\/www.aymericlamboley.fr\/blog\/wp-json\/wp\/v2\/media?parent=330"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/www.aymericlamboley.fr\/blog\/wp-json\/wp\/v2\/categories?post=330"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/www.aymericlamboley.fr\/blog\/wp-json\/wp\/v2\/tags?post=330"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}