Skeletal animations in javascript with Spine and MelonJS

When we started our html5 game Peter Acorn we were optimistic but cautious about the level of performance we could get from the platform on mobile. We started off with simple spritesheets which looped through our animations frame by frame and were extremely fast to execute. I will assume you either already have a MelonJS game setup or bootstrap yourself with one of the MelonJS tutorials to get up and running.



This was fast to get setup initially with good performance but there are limitations:

  • Getting smooth transitions between animations is difficult.
  • Using an image editor and scripts to structure and export the spritesheets takes time.
One major limitation which eventually broke the camels back with related to resolution and frame rate. If you want to increase the resolution of the game, then your sprite sheet gets much larger. To compound the issue if you want a smoother or more complex animation then adding extra frames to the sprite sheet exasperates the issue the higher the resolution is. You can offset this by having a square sheet but by now we had already seen Spine and wanted to try it.

We were skeptical about the performance of the javascript runtime especially on mobile but these concerns were not warranted in the end. First we had to find a way to get it integrated into MelonJS. You can find an example of this on Github  and a big thanks to Aaron for adding this support to MelonJS! 

You will want to load integration after both the melon and spine runtimes:

<script type="text/javascript" src="lib/melonjs/melonJS-1.1.0.js"></script>
<script type="text/javascript" src="lib/melonjs-spine/spine.js"></script>
<script type="text/javascript" src="lib/melonjs-spine/melonjs-spine.js"></script>

I have already added my chopped assets to Spine and created a few animations. I noticed that I needed to key all the bones otherwise I would get strange behaviour in-game and the animations would be totally out of whack as a result of bad relative co-ordinate data. So on the first frame of each of your animations select all the bones and make sure their rotation, translation and scale is keyed in. Take care especially on mobile to keep the number of bones, images and frames to a minimum to improve performance.



When exporting lets create an atlas which efficiently puts all the components parts of your character onto a single image. 



Turn off rotation (which packs more efficiently) as the integration does not seem to support it, not a huge loss anyway. You can also adjust the scaling factor but if you choose to use this you will also need to adjust the animationScale parameter in the Spine.Entity settings. It does not map directly to the scale factor you plug into here so some experimentation is needed.



You will get three files as a result of the export. The png contains the images for your character. The atlas is the mapping between the images and the skeleton and the json contains the animation data.

{name: 'peter', type:'json', src:'assets/atlas/peter/skeleton.json'},
{name: 'peter_atlas', type:'binary', src:'assets/atlas/peter/skeleton.atlas'},
{name: 'peter_atlas', type:'image', src:'assets/atlas/peter/skeleton.png'},

The integration allows you to create spine entities which will use skeletal animation instead of a sprite sheet. So you can use this base class for your animated entities.

var player = me.Spine.Entity.extend({
    
    init: function(x, y, settings) {
                   
        var settings = {
            atlas: 'peter_atlas',
            imagePath: 'peter_atlas',
            spineData: 'peter',
            name: 'PlayerEntity',
            imageScale: 1,
            animationScale: 1
        };
       
        this._super(me.Spine.Entity, "init", [x, y, settings]);

Still within the init method you can adjust how the integration maps your character into the game.

this.anchorPoint = new me.Vector2d(0, 0);
this.renderOffset = new me.Vector2d(0, 0);

The anchorPoint adjusts where the collision box is mapped related to your characters position and their width and height.

this.body.addShape(new me.Rect(-(this.width * this.anchorPoint.x), -(this.height * this.anchorPoint.y), this.width, this.height));

The renderOffset adjusts where the sprite is rendered in relation to the characters position.

context.translate(x + this.renderOffset.x, y + this.renderOffset.y);

Once these are set you can call this.updateColRectToAnchorPoint() to set the collision box on your character. We can now configure the animations on our character. My character Peter will start off with the running animation with 0 delay and we’ll set loop indefinitely to true. We can also set animation mixing using setMixByName which will tween between the animations when they are changed and makes the transitions appear really smooth. The time the runtime takes to smooth between the animations is configurable and I’ve set them to 100ms. The longer this value is the smoother it can look but you will need to experiment to find the best value for your animations.

this.state.setAnimationByName(0, "running", true);

this.stateData.setMixByName("running", "jumping", 0.1);
this.stateData.setMixByName("jumping", "running", 0.1);

You can switch animations by calling:

this.state.setAnimationByName(0, "jumping", true);

You can also set up a playlist of animations to run through using addAnimationByName. Once each animation is complete it will move onto the next one assuming you have queued one up. You will likely have one animation which will then loop indefinitely until a change in gameplay state forces a change. Using these animation queues you can create even smoother transitions than tweening alone.

this.state.setAnimationByName(0, "start-gliding", false);
this.state.addAnimationByName(0, "gliding", true, 0);

If you are taking advantage of spine slots which allow you to change the images associated with the bones then you will need to call setSlotsToSetupPost when changing animation.

this.skeleton.setSlotsToSetupPose();

Lastly if you want to get the name of the currently executing animation you can use:

var animation = this.state.tracks[0].animation.name

Despite using the runtime we were still able to reach 60 FPS on mobile devices as old as 3 years ago like my Galaxy Nexus which is hugely impressive. We managed this using the CocoonJS container and we are looking forward to talking about our experiences with this in the future.

The Spine software is a pleasure to work with and putting together animations is great fun. The large amount of integrations for many game engines and platforms together with the great performance makes it a fantastic package. Highly recommended!

Here are the animations in action although I do not doubt that an experienced animator would be able to do better:



Getting a faster sass-watch using grunt-sass

My sass --watch was getting increasingly slow especially with compiling bootstrap on every change. It was also using a substantial amount of battery life as a result.

After looking around it seems Sindre Sorhus has already solved the problem. The repository provides a native sass compiler which you can easily install using:

npm install --save-dev grunt-sass

I want to run grunt sasswatch to invoke the watcher separately to the other tasks to I created a new one. When it is called it will run the sass compile once and then invoke the watcher.

grunt.registerTask('sasswatch', [
  'sass',
  'watch:scss'
]);

The sass configuration calls my main scss which includes the many and heavy bootstrap files.

sass: {
    options: {
        sourceMap: true
    },
    dist: {
        files: {
            'app/css/main.css': 'app/main.scss'
        }
    }
}

Finally we can write the watcher which only listens for scss files in my application folder and the bootstrap one.

watch: {
  {
    files: ['stylesheets/*.scss', 'bootstrap-sass-official/assets/stylesheets/**.scss'],
    tasks: ['sass']
  }
}

Including the watch response time this solution is now taking around 1.6s compared with around 6.9s.

Windows Identity Foundation not removing Session Security Token from Cache

Recently were I work we had a PEN test on one of our applications and that highlighted that the federated authentication token for a relying party wasn’t being cleaned up correctly when logging out. After sitting down and working out what was cause for this problem I thought I would share my findings and the solution.

The problem

At work we use a custom session security token cache to store the token so we don’t have to pass around so much data to and from the client. When logging out from a replying party using a federated sign out e.g. “wsignoutcleanup1.0” the WIF code base should use the federation authentication module (FAM) and the “SignOut” method to get the session authentication module (SAM) and tidy up/delete the current session token. See the code example 1 below.

Example 1

public virtual void SignOut(bool isIPRequest)
{
  try
  {
    this.OnSigningOut(new SigningOutEventArgs(isIPRequest));
    SessionAuthenticationModule.Current.DeleteSessionTokenCookie();
    this.OnSignedOut(EventArgs.Empty);
  }
} 

The session authentication module and its method “DeleteSessiontokenCookie” then checks that the property “ContextSessionSecurityToken” isn’t null before calling the actual code to remove the session security token from the persistent cache and the in memory one. See example 2 below.

Example 2

public void DeleteSessionTokenCookie()
{
  this.CookieHandler.Delete();
  if (this.ContextSessionSecurityToken == null)
    return;
  this.RemoveSessionTokenFromCache(this.ContextSessionSecurityToken);
}

Now debugging the code and decompiling it I was able to work out that the session authentication module sets the “ContextSessionSecurityToken” when the “OnAuthenticateRequest” event is handled in the SAM. However, when signing out from the RP using “wsignoutcleanup1.0” this event is not fired or handled by the SAM if the FAM is defined above it in the web.config modules section (see below). No you read that correctly the order of the FAM and SAM in the web config appear to determine whether your token is cleared from the cache or not!

Does not remove the item from the cache

<removename="FormsAuthentication" />

<add name="WSFederationAuthenticationModule"
  type="System.IdentityModel.Services.WSFederationAuthenticationModule, System.IdentityModel.Services, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
  preCondition="managedHandler" />

<add name="SessionAuthenticationModule" 
  type="System.IdentityModel.Services.SessionAuthenticationModule, System.IdentityModel.Services, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
  preCondition="managedHandler" />

The fix

Now, I’m not certain but I do not believe an application should be dependent on the order of HTTP modules to function correctly. Am I missing something? A quick fix for us is indeed to switch the order of the modules in the web.config to get the “ContextSessionSecurityToken” to be set (See example modules below).

<remove name="FormsAuthentication" />

<add name="SessionAuthenticationModule" 
  type="System.IdentityModel.Services.SessionAuthenticationModule, System.IdentityModel.Services, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" 
  preCondition="managedHandler" />

<add name="WSFederationAuthenticationModule" 
  type="System.IdentityModel.Services.WSFederationAuthenticationModule, System.IdentityModel.Services, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" 
  preCondition="managedHandler" />

Additionally I can also add some code to the FAM to do manually set the property on the SAM (see below). Both approaches feel wrong to me in that they fix an issue in the underline WIF implementation. Well potential issue…

FAM change

public override void SignOut(bool isIPRequest)
{
  var sessionModule = FederatedAuthentication.SessionAuthenticationModule;
  SessionSecurityToken token;
  var hasManagedToRead = sessionModule.TryReadSessionTokenFromCookie(out token);
  if (hasManagedToRead)
  {
    var lafSam = sessionModule asLafSessionAuthenticationModule;
    lafSam.ContextSessionSecurityToken = token;
  }
  sessionModule.DeleteSessionTokenCookie();
  base.SignOut(isIPRequest);
}

SAM change (redefined property in the LAF custom SAM so I could set the property!

public SessionSecurityToken ContextSessionSecurityToken
{
  get
  {
    return (SessionSecurityToken)HttpContext.Current.Items[(object)typeof(SessionSecurityToken).AssemblyQualifiedName];
  }
  set
  {
    HttpContext.Current.Items[(object)typeof(SessionSecurityToken).AssemblyQualifiedName] = (object)value;
  }
}

Until next time.

May 13 2014

Applying a theme to your Chrome Dev Tools

I wanted to make the Chrome dev tools editor easier on the eyes so I had a look around to see if it was possible. Since the web tools UI is web based it seems easy to skin and there is a great eco system of themes available from which to choose from.

A great site to browse through is devthemez.com, although it seems some of the themes no longer render correctly perhaps to due breaking changes in Chrome. One relatively complete theme which did function correctly is chromegray so this is the one I kept.

It will need to be installed into the following directory if your on Windows:

C:\Users\%USERNAME%\appdata\local\Google\Chrome\User Data\Default\User StyleSheets

So you can run the following PowerShell command to get this installed directly.

Invoke-WebRequest -Uri "https://raw.github.com/karelvuong/chromegray/master/Stable/Custom.css" -OutFile "C:\Users\$env:USERNAME\appdata\local\Google\Chrome\User Data\Default\User StyleSheets\Custom.css" 

As a result I have a flatter design and an easier on the eyes editor which reminds me more of sublime text.

image

I have to recommend giving it a go, the editor updates live with the new theme. If you don’t like it then its easy to find another one or remove the theme altogether.

Older Posts