{"id":530,"date":"2012-01-21T15:46:08","date_gmt":"2012-01-21T14:46:08","guid":{"rendered":"http:\/\/www.aymericlamboley.fr\/blog\/?p=530"},"modified":"2014-11-01T14:56:11","modified_gmt":"2014-11-01T13:56:11","slug":"citrusengine-goes-stage3d-with-starling","status":"publish","type":"post","link":"http:\/\/www.aymericlamboley.fr\/blog\/citrusengine-goes-stage3d-with-starling\/","title":{"rendered":"CitrusEngine goes Stage3D with Starling"},"content":{"rendered":"<p>Hey folks, happy new year! My previous post go back from 1st december, I&#8217;ve been really busy : adding Starling in the CitrusEngine, learning iOS and working on my 2nd year school project. And obviously some great holidays between christmas &amp; the new year.<\/p>\n<p>Today I&#8217;m very pleased to share with you the Citrus Engine working on Starling! For those who don&#8217;t know it, it&#8217;s a platformer game engine using Box2D Alchemy and Signal libraries. However, it&#8217;s not only made for platformer games, you can use it for every 2D contents even if it isn&#8217;t a game thanks to its great Box2D management. So before this update, it has supported : Box2D physics, Signals event, a Sound Manager, a Console for quick debugging &amp; tools, Input management, lots of defined objects like Hero, Baddy, Platform, Coin, Sensor, Moving Platform&#8230;, parallax, a Loader manager, level editors thanks to its own (the Level Architect made with Air), or using Flash Pro, or Gleed2D. 2 views : the flash display list and blitting, and a layer management!<\/p>\n<p>Thanks to this update, the CitrusEngine now support the great stage3d framework Starling and I&#8217;ve also added 3 new objects Cannon, Teleporter &amp; Treadmill, a Level Manager and an abstract class to store the game&#8217;s data. This two last things are optional you may use it or not. In this blog post I will explain how I&#8217;ve adapted the CE for Starling, and my updates with some examples. For a quick getting start with the engine please refer on its website or to the tutorials on this blog.<\/p>\n<p>But let&#8217;s stop chatting, it&#8217;s time to test the demo :<br \/>\n<a href=\"http:\/\/www.aymericlamboley.fr\/blog\/wp-content\/uploads\/2012\/01\/index.html\" target=\"_blank\"><strong>Click here for the demo<\/strong><\/a>.<\/p>\n<p><!--more--><\/p>\n<p>The first level is really simple, some graphics a hero and a bad guy with some sensors, particles and text. The second level is just for performance test with Box2D &amp; Starling.<\/p>\n<p>Is there anything I could do before but not anymore with Starling? NO! The Starling view has not added any restriction on the engine. So you can also make this games with the Starling view : <a href=\"http:\/\/citrusengine.com\/\" target=\"_blank\"><strong>CE&#8217;s website with its demo<\/strong><\/a> and <a href=\"http:\/\/www.aymericlamboley.fr\/blog\/kinessia-game-school-project\/\" target=\"_blank\"><strong>my 1st year school project<\/strong><\/a>.<\/p>\n<p><strong>TOOLS :<\/strong><br \/>\nOk, so before starting the explanations let&#8217;s start with tools you may use : you need to target the Flash Player 11, grab the last flex SDK and code into FDT\/FlashBuilder\/FlashDevelop. You can use Flash Pro CS3 and + for creating your levels, but please don&#8217;t code with that! Then there are 4 awesome tools that I use : TexturePacker to create SpriteSheets, PhysicsEditor and my <a target=\"_blank\" href=\"http:\/\/www.aymericlamboley.fr\/blog\/physicseditor-template-for-the-citrus-engine\/\">template<\/a> to target the CitrusEngine, and finally two others for mac only ParticleDesigner &#038; GlyphDesigner. Also you may find useful my CE flash extension panel. <a target=\"_blank\" href=\"http:\/\/code.google.com\/p\/citrus-engine\/\"><em>All the content related with the CitrusEngine is available on its google code<\/em><\/a>.<\/p>\n<p><strong>STARLING VIEW :<\/strong><br \/>\nFirst of all, thanks Daniel for this awesome framework and your help! My main constraint, for this third engine view, was to keep a backward compatibility. If you have currently project made with the flash display list and you update the engine on its new version, you shouldn&#8217;t have any problem (be careful you need to compile for FP11 since it uses Stage3D due to Starling).<br \/>\nIn the Main Class which extends CitrusEngine we used to create a new State like that :<\/p>\n<pre lang=\"actionscript\">state = new MySpriteGameState();<\/pre>\n<p>Now with Starling :<\/p>\n<pre lang=\"actionscript\">\/\/ 2 params available, debug mode and anti-aliasing\r\nsetUpStarling(true, 4);\r\nstate = new MyStarlingGameState();<\/pre>\n<p>You can set up the debugger mode there, it displays the Mr. Doob Stats class adapted for Starling by Nicolas Gans, thank you!<br \/>\nThe starling var is protected, so you may defined it an other way. At the moment it does :<\/p>\n<pre lang=\"actionscript\">public function setUpStarling(debugMode:Boolean = false, antiAliasing:uint = 1):void {\r\n\t\t\t\r\n\tstarlingDebugMode = debugMode;\r\n\t\r\n\t_starling = new Starling(RootClass, stage);\r\n\t_starling.antiAliasing = antiAliasing;\r\n\t_starling.start();\r\n}<\/pre>\n<p>starlingDebugMode is a public static var. The RootClass is an internal class to the CitrusEngine class :<\/p>\n<pre lang=\"actionscript\">import starling.display.Sprite;\r\nimport starling.extensions.utils.Stats;\r\n\r\nimport com.citrusengine.core.CitrusEngine;\r\n\r\n\/**\r\n * RootClass is the root of Starling, it is never destroyed and only accessed through <code>_starling.stage<\/code>.\r\n * It may display a Stats class instance which contains Memory & FPS informations.\r\n *\/\r\ninternal class RootClass extends Sprite {\r\n\t\r\n\tpublic function RootClass() {\r\n\t\t\r\n\t\tif (CitrusEngine.starlingDebugMode)\r\n\t\t\taddChild(new Stats());\r\n\t}\r\n}<\/pre>\n<p>The state var is defined as a state&#8217;s interface, IState, there are two states : State which extends flash.display.Sprite and StarlingState which extends starling.display.Sprite ; there are very similar. Finally the Starling view is very similar to the old CE Sprite view. Like the Blitting view, these 3 views extends CitrusView. The StarlingView is a clone to the SpriteView, and its Art class too. However there is one nice thing : re open the demo, press tab to open the console, and write : <\/p>\n<pre lang=\"actionscript\">set Box2D visible true<\/pre>\n<p>The Box2D debug view was required, it was on the top of my to-do list. But that wasn&#8217;t quite obvious : there isn&#8217;t any graphics api with Starling, so the first solution was to create a texture of this graphics and add it as an image, but textures are limited to 2048 * 2048. And in an enter frame that was a performance killer. So, the Box2D debug view is running on the flash display list :<\/p>\n<pre lang=\"actionscript\" line=\"1\">package com.citrusengine.view.starlingview {\r\n\t\r\n\timport Box2DAS.Dynamics.b2DebugDraw;\r\n\r\n\timport starling.core.Starling;\r\n\timport starling.display.Sprite;\r\n\timport starling.events.Event;\r\n\r\n\timport com.citrusengine.physics.Box2D;\r\n\r\n\t\/**\r\n\t * This displays Box2D's debug graphics. It does so properly through Citrus Engine's view manager. Box2D by default\r\n\t * sets visible to false, so you'll need to set the Box2D object's visible property to true in order to see the debug graphics. \r\n\t *\/\r\n\tpublic class Box2DDebugArt extends Sprite {\r\n\r\n\t\tprivate var _box2D:Box2D;\r\n\t\tprivate var _debugDrawer:b2DebugDraw;\r\n\r\n\t\tpublic function Box2DDebugArt() {\r\n\t\t\t\r\n\t\t\taddEventListener(Event.ADDED, handleAddedToParent);\r\n\t\t\taddEventListener(Event.ENTER_FRAME, handleEnterFrame);\r\n\t\t\taddEventListener(Event.REMOVED, destroy);\r\n\t\t}\r\n\r\n\t\tprivate function handleAddedToParent(evt:Event):void {\r\n\t\t\t\r\n\t\t\tremoveEventListener(Event.ADDED, handleAddedToParent);\r\n\r\n\t\t\t_box2D = StarlingArt(parent).citrusObject as Box2D;\r\n\r\n\t\t\t_debugDrawer = new b2DebugDraw();\r\n\t\t\tStarling.current.nativeStage.addChild(_debugDrawer);\r\n\t\t\t_debugDrawer.world = _box2D.world;\r\n\t\t\t_debugDrawer.scale = _box2D.scale;\r\n\t\t}\r\n\r\n\t\tprivate function destroy(evt:Event):void {\r\n\t\t\t\r\n\t\t\tremoveEventListener(Event.ADDED, handleAddedToParent);\r\n\t\t\tremoveEventListener(Event.ENTER_FRAME, handleEnterFrame);\r\n\t\t\tremoveEventListener(Event.REMOVED, destroy);\r\n\t\t}\r\n\r\n\t\tprivate function handleEnterFrame(evt:Event):void {\r\n\t\t\t\r\n\t\t\t_debugDrawer.Draw();\r\n\t\t}\r\n\t}\r\n}<\/pre>\n<p>Now, let&#8217;s take a look on the most important class, the StarlingArt. It manages the art for every object. It handles many objects : png jpg gif pictures, swf (yes!), class reference, a fully qualified class name in string form (useful for a level editor!), or a Starling DisplayObject.<br \/>\nTo create a CE object in your state class : <\/p>\n<pre lang=\"actionscript\">override public function initialize():void {\r\n\t\t\t\r\n\tsuper.initialize();\r\n\r\n\tvar box2d:Box2D = new Box2D(\"Box2D\");\r\n\t\/\/box2d.visible = true;\r\n\tadd(box2d);\r\n\r\n\tvar baddy1:Baddy = new Baddy(\"Baddy1\", {view:\"baddy.png\", x:100, y:100, width:40, height:40})\r\n\tadd(bady1)\r\n\r\n\tvar baddy2:Baddy = new Baddy(\"Baddy2\", {view:\"baddy.swf\", x:400, y:100, width:40, height:40})\r\n\tadd(bady2)\r\n}<\/pre>\n<p>But how it works with a flash swf ? We have to thanks Emiliano Angelini which have created a Starling extension to make DynamicTextureAtlas, so your swf is transformed in a TextureAtlas &#8220;on the fly&#8221;. Awesome feature for a quick prototyping.<br \/>\nThe StarlingArt class :<\/p>\n<pre lang=\"actionscript\" line=\"1\">package com.citrusengine.view.starlingview {\r\n\r\n\timport Box2DAS.Dynamics.b2DebugDraw;\r\n\r\n\timport starling.core.Starling;\r\n\timport starling.display.DisplayObject;\r\n\timport starling.display.Image;\r\n\timport starling.display.MovieClip;\r\n\timport starling.display.Sprite;\r\n\timport starling.extensions.textureAtlas.DynamicAtlas;\r\n\timport starling.textures.Texture;\r\n\timport starling.textures.TextureAtlas;\r\n\timport starling.utils.deg2rad;\r\n\r\n\timport com.citrusengine.view.ISpriteView;\r\n\r\n\timport flash.display.Bitmap;\r\n\timport flash.display.Loader;\r\n\timport flash.events.Event;\r\n\timport flash.events.IOErrorEvent;\r\n\timport flash.net.URLRequest;\r\n\timport flash.utils.Dictionary;\r\n\timport flash.utils.getDefinitionByName;\r\n\r\n\t\/**\r\n\t * This is the class that all art objects use for the StarlingView state view. If you are using the StarlingView (as opposed to the blitting view, for instance),\r\n\t * then all your graphics will be an instance of this class. There are 2 ways to manage MovieClip :\r\n\t * - specify a \"object.swf\" in the view property of your object's creation.\r\n\t * - add an AnimationSequence to your view property of your object's creation, see the AnimationSequence for more informations about it.\r\n\t * The AnimationSequence is more optimized than the .swf which creates textures \"on the fly\" thanks to the DynamicAtlas class.\r\n\t * \r\n\t * This class does the following things:\r\n\t * \r\n\t * 1) Creates the appropriate graphic depending on your CitrusObject's view property (loader, sprite, or bitmap), and loads it if it is a non-embedded graphic.\r\n\t * 2) Aligns the graphic with the appropriate registration (topLeft or center).\r\n\t * 3) Calls the MovieClip's appropriate frame label based on the CitrusObject's animation property.\r\n\t * 4) Updates the graphic's properties to be in-synch with the CitrusObject's properties once per frame.\r\n\t * \r\n\t * These objects will be created by the Citrus Engine's StarlingView, so you should never make them yourself. When you use state.getArt() to gain access to your game's graphics\r\n\t * (for adding click events, for instance), you will get an instance of this object. It extends Sprite, so you can do all the expected stuff with it, \r\n\t * such as add click listeners, change the alpha, etc.\r\n\t **\/\r\n\tpublic class StarlingArt extends Sprite {\r\n\t\t\r\n\t\t\/**\r\n\t\t * The content property is the actual display object that your game object is using. For graphics that are loaded at runtime\r\n\t\t * (not embedded), the content property will not be available immediately. You can listen to the COMPLETE event on the loader\r\n\t\t * (or rather, the loader's contentLoaderInfo) if you need to know exactly when the graphic will be loaded.\r\n\t\t *\/\r\n\t\tpublic var content:DisplayObject;\r\n\r\n\t\t\/**\r\n\t\t * For objects that are loaded at runtime, this is the object that loades them. Then, once they are loaded, the content\r\n\t\t * property is assigned to loader.content.\r\n\t\t *\/\r\n\t\tpublic var loader:Loader;\r\n\r\n\t\t\/\/ properties :\r\n\t\t\r\n\t\t\/\/ determines animations playing in loop. You can add one in your state class : StarlingArt.setLoopAnimations([\"walk\", \"climb\"]);\r\n\t\tprivate static var _loopAnimation:Dictionary = new Dictionary();\r\n\t\t\r\n\t\tprivate var _citrusObject:ISpriteView;\r\n\t\tprivate var _registration:String;\r\n\t\tprivate var _view:*;\r\n\t\tprivate var _animation:String;\r\n\t\tprivate var _group:int;\r\n\t\t\r\n\t\t\/\/ fps for this MovieClip, it can be different between objects, to set it : view.getArt(myHero).fpsMC = 25; \r\n\t\tprivate var _fpsMC:uint = 30;\r\n\r\n\t\tprivate var _texture:Texture;\r\n\t\tprivate var _textureAtlas:TextureAtlas;\r\n\r\n\t\tpublic function StarlingArt(object:ISpriteView) {\r\n\r\n\t\t\t_citrusObject = object;\r\n\t\t\t\r\n\t\t\tif (_loopAnimation[\"walk\"] != true) {\r\n\t\t\t\t_loopAnimation[\"walk\"] = true;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tpublic function destroy():void {\r\n\r\n\t\t\tif (content is MovieClip) {\r\n\t\t\t\tStarling.juggler.remove(content as MovieClip);\r\n\t\t\t\t_textureAtlas.dispose();\r\n\t\t\t\tcontent.dispose();\r\n\t\t\t\r\n\t\t\t} else if (content is AnimationSequence) {\r\n\t\t\t\t\r\n\t\t\t\t(content as AnimationSequence).destroy();\r\n\t\t\t\tcontent.dispose();\r\n\t\t\t\t\r\n\t\t\t} else if (content is Image) {\r\n\t\t\t\t_texture.dispose();\r\n\t\t\t\tcontent.dispose();\r\n\t\t\t}\r\n\r\n\t\t}\r\n\t\t\r\n\t\t\/**\r\n\t\t * Add a loop animation to the Dictionnary.\r\n\t\t * @param tab an array with all the loop animation names.\r\n\t\t *\/\r\n\t\tstatic public function setLoopAnimations(tab:Array):void {\r\n\t\t\t\r\n\t\t\tfor each (var animation:String in tab) {\r\n\t\t\t\t_loopAnimation[animation] = true;\r\n\t\t\t}\r\n\t\t}\r\n\t\t\r\n\t\tstatic public function get loopAnimation():Dictionary {\r\n\t\t\treturn _loopAnimation;\r\n\t\t}\r\n\r\n\t\tpublic function get registration():String {\r\n\t\t\treturn _registration;\r\n\t\t}\r\n\r\n\t\tpublic function set registration(value:String):void {\r\n\r\n\t\t\tif (_registration == value || !content)\r\n\t\t\t\treturn;\r\n\r\n\t\t\t_registration = value;\r\n\r\n\t\t\tif (_registration == \"topLeft\") {\r\n\t\t\t\tcontent.x = 0;\r\n\t\t\t\tcontent.y = 0;\r\n\t\t\t} else if (_registration == \"center\") {\r\n\t\t\t\tcontent.x = -content.width \/ 2;\r\n\t\t\t\tcontent.y = -content.height \/ 2;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tpublic function get view():* {\r\n\t\t\treturn _view;\r\n\t\t}\r\n\r\n\t\tpublic function set view(value:*):void {\r\n\r\n\t\t\tif (_view == value)\r\n\t\t\t\treturn;\r\n\r\n\t\t\t_view = value;\r\n\r\n\t\t\tif (_view) {\r\n\t\t\t\tif (_view is String) {\r\n\t\t\t\t\t\/\/ view property is a path to an image?\r\n\t\t\t\t\tvar classString:String = _view;\r\n\t\t\t\t\tvar suffix:String = classString.substring(classString.length - 4).toLowerCase();\r\n\t\t\t\t\tif (suffix == \".swf\" || suffix == \".png\" || suffix == \".gif\" || suffix == \".jpg\") {\r\n\t\t\t\t\t\tloader = new Loader();\r\n\t\t\t\t\t\tloader.contentLoaderInfo.addEventListener(Event.COMPLETE, handleContentLoaded);\r\n\t\t\t\t\t\tloader.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, handleContentIOError);\r\n\t\t\t\t\t\tloader.load(new URLRequest(classString));\r\n\t\t\t\t\t}\r\n\t\t\t\t\t\/\/ view property is a fully qualified class name in string form. \r\n\t\t\t\t\telse {\r\n\t\t\t\t\t\tvar artClass:Class = getDefinitionByName(classString) as Class;\r\n\t\t\t\t\t\tcontent = new artClass();\r\n\t\t\t\t\t\taddChild(content);\r\n\t\t\t\t\t}\r\n\t\t\t\t} else if (_view is Class) {\r\n\t\t\t\t\t\/\/ view property is a class reference\r\n\t\t\t\t\tcontent = new citrusObject.view();\r\n\t\t\t\t\taddChild(content);\r\n\r\n\t\t\t\t} else if (_view is DisplayObject) {\r\n\t\t\t\t\t\/\/ view property is a Display Object reference\r\n\t\t\t\t\tcontent = _view;\r\n\t\t\t\t\taddChild(content);\r\n\t\t\t\t} else {\r\n\t\t\t\t\tthrow new Error(\"SpriteArt doesn't know how to create a graphic object from the provided CitrusObject \" + citrusObject);\r\n\t\t\t\t\treturn;\r\n\t\t\t\t}\r\n\r\n\t\t\t\tif (content && content.hasOwnProperty(\"initialize\"))\r\n\t\t\t\t\tcontent[\"initialize\"](_citrusObject);\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tpublic function get animation():String {\r\n\t\t\treturn _animation;\r\n\t\t}\r\n\r\n\t\tpublic function set animation(value:String):void {\r\n\r\n\t\t\tif (_animation == value)\r\n\t\t\t\treturn;\r\n\r\n\t\t\t_animation = value;\r\n\t\t\t\r\n\t\t\tif (_animation != null && _animation != \"\") {\r\n\t\t\t\t\r\n\t\t\t\tvar animLoop:Boolean = _loopAnimation[_animation];\r\n\t\t\t\t\r\n\t\t\t\tif (content is MovieClip)\r\n\t\t\t\t\t(content as MovieClip).changeTextures(_textureAtlas.getTextures(_animation), _fpsMC, animLoop);\r\n\t\t\t\t\r\n\t\t\t\tif (content is AnimationSequence)\r\n\t\t\t\t\t(content as AnimationSequence).changeAnimation(_animation, _fpsMC, animLoop);\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tpublic function get group():int {\r\n\t\t\treturn _group;\r\n\t\t}\r\n\r\n\t\tpublic function set group(value:int):void {\r\n\t\t\t_group = value;\r\n\t\t}\r\n\r\n\t\tpublic function get fpsMC():uint {\r\n\t\t\treturn _fpsMC;\r\n\t\t}\r\n\r\n\t\tpublic function set fpsMC(fpsMC:uint):void {\r\n\t\t\t_fpsMC = fpsMC;\r\n\t\t}\r\n\r\n\t\tpublic function get citrusObject():ISpriteView {\r\n\t\t\treturn _citrusObject;\r\n\t\t}\r\n\r\n\t\tpublic function update(stateView:StarlingView):void {\r\n\r\n\t\t\tif (content is Box2DDebugArt) {\r\n\r\n\t\t\t\t\/\/ Box2D view is not on the Starling display list, but on the classical flash display list.\r\n\t\t\t\t\/\/ So we need to move its view here, not in the StarlingView.\r\n\r\n\t\t\t\tvar box2dDebugArt:b2DebugDraw = (Starling.current.nativeStage.getChildAt(1) as b2DebugDraw);\r\n\r\n\t\t\t\tif (stateView.cameraTarget) {\r\n\r\n\t\t\t\t\tvar diffX:Number = (-stateView.cameraTarget.x + stateView.cameraOffset.x) - box2dDebugArt.x;\r\n\t\t\t\t\tvar diffY:Number = (-stateView.cameraTarget.y + stateView.cameraOffset.y) - box2dDebugArt.y;\r\n\t\t\t\t\tvar velocityX:Number = diffX * stateView.cameraEasing.x;\r\n\t\t\t\t\tvar velocityY:Number = diffY * stateView.cameraEasing.y;\r\n\t\t\t\t\tbox2dDebugArt.x += velocityX;\r\n\t\t\t\t\tbox2dDebugArt.y += velocityY;\r\n\r\n\t\t\t\t\t\/\/ Constrain to camera bounds\r\n\t\t\t\t\tif (stateView.cameraBounds) {\r\n\t\t\t\t\t\tif (-box2dDebugArt.x <= stateView.cameraBounds.left || stateView.cameraBounds.width < stateView.cameraLensWidth)\r\n\t\t\t\t\t\t\tbox2dDebugArt.x = -stateView.cameraBounds.left;\r\n\t\t\t\t\t\telse if (-box2dDebugArt.x + stateView.cameraLensWidth >= stateView.cameraBounds.right)\r\n\t\t\t\t\t\t\tbox2dDebugArt.x = -stateView.cameraBounds.right + stateView.cameraLensWidth;\r\n\r\n\t\t\t\t\t\tif (-box2dDebugArt.y <= stateView.cameraBounds.top || stateView.cameraBounds.height < stateView.cameraLensHeight)\r\n\t\t\t\t\t\t\tbox2dDebugArt.y = -stateView.cameraBounds.top;\r\n\t\t\t\t\t\telse if (-box2dDebugArt.y + stateView.cameraLensHeight >= stateView.cameraBounds.bottom)\r\n\t\t\t\t\t\t\tbox2dDebugArt.y = -stateView.cameraBounds.bottom + stateView.cameraLensHeight;\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\r\n\t\t\t\tbox2dDebugArt.visible = _citrusObject.visible;\r\n\r\n\t\t\t} else {\r\n\r\n\t\t\t\t\/\/ The position = object position + (camera position * inverse parallax)\r\n\t\t\t\tx = _citrusObject.x + (-stateView.viewRoot.x * (1 - _citrusObject.parallax)) + _citrusObject.offsetX;\r\n\t\t\t\ty = _citrusObject.y + (-stateView.viewRoot.y * (1 - _citrusObject.parallax)) + _citrusObject.offsetY;\r\n\t\t\t\tvisible = _citrusObject.visible;\r\n\t\t\t\trotation = deg2rad(_citrusObject.rotation);\r\n\t\t\t\tscaleX = _citrusObject.inverted ? -1 : 1;\r\n\t\t\t\tregistration = _citrusObject.registration;\r\n\t\t\t\tview = _citrusObject.view;\r\n\t\t\t\tanimation = _citrusObject.animation;\r\n\t\t\t\tgroup = _citrusObject.group;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tprivate function handleContentLoaded(evt:Event):void {\r\n\r\n\t\t\tif (evt.target.loader.content is flash.display.MovieClip) {\r\n\r\n\t\t\t\t_textureAtlas = DynamicAtlas.fromMovieClipContainer(evt.target.loader.content, 1, 0, true, true);\r\n\t\t\t\tcontent = new MovieClip(_textureAtlas.getTextures(animation), _fpsMC);\r\n\t\t\t\tStarling.juggler.add(content as MovieClip);\r\n\t\t\t}\r\n\r\n\t\t\tif (evt.target.loader.content is Bitmap) {\r\n\t\t\t\t\r\n\t\t\t\t_texture = Texture.fromBitmap(evt.target.loader.content);\r\n\t\t\t\tcontent = new Image(_texture);\r\n\t\t\t}\r\n\r\n\t\t\taddChild(content);\r\n\t\t}\r\n\r\n\t\tprivate function handleContentIOError(evt:IOErrorEvent):void {\r\n\t\t\tthrow new Error(evt.text);\r\n\t\t}\r\n\r\n\t}\r\n}<\/pre>\n<p>There are three new important things :<br \/>\n&#8211; the static var loopAnimation dictionnary, to determine if the animation will play as a loop. Try to don&#8217;t have same animations name if one is a loop whereas the other isn&#8217;t!<br \/>\n&#8211; the var fpsMC, thanks to Starling each MovieClip may have a different fps, default is 30. This property is not integrated like other object&#8217;s properties (x, visible, view&#8230;) , because it is not available with the other views.<br \/>\n&#8211; the AnimationSequence class. With Starling there isn&#8217;t a class to manage the switch between animations, so I&#8217;ve created this one :<\/p>\n<pre lang=\"actionscript\" line=\"1\">package com.citrusengine.view.starlingview {\r\n\r\n\timport starling.core.Starling;\r\n\timport starling.display.MovieClip;\r\n\timport starling.display.Sprite;\r\n\timport starling.textures.TextureAtlas;\r\n\r\n\timport flash.utils.Dictionary;\r\n\r\n\t\/**\r\n\t * The Animation Sequence class represents all object animations in one sprite sheet. You have to create your texture atlas in your state class.\r\n\t * Example : var hero:Hero = new Hero(\"Hero\", {x:400, width:60, height:130, view:new AnimationSequence(textureAtlas, [\"walk\", \"duck\", \"idle\", \"jump\"], \"idle\")});\r\n\t * \r\n\t * @param textureAtlas : a TextureAtlas object with all your object's animations\r\n\t * @param animations : an array with all your object's animations as a String\r\n\t * @param firstAnimation : a string of your default animation at its creation\r\n\t *\/\r\n\tpublic class AnimationSequence extends Sprite {\r\n\r\n\t\tprivate var _textureAtlas:TextureAtlas;\r\n\t\tprivate var _animations:Array;\r\n\t\tprivate var _mcSequences:Dictionary;\r\n\t\tprivate var _previousAnimation:String;\r\n\r\n\t\tpublic function AnimationSequence(textureAtlas:TextureAtlas, animations:Array, firstAnimation:String) {\r\n\r\n\t\t\tsuper();\r\n\r\n\t\t\t_textureAtlas = textureAtlas;\r\n\r\n\t\t\t_animations = animations;\r\n\r\n\t\t\t_mcSequences = new Dictionary();\r\n\r\n\t\t\tfor each (var animation:String in animations) {\r\n\t\t\t\t\r\n\t\t\t\tif (_textureAtlas.getTextures(animation).length == 0) {\r\n\t\t\t\t\tthrow new Error(\"One object doesn't have the \" + animation + \" animation in its TextureAtlas\");\r\n\t\t\t\t}\r\n\t\t\t\t\r\n\t\t\t\t_mcSequences[animation] = new MovieClip(_textureAtlas.getTextures(animation));\r\n\t\t\t\t\r\n\t\t\t}\r\n\t\t\t\r\n\t\t\taddChild(_mcSequences[firstAnimation]);\r\n\t\t\tStarling.juggler.add(_mcSequences[firstAnimation]);\r\n\t\t\t\r\n\t\t\t_previousAnimation = firstAnimation;\t\t\t\r\n\t\t}\r\n\t\t\r\n\t\t\/**\r\n\t\t * Called by StarlingArt, managed the MC's animations.\r\n\t\t * @param animation : the MC's animation\r\n\t\t * @param fps : the MC's fps\r\n\t\t * @param animLoop : true if the MC is a loop\r\n\t\t *\/\r\n\t\tpublic function changeAnimation(animation:String, fps:Number, animLoop:Boolean):void {\r\n\t\t\t\r\n\t\t\tif (!(_mcSequences[animation])) {\r\n\t\t\t\tthrow new Error(\"One object doesn't have the \" + animation + \" animation set up in its initial array\");\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\t\t\t\r\n\t\t\tremoveChild(_mcSequences[_previousAnimation]);\r\n\t\t\tStarling.juggler.remove(_mcSequences[_previousAnimation]);\r\n\t\t\t\r\n\t\t\taddChild(_mcSequences[animation]);\r\n\t\t\tStarling.juggler.add(_mcSequences[animation]);\r\n\t\t\t_mcSequences[animation].fps = fps;\r\n\t\t\t_mcSequences[animation].loop = animLoop;\r\n\t\t\t\r\n\t\t\t_previousAnimation = animation;\r\n\t\t}\r\n\t\t\r\n\t\tpublic function destroy():void {\r\n\t\t\t\r\n\t\t\tremoveChild(_mcSequences[_previousAnimation]);\r\n\t\t\tStarling.juggler.remove(_mcSequences[_previousAnimation]);\r\n\t\t\t\r\n\t\t\tfor each (var animation : String in _animations)\r\n\t\t\t\t_mcSequences[animation].dispose();\r\n\t\t\t\r\n\t\t\t_textureAtlas.dispose();\r\n\t\t\t\r\n\t\t\t_mcSequences = null;\r\n\t\t}\r\n\t}\r\n}<\/pre>\n<p>For using it, you&#8217;ve to create a SpriteSheet. I use TexturePacker. Create all your different MovieClip in flash, each one represent a different state. They all should have the same scene width\/height. Then export the swfs and import them in TexturePacker using Sparrow data format. Use the trim and enabe auto alias option, but not the crop! Export, you have your SpriteSheet with a xml.<br \/>\nAnd then embed them in your state class, and use it like that :<\/p>\n<pre lang=\"actionscript\">var bitmap:Bitmap = new _heroPng();\r\nvar texture:Texture = Texture.fromBitmap(bitmap);\r\nvar xml:XML = XML(new _heroConfig());\r\nvar sTextureAtlas:TextureAtlas = new TextureAtlas(texture, xml);\r\n\r\n_hero = Hero(getFirstObjectByType(Hero));\r\n_hero.view = new AnimationSequence(sTextureAtlas, [\"walk\", \"duck\", \"idle\", \"jump\", \"hurt\"], \"idle\");<\/pre>\n<p>You can find the swfs here : <a href=\"http:\/\/www.aymericlamboley.fr\/blog\/wp-content\/uploads\/2012\/01\/animations.zip\">zip<\/a>.<br \/>\nMaybe I will include the png &#038; xml directly in the constructor param, so we will not have to create each time the texture altas. But if we have several objects with the same texture, it will be less optimized&#8230; Don&#8217;t hesitate to comment to tell me your preferences!<\/p>\n<p>So I think this is it for the Starling view. You will find other informations in the demo code, showed later.<\/p>\n<p><strong>ABSTRACT GAME DATA<\/strong><br \/>\nThat was a request of the community : having a simple way to save game informations, datas&#8230; By the way, thanks Roger Clark, for helping lots of people this last month on the forum!!<br \/>\nThe problem was : how to create a CE class which will not be modified by users, but will help them? An abstract dynamic class did the trick :<\/p>\n<pre lang=\"actionscript\" line=\"1\">package com.citrusengine.utils {\r\n\r\n\timport org.osflash.signals.Signal;\r\n\t\r\n\t\/**\r\n\t * This is an (optional) abstract class to store your game's data such as lives, score, levels...\r\n\t * You should extend this class & instantiate it into your main class using the gameData variable.\r\n\t * You can dispatch a signal, dataChanged, if you update one of your data.\r\n\t * For more information, watch the example below. \r\n\t *\/\r\n\tdynamic public class AGameData {\r\n\t\t\r\n\t\tpublic var dataChanged:Signal;\r\n\t\t\r\n\t\tprotected var _lives:int = 3;\r\n\t\tprotected var _score:int = 0;\r\n\t\tprotected var _timeleft:int = 300;\r\n\t\t\r\n\t\tprotected var _levels:Array;\r\n\t\t\r\n\t\tpublic function AGameData() {\r\n\t\t\t\r\n\t\t\tdataChanged = new Signal(String, Object);\r\n\t\t}\r\n\r\n\t\tpublic function get lives():int {\r\n\t\t\treturn _lives;\r\n\t\t}\r\n\r\n\t\tpublic function set lives(lives:int):void {\r\n\t\t\t\r\n\t\t\t_lives = lives;\r\n\t\t\t\r\n\t\t\tdataChanged.dispatch(\"lives\", _lives);\r\n\t\t}\r\n\r\n\t\tpublic function get score():int {\r\n\t\t\treturn _score;\r\n\t\t}\r\n\r\n\t\tpublic function set score(score:int):void {\r\n\t\t\t\r\n\t\t\t_score = score;\r\n\t\t\t\r\n\t\t\tdataChanged.dispatch(\"score\", _score);\r\n\t\t}\r\n\r\n\t\tpublic function get timeleft():int {\r\n\t\t\treturn _timeleft;\r\n\t\t}\r\n\r\n\t\tpublic function set timeleft(timeleft:int):void {\r\n\t\t\t\r\n\t\t\t_timeleft = timeleft;\r\n\t\t\t\r\n\t\t\tdataChanged.dispatch(\"timeleft\", _timeleft);\r\n\t\t}\r\n\t\t\r\n\t\tpublic function destroy():void {\r\n\t\t\t\r\n\t\t\tdataChanged.removeAll();\r\n\t\t}\r\n\t}\r\n}<\/pre>\n<p>Finally we just create our GameData class which extends AGameData, and we use it like this :<\/p>\n<pre lang=\"actionscript\">gameData = new MyGameData();\r\n\/\/example in the main class :\r\nlevelManager.levels = gameData.levels;\r\n\r\n\/\/examples in the state class :\r\nCitrusEngine.getInstance().gameData.dataChanged.add(myFunctionDataChanged);\r\nCitrusEngine.getInstance().gameData.lives--;<\/pre>\n<p><strong>LEVEL MANAGER :<\/strong><br \/>\nThe Level Manager was an other community request, so I&#8217;ve added mine. It is quite complex, but very powerful :<\/p>\n<pre lang=\"actionscript\" line=\"1\">package com.citrusengine.utils {\r\n\r\n\timport org.osflash.signals.Signal;\r\n\r\n\timport flash.display.Loader;\r\n\timport flash.events.Event;\r\n\timport flash.net.URLRequest;\r\n\r\n\t\/**\r\n\t * The LevelManager is a complex but powerful class, you can use simple states for levels with SWC\/SWF\/XML.\r\n\t * Before using it, be sure that you have good OOP knowledge. For using it, you must use an Abstract state class \r\n\t * that you give as constructor parameter : Alevel. \r\n\t * \r\n\t * The four ways to set up your level : \r\n\t * <code>levelManager.levels = [Level1, Level2];\r\n\t * levelManager.levels = [[Level1, \"level1.swf\"], [level2, \"level2.swf\"]];\r\n\t * levelManager.levels = [[Level1, \"level1.xml\"], [level2, \"level2.xml\"]];\r\n\t * levelManager.levels = [[Level1, Level1_SWC], [level2, Level2_SWC]];\r\n\t * <\/code>\r\n\t * \r\n\t * An instanciation exemple in your Main class (you may also use the AGameData to store your levels) :\r\n\t * <code>levelManager = new LevelManager(ALevel);\r\n\t * levelManager.onLevelChanged.add(_onLevelChanged);\r\n\t * levelManager.levels = [Level1, Level2];\r\n\t * levelManager.gotoLevel();<\/code>\r\n\t * \r\n\t * The _onLevelChanged function gives in parameter the Alevel that you associate to your state : <code>state = lvl;<\/code>\r\n\t * Then you can associate other function :\r\n\t * <code>lvl.lvlEnded.add(_nextLevel);\r\n\t * lvl.restartLevel.add(_restartLevel);<\/code>\r\n\t * And their respective actions :\r\n\t * <code>_levelManager.nextLevel();\r\n\t * state = _levelManager.currentLevel as IState;<\/code>\r\n\t * \r\n\t * The ALevel class must implement public var lvlEnded & restartLevel Signals in its constructor.\r\n\t * If you have associated a SWF or SWC file to your level, you must add a flash MovieClip as a parameter into its constructor, \r\n\t * or a XML if it is one!\r\n\t *\/\r\n\tpublic class LevelManager {\r\n\r\n\t\tstatic private var _instance:LevelManager;\r\n\r\n\t\tpublic var onLevelChanged:Signal;\r\n\t\t\r\n\t\tprivate var _ALevel:Class;\r\n\t\tprivate var _levels:Array;\r\n\t\tprivate var _currentIndex:uint;\r\n\t\tprivate var _currentLevel:Object;\r\n\r\n\t\tpublic function LevelManager(ALevel:Class) {\r\n\r\n\t\t\t_instance = this;\r\n\t\t\t\r\n\t\t\t_ALevel = ALevel;\r\n\r\n\t\t\tonLevelChanged = new Signal(_ALevel);\r\n\t\t\t_currentIndex = 0;\r\n\t\t}\r\n\r\n\t\tstatic public function getInstance():LevelManager {\r\n\t\t\treturn _instance;\r\n\t\t}\r\n\r\n\r\n\t\tpublic function destroy():void {\r\n\t\t\t\r\n\t\t\tonLevelChanged.removeAll();\r\n\t\t\t\r\n\t\t\t_currentLevel = null;\r\n\t\t}\r\n\r\n\t\tpublic function nextLevel():void {\r\n\r\n\t\t\tif (_currentIndex < _levels.length - 1) {\r\n\t\t\t\t++_currentIndex;\r\n\t\t\t}\r\n\r\n\t\t\tgotoLevel();\r\n\t\t}\r\n\r\n\t\tpublic function prevLevel():void {\r\n\r\n\t\t\tif (_currentIndex > 0) {\r\n\t\t\t\t--_currentIndex;\r\n\t\t\t}\r\n\r\n\t\t\tgotoLevel();\r\n\t\t}\r\n\r\n\t\t\/**\r\n\t\t * Call the LevelManager instance's gotoLevel() function to launch your first level, or you may specify it.\r\n\t\t * @param index : the level index from 1 to ... ; different from the levels' array indexes.\r\n\t\t *\/\r\n\t\tpublic function gotoLevel(index:int = -1):void {\r\n\r\n\t\t\tif (_currentLevel != null) {\r\n\t\t\t\t_currentLevel.lvlEnded.remove(_onLevelEnded);\r\n\t\t\t}\r\n\r\n\t\t\tvar loader:Loader = new Loader();\r\n\r\n\t\t\tif (index != -1) {\r\n\t\t\t\t_currentIndex = index - 1;\r\n\t\t\t}\r\n\r\n\t\t\t\/\/ Level SWF and SWC are undefined\r\n\t\t\tif (_levels[_currentIndex][0] == undefined) {\r\n\r\n\t\t\t\t_currentLevel = _ALevel(new _levels[_currentIndex]);\r\n\t\t\t\t_currentLevel.lvlEnded.add(_onLevelEnded);\r\n\r\n\t\t\t\tonLevelChanged.dispatch(_currentLevel);\r\n\t\t\t\t\r\n\t\t\t\/\/ It's a SWC ?\r\n\t\t\t} else if (_levels[_currentIndex][1] is Class) {\r\n\t\t\t\t\r\n\t\t\t\t_currentLevel = _ALevel(new _levels[_currentIndex][0](new _levels[_currentIndex][1]()));\r\n\t\t\t\t_currentLevel.lvlEnded.add(_onLevelEnded);\r\n\t\t\t\t\r\n\t\t\t\tonLevelChanged.dispatch(_currentLevel);\r\n\t\t\t\t\r\n\t\t\t\/\/ So it's a SWF or XML, we load it \r\n\t\t\t} else {\r\n\r\n\t\t\t\tloader.load(new URLRequest(_levels[_currentIndex][1]));\r\n\t\t\t\tloader.contentLoaderInfo.addEventListener(Event.COMPLETE,_levelLoaded);\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tprivate function _levelLoaded(evt:Event):void {\r\n\r\n\t\t\t_currentLevel = _ALevel(new _levels[_currentIndex][0](evt.target.loader.content));\r\n\t\t\t_currentLevel.lvlEnded.add(_onLevelEnded);\r\n\r\n\t\t\tonLevelChanged.dispatch(_currentLevel);\r\n\r\n\t\t\tevt.target.removeEventListener(Event.COMPLETE, _levelLoaded);\r\n\t\t\tevt.target.loader.unloadAndStop();\r\n\t\t}\r\n\r\n\t\tprivate function _onLevelEnded():void {\r\n\r\n\t\t}\r\n\t\t\r\n\t\tpublic function get levels():Array {\r\n\t\t\treturn _levels;\r\n\t\t}\r\n\t\t\r\n\t\tpublic function set levels(levels:Array):void {\r\n\t\t\t_levels = levels;\r\n\t\t}\r\n\r\n\t\tpublic function get currentLevel():Object {\r\n\t\t\treturn _currentLevel;\r\n\t\t}\r\n\r\n\t\tpublic function set currentLevel(currentLevel:Object):void {\r\n\t\t\t_currentLevel = currentLevel;\r\n\t\t}\r\n\r\n\t\tpublic function get nameCurrentLevel():String {\r\n\t\t\treturn _currentLevel.nameLevel;\r\n\t\t}\r\n\t}\r\n}<\/pre>\n<p>Let&#8217;s see how it is used with the demo.<\/p>\n<p><strong>THE DEMO CODE :<\/strong><br \/>\nYou&#8217;re always here ? This is great \ud83d\ude00 It&#8217;s time for all the demo code :<\/p>\n<p>Main :<\/p>\n<pre lang=\"actionscript\" line=\"1\">package {\r\n\r\n\timport com.citrusengine.core.CitrusEngine;\r\n\timport com.citrusengine.core.IState;\r\n\timport com.citrusengine.utils.LevelManager;\r\n\t\r\n\t[SWF(backgroundColor=\"#FFFFFF\", frameRate=\"60\", width=\"640\", height=\"480\")]\r\n\r\n\t\/**\r\n\t * @author Aymeric\r\n\t *\/\r\n\tpublic class Main extends CitrusEngine {\r\n\r\n\t\tpublic function Main() {\r\n\t\t\t\r\n\t\t\tsetUpStarling(true);\r\n\t\t\t\r\n\t\t\tgameData = new MyGameData();\r\n\t\t\t\r\n\t\t\tlevelManager = new LevelManager(ALevel);\r\n\t\t\tlevelManager.onLevelChanged.add(_onLevelChanged);\r\n\t\t\tlevelManager.levels = gameData.levels;\r\n\t\t\tlevelManager.gotoLevel();\r\n\t\t}\r\n\t\t\r\n\t\tprivate function _onLevelChanged(lvl:ALevel):void {\r\n\r\n\t\t\tstate = lvl;\r\n\r\n\t\t\tlvl.lvlEnded.add(_nextLevel);\r\n\t\t\tlvl.restartLevel.add(_restartLevel);\r\n\t\t}\r\n\r\n\t\tprivate function _nextLevel():void {\r\n\r\n\t\t\tlevelManager.nextLevel();\r\n\t\t}\r\n\r\n\t\tprivate function _restartLevel():void {\r\n\r\n\t\t\tstate = levelManager.currentLevel as IState;\r\n\t\t}\r\n\t}\r\n}<\/pre>\n<p>MyGameData :<\/p>\n<pre lang=\"actionscript\" line=\"1\">package {\r\n\r\n\timport com.citrusengine.utils.AGameData;\r\n\r\n\t\/**\r\n\t * @author Aymeric\r\n\t *\/\r\n\tpublic class MyGameData extends AGameData {\r\n\r\n\t\tpublic function MyGameData() {\r\n\t\t\t\r\n\t\t\tsuper();\r\n\t\t\t\r\n\t\t\t_levels = [[Level1, \"levels\/A1\/LevelA1.swf\"], [Level2, \"levels\/A2\/LevelA2.swf\"]];\r\n\t\t}\r\n\t\t\r\n\t\tpublic function get levels():Array {\r\n\t\t\treturn _levels;\r\n\t\t}\r\n\r\n\t\toverride public function destroy():void {\r\n\t\t\t\r\n\t\t\tsuper.destroy();\r\n\t\t}\r\n\r\n\t}\r\n}<\/pre>\n<p>ALevel:<\/p>\n<pre lang=\"actionscript\" line=\"1\">package {\r\n\r\n\timport Box2DAS.Dynamics.ContactEvent;\r\n\r\n\timport starling.display.Quad;\r\n\timport starling.text.BitmapFont;\r\n\timport starling.text.TextField;\r\n\timport starling.textures.Texture;\r\n\timport starling.textures.TextureAtlas;\r\n\timport starling.utils.Color;\r\n\r\n\timport com.citrusengine.core.CitrusEngine;\r\n\timport com.citrusengine.core.StarlingState;\r\n\timport com.citrusengine.math.MathVector;\r\n\timport com.citrusengine.objects.CitrusSprite;\r\n\timport com.citrusengine.objects.platformer.Baddy;\r\n\timport com.citrusengine.objects.platformer.Hero;\r\n\timport com.citrusengine.objects.platformer.Platform;\r\n\timport com.citrusengine.objects.platformer.Sensor;\r\n\timport com.citrusengine.physics.Box2D;\r\n\timport com.citrusengine.utils.ObjectMaker;\r\n\timport com.citrusengine.view.starlingview.AnimationSequence;\r\n\r\n\timport org.osflash.signals.Signal;\r\n\r\n\timport flash.display.Bitmap;\r\n\timport flash.display.MovieClip;\r\n\timport flash.geom.Rectangle;\r\n\r\n\t\/**\r\n\t * @author Aymeric\r\n\t *\/\r\n\tpublic class ALevel extends StarlingState {\r\n\t\t\r\n\t\tpublic var lvlEnded:Signal;\r\n\t\tpublic var restartLevel:Signal;\r\n\t\t\r\n\t\tprotected var _ce:CitrusEngine;\r\n\t\tprotected var _level:MovieClip;\r\n\t\t\r\n\t\tprotected var _hero:Hero;\r\n\t\t\r\n\t\t[Embed(source=\"..\/embed\/Hero.xml\", mimeType=\"application\/octet-stream\")]\r\n\t\tprivate var _heroConfig:Class;\r\n\t\t\r\n\t\t[Embed(source=\"..\/embed\/Hero.png\")]\r\n\t\tprivate var _heroPng:Class;\r\n\t\t\r\n\t\t[Embed(source=\"..\/embed\/ArialFont.fnt\", mimeType=\"application\/octet-stream\")]\r\n\t\tprivate var _fontConfig:Class;\r\n\t\t\r\n\t\t[Embed(source=\"..\/embed\/ArialFont.png\")]\r\n\t\tprivate var _fontPng:Class;\r\n\t\t\r\n\t\tprotected var _maskDuringLoading:Quad;\r\n\t\tprotected var _percentTF:TextField;\r\n\r\n\t\tpublic function ALevel(level:MovieClip = null) {\r\n\t\t\t\r\n\t\t\tsuper();\r\n\t\t\t\r\n\t\t\t_ce = CitrusEngine.getInstance();\r\n\t\t\t\r\n\t\t\t_level = level;\r\n\t\t\t\r\n\t\t\tlvlEnded = new Signal();\r\n\t\t\trestartLevel = new Signal();\r\n\t\t\t\r\n\t\t\t\/\/ Useful for not forgetting to import object from the Level Editor\r\n\t\t\tvar objectsUsed:Array = [Hero, Platform, Baddy, Sensor, CitrusSprite];\r\n\t\t}\r\n\r\n\t\toverride public function initialize():void {\r\n\t\t\t\r\n\t\t\tsuper.initialize();\r\n\t\t\t\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\t\t\t\r\n\t\t\t\/\/ hide objects loading in the background\r\n\t\t\t_maskDuringLoading = new Quad(stage.stageWidth, stage.stageHeight);\r\n\t\t\t_maskDuringLoading.color = 0x000000;\r\n\t\t\t_maskDuringLoading.x = (stage.stageWidth - _maskDuringLoading.width) \/ 2;\r\n\t\t\t_maskDuringLoading.y = (stage.stageHeight - _maskDuringLoading.height) \/ 2;\r\n\t\t\taddChild(_maskDuringLoading);\r\n\t\t\t\r\n\t\t\t\/\/ create a textfield to show the loading %\r\n\t\t\tvar bitmap:Bitmap = new _fontPng();\r\n\t\t\tvar ftTexture:Texture = Texture.fromBitmap(bitmap);\r\n\t\t\tvar ftXML:XML = XML(new _fontConfig());\r\n\t\t\tTextField.registerBitmapFont(new BitmapFont(ftTexture, ftXML));\r\n\t\t\t\r\n\t\t\t_percentTF = new TextField(400, 200, \"\", \"ArialMT\");\r\n\t\t\t_percentTF.fontSize = BitmapFont.NATIVE_SIZE;\r\n\t\t\t_percentTF.color = Color.WHITE;\r\n\t\t\t_percentTF.autoScale = true;\r\n\t\t\t_percentTF.x = (stage.stageWidth - _percentTF.width) \/ 2;\r\n\t\t\t_percentTF.y = (stage.stageHeight - _percentTF.height) \/ 2;\r\n\t\t\t\r\n\t\t\taddChild(_percentTF);\r\n\t\t\t\r\n\t\t\t\/\/ when the loading is completed...\r\n\t\t\tview.loadManager.onLoadComplete.addOnce(_handleLoadComplete);\r\n\t\t\t\r\n\t\t\t\/\/ create objects from our level made with Flash Pro\r\n\t\t\tObjectMaker.FromMovieClip(_level);\r\n\t\t\t\r\n\t\t\t\/\/ the hero view come from a sprite sheet, for the baddy that was a swf\r\n\t\t\tbitmap = new _heroPng();\r\n\t\t\tvar texture:Texture = Texture.fromBitmap(bitmap);\r\n\t\t\tvar xml:XML = XML(new _heroConfig());\r\n\t\t\tvar sTextureAtlas:TextureAtlas = new TextureAtlas(texture, xml);\r\n\t\t\t\r\n\t\t\t_hero = Hero(getFirstObjectByType(Hero));\r\n\t\t\t_hero.view = new AnimationSequence(sTextureAtlas, [\"walk\", \"duck\", \"idle\", \"jump\", \"hurt\"], \"idle\");\r\n\t\t\t_hero.hurtDuration = 500;\r\n\r\n\t\t\tview.setupCamera(_hero, new MathVector(320, 240), new Rectangle(0, 0, 1550, 450), new MathVector(.25, .05));\r\n\t\t}\r\n\t\t\r\n\t\tprotected function _changeLevel(cEvt:ContactEvent):void {\r\n\t\t\t\r\n\t\t\tif (cEvt.other.GetBody().GetUserData() is Hero) {\r\n\t\t\t\tlvlEnded.dispatch();\r\n\t\t\t}\r\n\t\t}\r\n\t\t\r\n\t\tprotected function _handleLoadComplete():void {\r\n\t\t\t\r\n\t\t\tremoveChild(_percentTF);\r\n\t\t\tremoveChild(_maskDuringLoading);\r\n\t\t}\r\n\t\t\t\r\n\t\toverride public function update(timeDelta:Number):void {\r\n\t\t\t\r\n\t\t\tsuper.update(timeDelta);\r\n\t\t\t\r\n\t\t\tvar percent:uint = view.loadManager.bytesLoaded \/ view.loadManager.bytesTotal * 100;\r\n\r\n\t\t\tif (percent < 99) {\r\n\t\t\t\t_percentTF.text = percent.toString() + \"%\";\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\toverride public function destroy():void {\r\n\t\t\t\r\n\t\t\tTextField.unregisterBitmapFont(\"ArialMT\");\r\n\t\t\t\r\n\t\t\tsuper.destroy();\r\n\t\t}\r\n\t}\r\n}<\/pre>\n<p>Level1 :<\/p>\n<pre lang=\"actionscript\" line=\"1\">package {\r\n\r\n\timport Box2DAS.Dynamics.ContactEvent;\r\n\r\n\timport starling.core.Starling;\r\n\timport starling.extensions.particles.ParticleDesignerPS;\r\n\timport starling.extensions.particles.ParticleSystem;\r\n\timport starling.text.BitmapFont;\r\n\timport starling.text.TextField;\r\n\timport starling.textures.Texture;\r\n\timport starling.utils.Color;\r\n\r\n\timport com.citrusengine.objects.platformer.Hero;\r\n\timport com.citrusengine.objects.platformer.Sensor;\r\n\r\n\timport flash.display.MovieClip;\r\n\r\n\t\/**\r\n\t * @author Aymeric\r\n\t *\/\r\n\tpublic class Level1 extends ALevel {\r\n\t\t\r\n\t\t[Embed(source=\"..\/embed\/Particle.pex\", mimeType=\"application\/octet-stream\")]\r\n\t\tprivate var _particleConfig:Class;\r\n\r\n\t\t[Embed(source=\"..\/embed\/ParticleTexture.png\")]\r\n\t\tprivate var _particlePng:Class;\r\n\t\t\r\n\t\tprivate var _particleSystem:ParticleSystem;\r\n\t\t\r\n\t\tprivate var _bmpFontTF:TextField;\r\n\r\n\t\tpublic function Level1(level:MovieClip = null) {\r\n\t\t\tsuper(level);\r\n\t\t}\r\n\r\n\t\toverride public function initialize():void {\r\n\t\t\t\r\n\t\t\tsuper.initialize();\r\n\t\t\t\r\n\t\t\tvar psconfig:XML = new XML(new _particleConfig());\r\n\t\t\tvar psTexture:Texture = Texture.fromBitmap(new _particlePng());\r\n\r\n\t\t\t_particleSystem = new ParticleDesignerPS(psconfig, psTexture);\r\n\t\t\t_particleSystem.start();\r\n\t\t\tStarling.juggler.add(_particleSystem);\r\n\t\t\t\r\n\t\t\tvar endLevel:Sensor = Sensor(getObjectByName(\"endLevel\"));\r\n\t\t\tendLevel.view = _particleSystem;\r\n\t\t\t\r\n\t\t\t_bmpFontTF = new TextField(400, 200, \"The Citrus Engine goes on Stage3D thanks to Starling\", \"ArialMT\");\r\n\t\t\t_bmpFontTF.fontSize = BitmapFont.NATIVE_SIZE;\r\n\t\t\t_bmpFontTF.color = Color.WHITE;\r\n\t\t\t_bmpFontTF.autoScale = true;\r\n\t\t\t_bmpFontTF.x = (stage.stageWidth - _bmpFontTF.width) \/ 2;\r\n\t\t\t_bmpFontTF.y = (stage.stageHeight - _bmpFontTF.height) \/ 2;\r\n\t\t\t\r\n\t\t\taddChild(_bmpFontTF);\r\n\t\t\t_bmpFontTF.visible = false;\r\n\t\t\t\r\n\t\t\tvar popUp:Sensor = Sensor(getObjectByName(\"popUp\"));\r\n\t\t\t\r\n\t\t\tendLevel.onBeginContact.add(_changeLevel);\r\n\t\t\t\r\n\t\t\tpopUp.onBeginContact.add(_showPopUp);\r\n\t\t\tpopUp.onEndContact.add(_hidePopUp);\r\n\t\t}\r\n\t\t\r\n\t\tprivate function _showPopUp(cEvt:ContactEvent):void {\r\n\t\t\t\r\n\t\t\tif (cEvt.other.GetBody().GetUserData() is Hero) {\r\n\t\t\t\t_bmpFontTF.visible = true;\r\n\t\t\t}\r\n\t\t}\r\n\t\t\r\n\t\tprivate function _hidePopUp(cEvt:ContactEvent):void {\r\n\t\t\t\r\n\t\t\tif (cEvt.other.GetBody().GetUserData() is Hero) {\r\n\t\t\t\t_bmpFontTF.visible = false;\r\n\t\t\t}\r\n\t\t}\r\n\t\t\r\n\t\toverride public function destroy():void {\r\n\r\n\t\t\tStarling.juggler.remove(_particleSystem);\r\n\t\t\t_particleSystem.stop();\r\n\t\t\t_particleSystem.dispose();\r\n\t\t\t\r\n\t\t\tremoveChild(_bmpFontTF);\r\n\r\n\t\t\tsuper.destroy();\r\n\t\t}\r\n\r\n\t}\r\n}<\/pre>\n<p>Level2 :<\/p>\n<pre lang=\"actionscript\" line=\"1\">package {\r\n\r\n\timport starling.text.BitmapFont;\r\n\timport starling.text.TextField;\r\n\timport starling.utils.Color;\r\n\r\n\timport flash.display.MovieClip;\r\n\timport flash.events.TimerEvent;\r\n\timport flash.utils.Timer;\r\n\r\n\t\/**\r\n\t * @author Aymeric\r\n\t *\/\r\n\tpublic class Level2 extends ALevel {\r\n\t\t\r\n\t\tprivate var _timer:Timer;\r\n\t\t\r\n\t\tprivate var _bmpFontTF:TextField;\r\n\r\n\t\tpublic function Level2(level:MovieClip = null) {\r\n\t\t\t\r\n\t\t\tsuper(level);\r\n\t\t}\r\n\t\t\r\n\t\toverride public function initialize():void {\r\n\t\t\t\r\n\t\t\tsuper.initialize();\r\n\t\t\t\r\n\t\t\t_timer = new Timer(3000);\r\n\t\t\t_timer.addEventListener(TimerEvent.TIMER, _onTick);\r\n\t\t\t\r\n\t\t\t_bmpFontTF = new TextField(400, 200, \"This is a performance test level. Box2D physics become some time unstable. You can see box2d bodies thanks to the console.\", \"ArialMT\");\r\n\t\t\t_bmpFontTF.fontSize = BitmapFont.NATIVE_SIZE;\r\n\t\t\t_bmpFontTF.color = Color.WHITE;\r\n\t\t\t_bmpFontTF.autoScale = true;\r\n\t\t\t_bmpFontTF.x = (stage.stageWidth - _bmpFontTF.width) \/ 2;\r\n\t\t\t_bmpFontTF.y = (stage.stageHeight - _bmpFontTF.height) \/ 2;\r\n\t\t}\r\n\t\t\t\r\n\t\toverride protected function _handleLoadComplete():void {\r\n\t\t\t\r\n\t\t\tsuper._handleLoadComplete();\r\n\t\t\t\r\n\t\t\taddChild(_bmpFontTF);\r\n\t\t\t_timer.start();\r\n\t\t}\r\n\r\n\t\tprivate function _onTick(tEvt:TimerEvent):void {\r\n\t\t\t\r\n\t\t\tif (_timer.currentCount == 2)\r\n\t\t\t\tremoveChild(_bmpFontTF);\r\n\t\t\t\r\n\t\t\t\/\/ PhysicsEditorObjects class is created by the software PhysicsEditor and its additional CitrusEngine template.\r\n\t\t\t\/\/ Muffins are not in front of everything due to the foreground group param set to 1 in the Level Editor, default is 0.\r\n\t\t\tvar muffin:PhysicsEditorObjects = new PhysicsEditorObjects(\"muffin\", {peObject:\"muffin\", view:\"muffin.png\", registration:\"topLeft\", x:Math.random() * view.cameraBounds.width});\r\n\t\t\tadd(muffin);\r\n\t\t}\r\n\r\n\t\toverride public function destroy():void {\r\n\t\t\t\r\n\t\t\t_timer.removeEventListener(TimerEvent.TIMER, _onTick);\r\n\t\t\t_timer.stop();\r\n\t\t\t_timer = null;\r\n\t\t\t\r\n\t\t\tsuper.destroy();\r\n\t\t}\r\n\t}\r\n}<\/pre>\n<p>And finally the PhysicsEditorObjects :<\/p>\n<pre lang=\"actionscript\" line=\"1\">package {\r\n\r\n\timport Box2DAS.Collision.Shapes.b2PolygonShape;\r\n\timport Box2DAS.Common.V2;\r\n\r\n\timport com.citrusengine.objects.platformer.Crate;\r\n\r\n\t\/**\r\n\t * @author Aymeric\r\n\t * <p>This is a class created by the software http:\/\/www.physicseditor.de\/<\/p>\r\n\t * <p>Just select the CitrusEngine template, upload your png picture, set polygons and export.<\/p>\r\n\t * <p>Be careful, the registration point is topLeft !<\/p>\r\n\t * @param peObject : the name of the png file\r\n\t *\/\r\n    public class PhysicsEditorObjects extends Crate {\r\n\t\t\r\n\t\t[Inspectable(defaultValue=\"\")]\r\n\t\tpublic var peObject:String = \"\";\r\n\r\n\t\tprivate var _tab:Array;\r\n\r\n\t\tpublic function PhysicsEditorObjects(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 destroy():void {\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\t\t}\r\n\r\n\t\toverride protected function defineFixture():void {\r\n\t\t\t\r\n\t\t\tsuper.defineFixture();\r\n\t\t\t\r\n\t\t\t_createVertices();\r\n\r\n\t\t\t_fixtureDef.density = _getDensity();\r\n\t\t\t_fixtureDef.friction = _getFriction();\r\n\t\t\t_fixtureDef.restitution = _getRestitution();\r\n\t\t\t\r\n\t\t\tfor (var i:uint = 0; i < _tab.length; ++i) {\r\n\t\t\t\tvar polygonShape:b2PolygonShape = new b2PolygonShape();\r\n\t\t\t\tpolygonShape.Set(_tab[i]);\r\n\t\t\t\t_fixtureDef.shape = polygonShape;\r\n\r\n\t\t\t\tbody.CreateFixture(_fixtureDef);\r\n\t\t\t}\r\n\t\t}\r\n\t\t\r\n        protected function _createVertices():void {\r\n\t\t\t\r\n\t\t\t_tab = [];\r\n\t\t\tvar vertices:Vector.<V2> = new Vector.<V2>();\r\n\r\n\t\t\tswitch (peObject) {\r\n\t\t\t\t\r\n\t\t\t\tcase \"muffin\":\r\n\t\t\t\t\t\t\t\t\t\t\t\r\n\t\t\t        vertices.push(new V2(-0.5\/_box2D.scale, 81.5\/_box2D.scale));\r\n\t\t\t\t\tvertices.push(new V2(10.5\/_box2D.scale, 59.5\/_box2D.scale));\r\n\t\t\t\t\tvertices.push(new V2(46.5\/_box2D.scale, 27.5\/_box2D.scale));\r\n\t\t\t\t\tvertices.push(new V2(50.5\/_box2D.scale, 27.5\/_box2D.scale));\r\n\t\t\t\t\tvertices.push(new V2(92.5\/_box2D.scale, 61.5\/_box2D.scale));\r\n\t\t\t\t\tvertices.push(new V2(99.5\/_box2D.scale, 79.5\/_box2D.scale));\r\n\t\t\t\t\tvertices.push(new V2(59.5\/_box2D.scale, 141.5\/_box2D.scale));\r\n\t\t\t\t\tvertices.push(new V2(17.5\/_box2D.scale, 133.5\/_box2D.scale));\r\n\t\t\t\t\t\r\n\t\t\t\t\t_tab.push(vertices);\r\n\t\t\t\t\tvertices = new Vector.<V2>();\r\n\t\t\t\t\t\t\t\t\t\t\t\r\n\t\t\t        vertices.push(new V2(59.5\/_box2D.scale, 141.5\/_box2D.scale));\r\n\t\t\t\t\tvertices.push(new V2(99.5\/_box2D.scale, 79.5\/_box2D.scale));\r\n\t\t\t\t\tvertices.push(new V2(83.5\/_box2D.scale, 133.5\/_box2D.scale));\r\n\t\t\t\t\t\r\n\t\t\t\t\t_tab.push(vertices);\r\n\t\t\t\t\tvertices = new Vector.<V2>();\r\n\t\t\t\t\t\t\t\t\t\t\t\r\n\t\t\t        vertices.push(new V2(50.5\/_box2D.scale, 27.5\/_box2D.scale));\r\n\t\t\t\t\tvertices.push(new V2(46.5\/_box2D.scale, 27.5\/_box2D.scale));\r\n\t\t\t\t\tvertices.push(new V2(42.5\/_box2D.scale, -0.5\/_box2D.scale));\r\n\t\t\t\t\t\r\n\t\t\t\t\t_tab.push(vertices);\r\n\t\t\t\t\t\r\n\t\t\t\t\tbreak;\r\n\t\t\t\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tprotected function _getDensity():Number {\r\n\r\n\t\t\tswitch (peObject) {\r\n\t\t\t\t\r\n\t\t\t\tcase \"muffin\":\r\n\t\t\t\t\treturn 1;\r\n\t\t\t\t\tbreak;\r\n\t\t\t\r\n\t\t\t}\r\n\r\n\t\t\treturn 1;\r\n\t\t}\r\n\t\t\r\n\t\tprotected function _getFriction():Number {\r\n\t\t\t\r\n\t\t\tswitch (peObject) {\r\n\t\t\t\t\r\n\t\t\t\tcase \"muffin\":\r\n\t\t\t\t\treturn 0.6;\r\n\t\t\t\t\tbreak;\r\n\t\t\t\r\n\t\t\t}\r\n\r\n\t\t\treturn 0.6;\r\n\t\t}\r\n\t\t\r\n\t\tprotected function _getRestitution():Number {\r\n\t\t\t\r\n\t\t\tswitch (peObject) {\r\n\t\t\t\t\r\n\t\t\t\tcase \"muffin\":\r\n\t\t\t\t\treturn 0.3;\r\n\t\t\t\t\tbreak;\r\n\t\t\t\r\n\t\t\t}\r\n\r\n\t\t\treturn 0.3;\r\n\t\t}\r\n\t}\r\n}<\/pre>\n<p>This is it! Again, everything is available on the CE's google code. But hey, this is the CitrusEngine V3 BETA 1. What is next !?<\/p>\n<p><strong>LOOKING FOR CONTRIBUTOR<\/strong><br \/>\nThe CE is currently looking for some new contributors for more amazing features! If you're an advanced game developer, or a simple student (like me), you can contribute!<\/p>\n<p><strong>What about the Inspectable metadata tag?<\/strong><br \/>\nI would like to add it, but now with Starling support it means that Flash Pro must be able to target FP11 that's a bit complicated. So not at the moment unless you ask for it!<\/p>\n<p><strong>LEVEL ARCHITECT<\/strong><br \/>\nThe Level Architect is a great tool but not quite reached. It needs always some work. Personnaly I prefer using Flash Pro, some people don't. That would be cool if someone would contribute to it.<\/p>\n<p><strong>MOBILE<\/strong><br \/>\nI'll be honest : if you want to create a mobile game and you have more than 5 dynamics objects, forget the CitrusEngine for the moment. Box2D is a performance killer on mobile with Flash. However Eric started a simpler class for collision management : <a target=\"_blank\" href=\"http:\/\/code.google.com\/p\/citrus-engine\/source\/browse\/trunk\/com\/citrusengine\/physics\/simple\/CitrusSolver.as\">CitrusSolver<\/a>. It is well advanced, just waiting for some more work.<\/p>\n<p><strong>ENTITY\/COMPONENT SYSTEM<\/strong><br \/>\nI haven't include my ladder management in the Hero class. Because the Hero class would become more & more complex... what if it uses a sword, a gun, a rope... ? Extending it is not enough. <a target=\"_blank\" href=\"http:\/\/www.aymericlamboley.fr\/blog\/doubly-linked-list-and-object-pooling\/\">In a previous post<\/a>, I've explained why we need a simple entity system. Richard Lord has made an amazing blog post : <a href=\"http:\/\/www.richardlord.net\/blog\/what-is-an-entity-framework\"><em>What is an entity framework for game development?<\/em><\/a>. Now I've fully understand how it works. But mixing it with box2d, frame animation, input management doesn't sound easy...<\/p>\n<p><strong>CONCLUSION<\/strong><br \/>\nIf you have read everything, that's awesome! Feel free to comment \/ request features for the CitrusEngine, and if you want to be a contributor you're welcome !<br \/>\nFor the next months, I will continue to be active for the CE's community, always playing with Flash AS3, learning iOS, learning haXe nme, and work hard on my 2nd year school project. And finally I'm looking for job, beginning in July or later.<\/p>\n<p>Oh and I will be at the World Wide haXe conf on Friday 13 - Saturday 14 April, at Paris. Hope to meet some of you there \ud83d\ude42<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Hey folks, happy new year! My previous post go back from 1st december, I&#8217;ve been really busy : adding Starling in the CitrusEngine, learning iOS and working on my 2nd year school project. And obviously some great holidays between christmas &amp; the new year. Today I&#8217;m very pleased to share with you the Citrus Engine &hellip; <a href=\"http:\/\/www.aymericlamboley.fr\/blog\/citrusengine-goes-stage3d-with-starling\/\" class=\"more-link\">Continue reading <span class=\"screen-reader-text\">CitrusEngine goes Stage3D with Starling<\/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,33,11,6,90],"tags":[15,50,34,26,74,91],"_links":{"self":[{"href":"http:\/\/www.aymericlamboley.fr\/blog\/wp-json\/wp\/v2\/posts\/530"}],"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=530"}],"version-history":[{"count":18,"href":"http:\/\/www.aymericlamboley.fr\/blog\/wp-json\/wp\/v2\/posts\/530\/revisions"}],"predecessor-version":[{"id":1282,"href":"http:\/\/www.aymericlamboley.fr\/blog\/wp-json\/wp\/v2\/posts\/530\/revisions\/1282"}],"wp:attachment":[{"href":"http:\/\/www.aymericlamboley.fr\/blog\/wp-json\/wp\/v2\/media?parent=530"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/www.aymericlamboley.fr\/blog\/wp-json\/wp\/v2\/categories?post=530"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/www.aymericlamboley.fr\/blog\/wp-json\/wp\/v2\/tags?post=530"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}