V3.1.7 features reworked and cool additions!

Hey guys, we’re proud to share this new build!

Sometimes it is good to see what we already have and improve it. No worries, we have added some good new features: Spine support, a vehicle example!! and real state transitions. And we have also improved three main features: the camera system, the sound manager and object pooling!

    Changelog

  • Updated on Feathers 1.1.0.
  • Updated on Nape 2.0.9.
  • Added Spine 2D skeleton library support.
  • Added DragonBones support for the display list.
  • SoundManager reworked with a CitrusSound class and CitrusSoundGroup.
  • PoolObject reworked.
  • Camera reworked.
  • Added a vehicle package running with Nape composed with a chassis, a driver, two wheels and some nuggets!
  • Added support for pure state transition (having two state at the same time) using futureState.
  • Starling.handleLostContext is defined to true if you use Android, made in setUpStarling function.
  • if StarlingArt updateArtEnabled is set to false, it will flatten the Sprite.
  • StarlingArt may handle an uint color, it will automatically create a quad.
  • AnimationSequence textureAtlas could be an AssetManager object.
  • You may add a MovieClip to an AnimationSequence.
  • added stopAllPlayingSounds(…except) method.
  • added removeAllSounds(…except) method.
  • removeSound has a new argument : stopSoundIfPlaying:Boolean = false.
  • Emitters have their updateCallEnabled = true;
  • SoundManager fix: stream sound directly after load(); when sound was added as an url.
  • fixed: stopSound wasn’t setting the playing var to false.
  • fixed: InputComponent wasn’t setting isDoingLeft.
  • fixed: updateCombinedGroundAngle() on box2d Hero
  • throwing an error if the Main class doesn’t extends StarlingCitrusEngine or didn’t call setUpStarling

The vehicle package
For some people it was complicated to combine physics object into the Citrus Engine. There were a bit lost and weren’t sure how to do that. We apologize, there wasn’t a very good example. Now we have a vehicle class built with Nape! We invite you to take a look on its source code and its example to know how it works! We would love to make a beautiful demo, but we need some assets. This is also a cool way to contribute to the Citrus Engine 😉

Real state transitions
Before this update, it was complicated to make a transition between two states. For example finishing a level and make a nice transition to the next level. We weren’t able to have two states running at the same time, we had to add effects directly from the main or a state manager, it wasn’t really handy.
Now we may have two states running at the same time, making a transition is very easy! Take a look on this demo. There is a random transition (an alpha or one pushing the other) between the states. Note that both are running at the same time and respond to input keys!
Have a look on the source code. The engine let you free to manage it as you want. Note that it may be a bit laggy if your initialize function creates many object. You may add only few in this function, and call an other function when the transition is completed (for example create a pool object).

Object Pooling System

The new object pooling system is now completely customizable. In our previous version unfortunately, performance was drained by unnecessary object destruction (this included view and body destruction). Now we are able to pool both view (display objects) and bodies, resulting in a big performance boost! The system is extendable to pool  any kinds of objects, but in the citrus.objects package are available default objects which should fit most if not all of your needs : the NapeObjectPool and Box2DObjectPool pool bodies (as well as views) : basically we de-activate the bodies when objects are disposed so they remain in memory but are no longer simulated. display objects are invisible, and the updates are no longer called on the art or the object. We need those two kinds of pools because of the difference between nape and box2D : with box2D, you cannot simply de-activate a body in the middle of a world step, which means this is post-poned to the next frame, but you can dispose of the object at any time.
For convenience, we added a CitrusSpritePool to pool CitrusSprites, but the CitrusObjectPool is also available to pool other kinds of objects and you could pool citrus sprites easily with it if you’re a bit more experienced.
The system is highly flexible : signals are dispatched when an object is created, recycled or disposed so that you can customize what should happen in every context : This enables you to create a view (a sprite for example) only once at creation – and then simply re-use it when recycling.
With the previous version, every time you disposed of an object, this object was destroyed and so was its view, so you had to create a new one every time, and now you don’t need to. I hope that the example we provide will be easy enough for you to understand the difference and to manage well the way you dispose/recycle your objects :

We have added a fresh example project to showcase the pooling system, which hopefully is documented enough for you to understand the new features and how to use them, you can take a look at it here.

SoundManager

The new sound manager comes with “sound groups” or “volume groups” if you wish.
The idea is that a sound that is added to a group will see its base volume multiplied by such group. There are now two default groups when CE starts : the BGM music group and the SFX group, but you can create and add new groups.

the way you add a sound is also different , here’s an example :

_ce.sound.addSound("soundeffect", { sound:"assets/soundeffect.mp3", panning:0.3, triggerSoundComplete:true, timesToPlay:12 , group:CitrusSoundGroup.SFX } );

_ce.sound.addSound("bgm", { sound:new music() as Sound,volume:1,panning:-0.4, timesToPlay:-1, group:CitrusSoundGroup.BGM } );

_ce.sound.playSound("soundeffect");
_ce.sound.playSound("bgm");

addSound() now takes in the name of the sound, and a params object like citrus objects do. you can find the set of accepted parameters in the docs but here are the important ones : the sound params (required) which can be a class, a string url, or a sound object.
panning, volume, timesToPlay (if timesToPlay is 0 or negative, the sound will loop.), triggerSoundComplete (if set to true, SoundManager will dispatch a CitrusSoundEvent with the soundName and other info) … finally, the group parameter.
Sounds can have no groups by default. A sound with no group means its volume will only be affected by the master volume. in fact the sound Manager has now a masterVolume property which affects all sounds. To define a group in the params, you need to give it a group ID, the defaults are CitrusSoundGroup.BGM and CitrusSoundGroup.SFX.

playSound() now only needs the sounds id.
to change the groups volumes, you can do the following :

_ce.sound.getGroup(CitrusSoundGroup.BGM).volume = 1;
_ce.sound.getGroup(CitrusSoundGroup.SFX).volume = 0.3;

and the change will automatically be applied on sounds that are in those groups.
you will notice that getGroup() returns a group object from which you can get all CitrusSounds that are in that group.

you can play, pause, resume, stop, sounds from the sound manager

_ce.sound.pauseSound("bgm");

or directly from the citrus sounds objects which you can get either from a group object

_ce.sound.getGroup(CitrusSoundGroup.SFX).getSound("bgm").pause();

or the sound manager itself :

_ce.sound.getSound("bgm").pause();

(it is safer to do everything from the sound manager.)

You can move sounds in and out of groups (as you would plug/unplug jacks from a mixer into different inputs) using the following :

_ce.sound.moveSoundToGroup("bgm",null);

“bgm” will now have no group… its volume will be its base volume, multiplied by the master volume.

_ce.sound.moveSoundToGroup("bgm",CitrusSoundGroup.SFX);

“bgm” is now in the SFX group, you know what that means for its final volume by now 🙂

There are the valuable master controls :

_ce.sound.masterMute = true;
_ce.sound.masterVolume = 0.8;

Note that  the ‘base’ panning of a sound (that you can later modify like this  _ce.sound.getSound(“bgm”).panning = 0;) is always going to be its final panning. groups are only for volumes.

There are actually a bit more utility functions in there, you don’t actually have to work with CitrusSound if you don’t want to as pretty much everything can be done through _ce.sound … we kept crossfade and tweenVolume (and we are no longer creating a lot of SoundTransform objects which should increase performance)

I almost forgot :
you can now pauseAll() sounds and resumeAll(), this sounded like something very important for the user experience, as much as volume control actually.

Camera
The camera has seen some problems over the month, specially with parallax, and rotation and so on. I’m afraid not all is fixed as there is a clear discussion on what should parallax actually represent, and because of that, how it should “move the objects around”.
The current camera should not differ much from the old one in terms of what you see.
Instead of MathVector, we are using Points.
There are two new camera properties  : boundsMode and parallaxMode.
all of these modes constant values can be found in ACitrusCamera :

_camera.parallaxMode = ACitrusCamera.PARALLAX_MODE_DEPTH;
_camera.boundsMode= ACitrusCamera.BOUNDS_MODE_OFFSET;

the parallaxMode will define how an object’s final position will be displaced by the parallax value it has.
0 means the object is so far away that it doesn’t move at all and stays on screen if its original position allows it to be on screen.

The PARALLAX_MODE_TOPLEFT, which is the default parallaxMode, should reproduce what the older releases did with parallax. this means parallaxed objects will be displaced by the distance to the “top left of the state” of the “top left corner of the camera”. its hard to explain, but its actually what was going on until now.
The PARALLAX_MODE_DEPTH, after some discussion, sounded like the more logical approach, as an object is now displaced from the “center” of the camera : if your camera looks straight at a parallaxed object, this object will be right at the center of the camera no matter what parallax value it has. if its slightly to the left, it will be more so , the higher the parallax – this gives use a nice, more logical, depth effect, however ‘breaks’ all games created until now with parallaxed objects. In this mode an object at 0,0 will be seen at 0,0 only if the camera looks straight at it – in the previous mode, one would set his background at 0,0 with a parallax of 0. with this mode the background is no longer appearing at this position, but at the center (offset) of the camera and needs to be re-positioned unfortunately, that’s why we agreed to leave this as an option, but just know that its there if you want cool depth effect.
Something I should add, the camera zoom function does not mean the camera moves towards the objects – as such, when zooming, with the right parallax “depth” mode, the objects will not move away from the camera as one would expect if one would actually move a camera towards objects on the z axis. Unfortunately, this actually happens with the default mode, simply because it was wrong. That doesn’t mean the depth mode is right though, if you’ve got suggestions, knowledge or needs when it comes to “parallax” or “fake depth” don’t hesitate to engage in a discussion in the forums. (you may also request other modes).

The boundsMode, is something else (of course). Now its only defining how will the camera be restricted inside the bounds if there are any. by default the mode is ACitrusCamera.BOUNDS_MODE_AABB; – an AABB rectangle is calculated for the rotated/scaled camera, and this rectangle is not allowed to move out of the bounds. this is perfect if you’re not using rotation. If you are, and are rotating the camera close to the edge, you’ll see the collisions of the AABB rectangle with the bounds -and the “collision solving” makes the camera jerk around as it tries to follow the target as best as possible when rotating. One possible solution was the BOUNDS_MODE_OFFSET which only stops the camera’s offset point to go out of the bounds – this means rotation and zoom are now completely free – all you are sure of is that the camera center will not go away from a certain zone (and you can reduce your bounds to not see too much outside of the bounds or play around with that notion). the BOUNDS_MODE_ADVANCED is a completely crazy idea which consists of “drawing the circles that goes through the corner of the camera’s rectangle that is furthest from the camera’s offset, and not let that circle go out of the bounds. This stops the ugly effects you get from the AABB rectangle colliding with the bounds rectangle because we’re now restricting a ball and the camera “rotates inside it” although the negative thing about it is, if the offset of the camera is not at the center of the camera then the circle is bigger and also you’ll never see the corners of your bounds, but that might not matter … you still have the choice of making your bounds bigger or even dynamically change them. most of the camera’s problem come from those bounds anyway.

The camera no longer has restrictZoom as a property. zoom restriction only applies when you restrict the AABB rectangle of the camera (for now) and it made no sense when you had now bounds to restrict the zoom into anyway.

you can temporarily stop your camera from following your target by turning _camera.followTarget to false which is handy in some scenarios.

zoomFit() now returns the actual scale it has calculated to fit the dimension you gave it inside the camera.
the camera also has a “baseZoom” property which is 1 by default.
if you set it to 2, all the following zoom modifications such as setZoom(0.5); will be multiplied by baseZoom and so the real zoom in this case will be 0.5×2 = 1 . This helps for relative camera effects or when you always want a certain zone of the screen to show up when the zoom is from 0 to 1 :

you can store the result of zoomFit inside baseZoom and you’ll now be sure every zoom factor you apply to the camera will be relative to that zone you gave to zoomFit – if you designed your game for a 800×600 base dimension, you can use zoomFit(800,600), the camera will auto fit that “zone” inside the screen and you’ll see your game in full, and in the same way on all screens. of course zoomFit only fits as best as it can, if the screen is wider you’ll obviously see more on the sides.  after doing that, if you do put what zoomFit returns in baseZoom, you’ll know that zoom(2) will actually zoom to see a zone of 400×300 of your game, whatever screen you’re on. this is all for convenience of working with zoom functions – and also handling your game on different screens without having to scale viewports or workarounds by scaling the state or things like this (yes the camera is useful even if you have no targets to follow !!!)

Also something that might sound good to some of you :

_camera.reset();

the reset function puts the camera at its target position, zoom and rotation straight away without easing.If you want to start your game at a zoom of 0.5 without seeing the camera zoom out from 1, you can do the following now and not get the undesired zoom out effect :

_camera.baseZoom = _camera.zoomFit(800,600);
_camera.setZoom(0.5);
_camera.reset();

The camera example project has been updated here.

that’s all !

21 thoughts on “V3.1.7 features reworked and cool additions!

  1. This looks great. I heard mention of pathfinding being added to the citrus engine this summer. Will this be the only update this summer or is pathfinding still soon to come?

    Thanks!

  2. Thank you guys for your efforts. You guys are stunning and Citrus will be shining.

    I would definitely put more emphasis on stabilising the API, improving performance and bugfixing. As soon as these are in place, huge game studios will pick up the engine as well and start working with it. If I were you (and if I may give an advice), I would focus on stabilising the whole API and improve performance. Until it’s changing every month, big studios can’t work with it, because it would involve constant refactoring for them. As soon as some popular games pop on the stores using Citrus, it will get more traction, more pull requests, more devs and you guys will find more time to work on features later. I only know this because I work at a game studio as well as a CTO 🙂 Studios need a very stable base to build upon, they will customise the engine anyways.

    Thank you.

    • Hi Peter, thanks for the kind words.

      You’re right concerning the stability of the API. We try to do our best to stay compatible with previous version as far as possible, but sometimes there are important changes which must be done.

      It’s vey important for us to have feedbacks from developers to know what is wrong, what can be improved, and which features are missing. We have something huge in the pipeline coming from a developer concerning AI.

      In our personal day life, we are freelancers, so it means that we’re available to work with several companies: help them to get started, implement some new features, explaining deeply the engine architecture, etc.

      The showcase shows some good products made by different studios. When the Citrus Engine had a prices, and it wasn’t cheap ($400), some companies were already building games with it. Now this V3 is much more advanced, free and open source. Companies shouldn’t wait to give it a serious try!

  3. we miss your update , it looks great with all these features , it will be HOT this summer ! i like the work done on Camera it need more interest , thanks Aymeric ,and gsynuh for your great work

  4. Thanks for the update guys, I’m having some trouble with it though.
    I’m using FDT on osx and when I add any of the 3.1.7 .swc files to my class path they seem to be empty. I rolled back to 3.1.6 and it shows the contents of the swc as expected, has anyone else reported problems with the latest version?

  5. Hey Aymeric, thnx for the update, i was just wondering, how does that “add a MovieClip to an AnimationSequence” works, do you have an example of it? i think it may help me with a problem I’m having with animations.

  6. hi,
    Aymeric , it would be great if you gave us a link to a good tutorial that uses the citrus engine, but with flashdevelop, since that is free. I get confused because all citrus engine tutorials are using flash builder which seems much different than flash develop
    Thanks bro.

    • Hey, unfortunately I don’t use this IDE (I’m on Mac). I’m pretty sure you will find some tutorials concerning FD. Using the Citrus Engine with it shouldn’t be a problem!

  7. How about preLoadSounds function? I used it to check when all sounds are downloaded and then run the game. Now there is no such functionality. How to explain it?

  8. Howdy,

    This is a fantastic engine. It’s very flexible and eloquent. You all practice good OOP standards and conventions. I’m using Citrus to teach Flash game development to my college students in San Francisco. They like it a lot. Do you have a working example/demo for the Vehicle package? I see the example of the class, but do you have a swf up?

    Thanks for the great work!

Leave a Reply

Your email address will not be published. Required fields are marked *