V3.1.0 new input system, Nape 2, multiplayer friendly, meet Braid.

Hi guys,

Two weeks without Citrus Engine updates? What happened? No worries, we have worked hard to make this new update available adding tons of improvements, update and new features! A quick cool one : thanks to AIR3.6 and the LevelManager update you’re now able to load SWF levels made with Flash Pro on iOS!

In this update we have made one breaking change : no more “com” and “engine” package, just “citrus” now. This one has been made to remove problems with code hinting in some IDE : you had to resize the code hinting window to actually see if you were in a box2d package, or a nape package, etc. We apologize for compatibility issues with games made with the previous version (it takes some time to rename package in level editors…), that’s why we started to save old SWC releases on the GitHub.

We’re pleased to announce a new input system available! Thanks to this one you can easily change your key configuration, handle VirtualJoystick and VirtualButton for quickly making awesome mobile games. We’ve added a channel system which means that henceforth the Citrus Engine is multiplayer friendly! Also with this new input system comes a TimeShifter for time manipulation, like in Braid game. Yup, we’ve made a Braid demo with the engine (3 Mo to load, shift key for time management), also available on Android devices through an apk (working fine on Nexus 7). That’s one of the features of the TimeShifter, it allows to replay input actions too.

And finally, we’ve updated on Nape 2 (and Starling RC 1.3 without compatibility issue). We’ve worked hard on the Nape platformer pre-built objects, now you have the same feeling with box2d ones concerning : world/space gravity, NapePhysicsObject, Enemy & Hero behaviors. So everything is ready to start this new Ludum Dare!


  • Renamed package “com” and “citrusengine” into “citrus”.
  • The LevelManager can load levels made with Flash Pro on iOS using a LoaderContext.
  • The setUpStarling function may take the flash stage as an argument (useful if not the root class).
  • AnimationSequence’s dictionary is now accessible thanks to a getter.
  • Changed _input to protected to allow custom Input.
  • Added the new input package supporting keyboard, joystick, button, channel, key action…
  • Added a TimeShifter à la Braid! Allow also to replay an action.
  • Upgraded on Nape 2.0.
  • Nape’s gravity is equal to Box2D’s gravity.
  • Nape’s object physics behavior are closed to Box2d one (friction, speed, hero & enemy interaction…)
  • refreshPoolObjectArt handles the startIndex.
  • Now we can easily read the velocity of a body thanks to a getter.
  • Thanks to ObjectMaker we can define vertices using Tiled Map Editor software.
  • Update on Starling RC 1.3 + added its new AssetManager class.
  • StarlingArt is now able to dispose automatically basic DisplayObject.
  • Starling’s AnimationSequence has a clone method.
  • Starling’s AnimationSequence dispatch onAnimationComplete Signal.

The Input Package

Prior to this update , the Input class was cleverly handling the keyboard with justPressed and isDown giving the basic but very useful methods to define what to do in your update loops as a response to input

Since this new input package is an important step for input handling and it introduces a new way of handling it, It requires some explanation on its origins so you can fully grasp why it was made and what you can use it for.

Introduction :
The basic idea that made this new system came to life is : identifying the player’s intentions and not the key he is pressing. The best way to do that is to have the actual “intention” be easily identifiable – as a string, and that’s how this system works. Intentions are actions like “jump” or “duck” or “fly”, and it makes coding your game objects so much easier…

This means a lot for CE users : you no longer have to remember key codes or import flash.ui.Keyboard to have them of course, you can change the key configuration of your game in real time (a feature a lot of desktop gamers desperately want to have sometimes) and by extrapolating the idea further : it gives room for a wide variety of controllers ( gamepads , socket) … The Hero class would no longer need to know from which controller the “call” came from. Unifying any possible input styles is the best thing a game engine could need.

Inside the system :
Now I hope I got you a bit excited about this new feature. This was inspired by the MIDI system : note on , note off, and continuous controller signals (but with a twist). We also thought of an on-screen joystick and buttons for games targeting tablets which was an idea that totally fit in the new input system and even got us to go further and made it what it is now.

How to use it :
We left backwards compatible versions of isDown and justPressed in the Input so the basic games should still run without having to change much code, however If you are using special controls and custom objects requiring other keycodes than the simple arrows and spacebar configuration then you should migrate to the new system or your game will be “broken”. And here’s how to migrate/use the system :

inside your “hero”, you can now use

if(_ce.input.justDid("fly", inputChannel))
    //insert "start flying" code here

if(_ce.input.isDoing("fly", inputChannel))
   //if you are using a controller such as a joystick and want to give more control to your player, you can use the new getActionValue method
   var flying_intensity:Number = _ce.input.getActionValue("fly", inputChannel);
   //and apply it.
   _body.velocity.y = - flying_intensity * 150; //this is just an example application, it might not be as accurate as you'd want it to.

   //insert other "flying" code here

inputChannel should be a class property defining the channel the game object will listen to.
Now in your state get the default keyboard from CE’s input instance, and add new actions to your keyboard :

//your hero can listen to channel 1 this way.
hero.inputChannel = 1;

var keyboard:Keyboard = CitrusEngine.getInstance().input.keyboard as Keyboard;
//and here we can add the "fly" action to the keyboard, that will only be sent to channel 1.
keyboard.addKeyAction("fly", Keyboard.F,1);

Now the F key will tell Input that the player wants to fly, and your hero will respond to that.
The keyboard sends values of 1 when an action is on, so getActionValue is not interesting in this case, but if you are going to use the VirtualJoystick it will be.

//create a virtual joystick input controller :
var vj:VirtualJoystick = new VirtualJoystick("joy",{radius:120,defaultChannel:1});

//add the "fly" action to the Y axis of the joystick, when joystick is between -1 and 0

Now you can fly with the F key, or with the virtual joystick, with the added bonus that VirtualJoystick will control the velocity more precisely.

In this example we have decided to use channel number 1 but it is completely optional, if you do not want to set channels up, everything will go through channel 0.

A purposeful system :

Basically, it is a messaging system but focused on input, as using this for any other type of internal messaging is not recommended (you better use the Signal package). As long as you have access to the CitrusEngine Input instance, whether it be from your game menu or game objects, you can know what the user wants to do.

If it’s a “virtual controller” it’d be the same. It is conceivable to have a “socket” input controller (which you can create by extending the InputController class) from which data from an external server would be parsed and send actions to the input system, so you could control your game through IP very easily if you are into highly experimental stuff such as performance art that need to communicate with electronics (yes because CE can be used for anything !) .
A virtual controller can also be an AI – so your main hero or enemies can be controller by an AI without having to hardcode it inside the class, but simply switching it’s inputChannel.
This of course can have unwanted effects since the physics engine is unpredictable but you can still experiment with this possibility.

Other features :

you can route all input actions to a single channel if you need to : for example if you bring up a pause menu – so all the keyboard (or any other controller) actions get routed to the channel you want for your menu’s UI and in the background, the player’s character will no longer react to input.
This came along with the development of TimeShifter, so we are waiting for your feedback on this feature, or any other features you’d want to be added or modified.

the following will route all input action to channel 5 when a menu comes up for example:


when you are done of course, just call



TimeShifter is an experimental class that saves the property changes on each frame, which means you can replay actions (kill-cams?) or rewind time, as long as you have registered the properties you want to be saved. It also takes snapshots of actions in the input system so they can get replayed. Replaying actions only is however not the best way to replay a section of gameplay since physics engines are non-deterministic and your player might fall on a platform in one replay and fall off of it on the next replay even though the snapshots are exactly the same.
When constructing a TimeShifter instance, you need to define the maximum size of the buffer (in seconds) since it consumes memory to save “states” of properties and the buffer length shouldn’t be infinite (or it would be similar to a highly ‘dangerous’ memory leak).
As of now, this buffer length you define will be the amount of time that will be replayed or rewounded. For our Braid demo, we keep 20 seconds of buffer, but for a simple “kill-cam” replay style, you’d just need 5 seconds or less.

Here’s how to start a delayed replay :

// we want a maximum buffer size of 5 seconds.
var timeshifter:TimeShifter = new TimeShifter(5);
//and we want to replay the action 0.2 seconds after a coin has been hit
coin.onEndContact.add( function(e:InteractionCallback):void { timeshifter.startReplay(0.2); });

As of now, TimeShifter listens to channel 16 for the “timeshift” action.
It is good to consider a certain channel to be an “internal” channel for such purposes – we arbitrarily chose 16.

TimeShifter only records what you ask it to. So let’s just take a look at how we set up the recording for a hero’s position and animations

{ object:hero,
continuous:["x", "y"],
discrete:["dead","inverted", "collideable", "animation", "animationFrame"] } );

the idea here is to give it continuous values (positions for example that will be interpolated linearly when the speed of playback is lower than 1)
and discrete values such as booleans or strings to record.
so what will be recorded here are publicly accessible (and not read-only) properties :
-discrete booleans:
-discrete strings:
-discrete int:
object.animationFrame (which is a number, but cannot be interpolated as AnimationSequences would not know what frame 1.2345 is.
Check out the Braid demo’s source code to learn more about how you can use this system.

Multiplayer friendly
Thanks to this new input system, we are able to make a multiplayer game using the Citrus Engine. First of all, I would like to highlight that making a multiplayer game based on physics engine is something complicated. Here are two links with very good references : Glenn Fiedler’s blog (also very useful for FRIM) and a Valve article.
A quick and dirty demo made with Union Platform and their free services for quick testing (expect lags so some collisions may not happen!). Source code commented there. Obviously this demo needs lots of work to be fully functional like object’s movement interpolation when a new state snapshot is received, occupants management, ect… But it shows that the Citrus Engine thanks to this new input system is now multiplayer friendly!

We hope that you will love this update. Happy coding!

4 thoughts on “V3.1.0 new input system, Nape 2, multiplayer friendly, meet Braid.

  1. I really think that is the most impressive feature , really great, I’ve always loved this game Braid, and I found it hard to coding and impressive, I even named best indie game for me and my whole team, you’re really the best aymeric

Leave a Reply

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