Hi folks! Since my studies are over and my new portfolio online, I have time to focus on personal projects. Yep, it was time to contribute again to the Citrus Engine. I’ve worked 3 days at full time focusing on its big issue : mobile performances. I’m glad to say that now they are just an old memories!
6 months ago, I’ve made the CE compatible with Stage3D thanks to Starling and added some cool stuff. CitrusEngineV3 BETA 1 has been downloaded 3047 in 6 months, that’s not bad! However it didn’t see lots of Stage3D game, because it was missing the point : people wants to make mobile games.
You will find all the sources at the end.
Nape
Nape is Luca Deltodesco‘s open-source physics engine written in haXe with the help of his preprocessor caXe. It is written with AVM2 in mind for performance, and utilising another part of his build chain provides a seamless API between AS3 and haXe though it does compile with hxcpp with other targets unknown. The idea behind Nape is to provide a high-performance, powerful and moreover friendly and safe physics engine.
Nape is faster than Box2D, and easier to handle. Adding it in the CE wasn’t hard, and we use it the same way than Box2D. Using Box2D (Alchemy version) & Stage3D with Starling in the CE you must take a look on Adobe Flash Player Premium announcement if you are targetting Browser games. With Nape, you are no more related to the “premium features license” from Adobe.
A NapeStarlingGameState Class exemple :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 | package { import com.citrusengine.core.StarlingState; import com.citrusengine.objects.NapePhysicsObject; import com.citrusengine.objects.platformer.nape.Platform; import com.citrusengine.physics.Nape; import starling.display.Image; import starling.events.Touch; import starling.events.TouchEvent; import starling.events.TouchPhase; import starling.textures.Texture; /** * ... * @author Aymeric */ public class NapeStarlingGameState extends StarlingState { [Embed(source="../bin/small_crate.png")] private var _cratePng:Class; public function NapeStarlingGameState() { super(); } override public function initialize():void { super.initialize(); var nape:Nape = new Nape("nape"); //nape.visible = true; -> to see the debug view! add(nape); add(new Platform("borderBottom", { x:0, y:stage.stageHeight - 10, width:stage.stageWidth, height:10 } )); add(new Platform("borderLeft", { x:0, y:0, width:10, height:stage.stageHeight } )); add(new Platform("borderRight", { x:stage.stageWidth - 10, y:0, width:10, height:stage.stageHeight } )); stage.addEventListener(TouchEvent.TOUCH, _addObject); } private function _addObject(tEvt:TouchEvent):void { var touch:Touch = tEvt.getTouch(stage, TouchPhase.BEGAN); if (touch) { var image:Image = new Image(Texture.fromBitmap(new _cratePng())); var physicObject:NapePhysicsObject = new NapePhysicsObject("physicobject", { x:touch.getLocation(this).x, y:touch.getLocation(this).y, width:35, height:38, view:image} ); add(physicObject); } } } } |
There wouldn’t be any CE’s API difference if you used Box2D (except for package/class names).
Bad mobile performances’ end
So Nape was amazing powerful in the CE? Yes and no. The performance was better than Box2D, but not as good as I’d like. Troubled, I started thinking there was a bad “architecture” in the Citrus Engine… and there was one. The solution came from my Haxe NME port, or more precisely a simple Box2D’s Haxe NME project on my desktop. This one had better performance than my port, on very simple test. The Box2D’s debug view changed object’s color after some time ; not on mine! Sleeping object… something prevented objects to sleep. After some investigations, it was the way how gravity was settled : no gravity in World/Space but one applied on each dynamic objects :
/** * You should override this method to extend the functionality of your physics object. This is where you will * want to do any velocity/force logic. By default, this method also updates the gravitatonal effect on the object. * I have chosen to implement gravity in each individual object instead of globally via Box2D so that it is easy * to create objects that defy gravity (like birds or bullets). This is difficult to do naturally in Box2D. Instead, * you can simply set your PhysicsObject's gravity property to 0, and baddabing: no gravity. */ override public function update(timeDelta:Number):void { if (_bodyDef.type == b2Body.b2_dynamicBody) { var velocity:V2 = _body.GetLinearVelocity(); velocity.y += gravity; _body.SetLinearVelocity(velocity); } } |
Easier to manage different objects’ gravity, but no sleeping objects. Now the gravity is set in the World/Space creation and this update function is empty. That was it!
Performance tests
Ok now everything is correct, it’s time to give some numbers! Don’t hesitate to give yours! The tests are based on the code above and use an iPhone 4S.
Citrus Engine with Starling & Box2D : 50 dynamic objects = 50FPS.
CitruxEngine with Box2D : 67 dynamic objects = 50FPS.
Citrus Engine with Starling & Nape : 80 dynamic objects = 50FPS.
I’ve not implemented Nape on the CitruxEngine yet.
Note that I was compiling for 60FPS, targetting 50 FPS just to be sure that performance decrease. Important, the 50FPS is stable when objects are sleeping. Since they are all in contact each other, the framerate drop fast.
Physics engines cohabitation
The Citrus Engine is built with a “platformer” starter-kit using Box2D, which you can use to easily make awesome 2D sidescrolling games. I’ve started to port some platformer objects on Nape. That would be really stupid to delete Box2D support since Nape is more powerful. Remember the Citrus Engine with Box2D is performing very well on Desktop & Browser games. So it’s time for a cohabitation! There is a (little) file size difference using 2 physics engines instead of one (250 Ko for a SWF, 700 Ko for an IPA), but primarily I didn’t detect a performance drop! For the final release, you may remove the other engine stuff, it takes less than 2 minutes.
At the moment, Nape class name have “Nape” in front of them or are in a Nape package (platformer objects). I will do the same with Box2D objects.
Future
I will port some Box2D platformer objects into Nape, and later offer the Citrus Engine V3 BETA 2. The idea is to have the same behavior with both physics engines. Community help is always greatly appreciated 😉
The CitruxEngine will drop Box2D support to use only Nape since I had an endless bug with it. Remember, this Haxe NME port is not ready for production but don’t be shy, get involved 😀
An optional entity/component system is always something jumping in my head, I hope to be able to offer one soon.
Sources & demo
Citrus Engine with Nape & Starling ; Browser live demo
Citrus Engine with Box2D & Starling ; Browser live demo
CitruxEngine with Box2D ; Browser live demo
Grab the last Citrus Engine update on its Google Code!
Well done dude !
Seems like center of mass has set incorrect.
Yes it seems to be, I’m trying to solve this!
Nice writeup! I actually know what’s going on. I have seen this with my own nape-wrapper before. Check line 112 in com.citrusengine.objects.NapePhysicsObject. Note that Polygon.rect(..) works in body-space, so 0,0 is the actual center of mass.
Polygon.rect(-_width*0.5, -_height*0.5, _width*0.5, _height*0.5, _material) or using Polygon.box(..) should fix your objects. 🙂
Hey, yes that was it, thank you! It is fix now!
I’m glad I could help!
What kind of FPS are you getting on latest AIR release for mobile?
Hey Paul, I’ve the same result with Air 3.1 & Air 3.3
Well done, Aymeric. Any date estimate as to when CE version 3 will be ready?
Thanks David! There will be a new beta next week with lots of new features! The Citrus Engine V3 is already stable, there will be just some refactoring which will cause problems with previous version otherwise it’s 100% retro-compatible, so don’t hesitate to start a project 😉
Best Regards,
Aymeric
Thanks Aymeric, you talented and generous man! 🙂 My salute to you for taking up the role of contributor to CE.
I knew it would be difficult for Eric to maintain the project alone, seeing that he’s currently busy with many other things.
Citrus is an awesome engine, but it needs to be continually improved and supported to reflect the latest, best practices in AS3. And that’s exactly what you are doing. So well done, man!
I was thinking of contributing to CE a while ago, but now I think my AS3 skills have gone a bit rusty after a nearly two years of not using it. I wanted to add a Weapons and Projectile classes… i.e. give the hero ability to pickup weapons and be able to fire projectiles… that would have been a very useful feature to add to CE as an out-of-the-box 2D platformer.
And then I became a web developer 🙂
Maybe in the very near future… when I get back into game development again.
Thanks for your effort in maintaining CE. I’m not a user yet, but I do appreciate your giving of time in contributing to this highly potential work! 🙂 Thank you!
Thanks again for the nice words David!
Actually there is a Missile class which can be use as a projectile. One year ago I gave the ability for the to use a sword. But the Hero class became more and more complex… Then I heard about entity/component system. My last blog post is about this system which is now a part of the Citrus Engine! I’m not 100% satisfied, but it isn’t bad at all. Many people thinks the CE is only made to make platformer game, but we can make all type of 2D games with it. I need to create small games…
Anyway if one day you want to get back in games development you’re welcome. Any contribution is very appreciated, even a small one!