TreeFortress
Mobile Game Studio
  • facebook
  • twitter
  • google
  • rss
  • About Us
  • Contact
  • Blog
Select Page ...

Introducting SpriterAS - Play Spriter Animations with Starling

Shawn January 24, 2013 Blog 9 Comments

[Update] We’ve added a second post with a small benchmark to show performance [/Update]

We’re happy to announce the public release of our first library SpriterAS. SpriterAS is an Actionscript Library based on Starling, which lets you playback Spriter Animations (.SCML files).

  • https://github.com/treefortress/SpriterAS

What is Spriter?

Spriter is kickstarter funded animation tool, which enables you to create advanced animations and render them on the GPU with a very low memory footprint.

It uses a technique that we like to call Armature Animation.  Traditionally, animations are created from a series of  individual frames, and played back in a series, almost like a Flip Book. This is an age-old technique that has traditionally worked well, but it tends to consumes GPU memory very quickly, and the maximum size for a Texture on Mobile devices is just 2048x2048px.

That forces the following limitations:

  • Animations must be kept very short and sweet
  • Animations must be created at low Framerates, usually 12-24fps
  • Animations tend to be low-resolution in order to fit in 2048×2048 Texture Size, creating true “Retina” quality graphics is almost impossible.

Here’s an example of a traditional frame-based animation:

You can see how inefficient this is! Our 2048×2048 texture gets consumed very quickly.

Armature Animation takes a different approach. Instead of storing each frame in a SpriteSheet, it simply stores each individual piece of the animation (head, foot, arm etc). And then, programatically moves the pieces from one keyframe to the next. You can think of it similar to how a Puppet works, each piece is on a string and moves independantly of the others.

The benefits to this technique are immense:

  • Create beautiful, crisp, hi-resolution graphics which never need to be downscaled, even on Retina Devices
  • Animations that run at 60fps, like butter :)
  • Create animations of any length, without penalty
  • Swap Images mid-animation, allowing you to use the same animation for different character variations
  • Detach Parts of the Character, allowing you to create dismemberment effects
  • Map other display objects to pieces of your animation (attach particle emitter to someone’s hand, embed an arrow in their head etc)

As with everything in Programming, this isn’t a complete Silver Bullet, there are some important tradeoff’s:

  • Less flexibility with animations, can’t have custom shapes on every single frame. This can be a considerable learning curve for your Animator.
  • More expensive to render. Because each character is made up of many sprites, they can incur a much higher rendering load than traditional Spritesheet’s. Instead of rendering 1 display object per character, we might be rendering 8 or 12. So, although we save on memory usage, we lose on the total number of character we can render at once.

Here is an example of a typical SpriteSheet for a 2 Spriter-Based Characters:

 

You can see how much more efficient this is, this is everything we need to render 2 complete characters. With this, we can make literally hours of animations for no additional cost. Also, we can render extremely high fidelity graphics and run them @ 60fps.

SpriterAS Basic Usage

Now that I’ve sufficiently bored you with the background story, lets jump in and see how SpriterAS works.

The library itself is hosted on our github, and includes sample SCML files and demo’s to get you started quickly:

  • https://github.com/treefortress/SpriterAS

Once you have imported the library, and bootstraped Starling, you’re ready to get going!

The first step is to load your SCML files, using the provided SpriterLoader class:

function onStarlingReady(event:Event):void {
	//Use SpriterLoader to load your SCML File.
	//It will load all images, and pack them to a shared TextureAtlas.
	spriterLoader = new SpriterLoader();
	spriterLoader.completed.addOnce(onSpriterLoadComplete);
	spriterLoader.load(["assets/spriter/brawler/brawler.scml",
			    "assets/spriter/orc/orc.scml"]);
}

function onSpriterLoadComplete(loader:SpriterLoader):void {
	//Once SpriterLoader is complete, we can use it as a Factory, and just ask for the animation we want:
	var orc:SpriterClip = loader.getSpriterClip("orc");

	//Play an animation, and add our DisplayObject to the stage
	orc.play("walk");
	starlingRoot.addChild(orc);

	//For performance reasons, SpriterClip's do not update themselves,
	//you can do it manually, or just use Starling's Juggler.
	Starling.juggler.add(orc);

	//Create another character, in short form
	var brawler:SpriterClip = loader.getSpriterClip("brawler");
	brawler.play("idle");
	addChild(brawler);
	Starling.juggler.add(brawler);
}

If that works correctly, you should get something similar to this:

Observe! Beautiful, crisp animations running @ 60fps.

In order to enable the ‘attack on click’, we just need to add a few lines of code:

//For performance reasons, SpriterClip's are not touchable by default.
brawler.touchable = true;

//Add a listener to the spriterClip, listening for a Starling TouchEvent
brawler.addEventListener(TouchEvent.TOUCH, onCharacterTouched);

protected function onCharacterTouched(event:TouchEvent):void {
	//Boilerplate Starling Mouse-Handler Code, TOUCH_END == MOUSE_UP
	var touch:Touch = event.touches[0];
	if(touch.phase == TouchPhase.ENDED){
		var clip:SpriterClip = (event.currentTarget as SpriterClip);
		//Play the attack animation
		clip.play("attack");
		//Use the 'animationComplete' signal to return to the previous animation when the attack is over
		clip.animationComplete.addOnce(function(clip:SpriterClip){
			clip.play("idle");
		});
	}
}

Callbacks

SpriterAS supports a callback system, so that you can insert a Callback into an animation at a certain position (in milliseconds), and then get notified whenever that position is reached.

This is useful for syncing animation with a character’s actions, for example, imagine an animation of a character firing a Bow and Arrow, you need to be able to spawn the Arrow Projectile at a precise moment in the character’s animation.

Here’s an example of how we would use the addCallback API to sync a character’s movements:


//To respond an specific timed events, add a custom callback:
orc.addCallback(function() {
	shakeSprite(3);
}, 840); //Callback @ 840ms

orc.addCallback(function() {
	shakeSprite(6);
}, 400); //Callback @ 400ms

protected function shakeSprite(amount:int):void {
	for(var i:int = 0; i < amount; i++){
		setTimeout(function(){
			orc.x = 300 - 5 + 10 * Math.random();
			orc.y = -5 + 10 * Math.random();
		}, i * 34);
	}
}

View Source On GitHub

The result of that, looks like this:

Sprite Swapping

Another feature of SpriterAS is to swap pieces of an running animation using the swapPiece() and unswapPiece() API’s.

This is useful for lots of fun stuff:

  • Easily change facial expressions of characters mid-animation (ie blinking)
  • Swap the Weapons / Armor that a Character is holding
  • Randomly swap body-parts or clothing to give more variety to your Characters
  • Swap ‘damaged states’, a shield or piece of armor could deteriorate as it’s used

To do this, you must know the exact image name as it’s included in your spriter animation, then you simply swap one piece for another.

Lets look at how we’d make our Orc character blink:


//Make the sprite Blink by swapping his Eye sprites every few seconds.
setTimeout(blink, 2000);

protected function blink():void {
	//swap open eyes for closed, using the filenames included in your Spriter Animation
	orc.swapPiece("orc_0000_eyes", "orc_0000_eyes_closed");
	setTimeout(function():void {
		//unswap eyes to 'open' them, this will return to the original animation.
		orc.unswapPiece("orc_0000_eyes");

		//Blink again in a while...
		setTimeout(blink, 1000 + Math.random() * 2500);
	}, 60);
}

View Source On GitHub

With that code, you will create something like this:

Notice how he can blink at any point, even during his attack cycle, but the animation continues to run smoothly.

Important: In order to use this technique, the swapped images must be the _exact_ same size. So you need to take some care/forethought when creating your Spriter Animations. If they are not exactly the same, the image will appear to jump/move when you swap it. Eventually Spriter will support custom per-object registration points, which should remove this requirement.

Map External Display Objects

Another fun technique we can use is to match external sprites with a Character’s animation. This had many potential uses, but in this quick example, we’ll map a Starling Particle Emitter to our character’s hands, creating the effect that their hands are releasing magical energy.


//Create 2 standard Starling ParticleEmitters
emitterFront = new PDParticleSystem(new XML(new particleXml()), Texture.fromBitmap(new particleBitmap()));
addChild(emitterFront);
emitterFront.start();
Starling.juggler.add(emitterFront);

emitterBack = new PDParticleSystem(new XML(new particleXml()), Texture.fromBitmap(new particleBitmap()));
addChildAt(emitterBack, 0);
emitterBack.start();
Starling.juggler.add(emitterBack);

//Every frame, position the 2 emitters to match the position of the Character's Hands
public function advanceTime(time:Number):void {
	var frontHand:Image = mage.getImage("mage_0000_handfront");
	emitterFront.emitterX = mage.x + frontHand.x;
	emitterFront.emitterY = mage.y + frontHand.y;

	var backHand:Image = mage.getImage("mage_0004_handback");
	emitterBack.emitterX = mage.x + backHand.x;
	emitterBack.emitterY = mage.y + backHand.y;
}

View Source On GitHub

And here’s our little Mage, with glowing Magic Hands!

Control Body Parts

With SpriterAS you can easily control a characters individual body parts programatically. This can be used for knocking a helmet off a character, or maybe some cool random decapitation effects.

Here’s an example that shows how we can programmatically control any piece of a character using the excludePiece() and includePiece() API:


//Make the SpriterClip touchable
brawler.touchable = true;
brawler.addEventListener(TouchEvent.TOUCH, onSpriteTouched);

protected function onSpriteTouched(event:TouchEvent):void {
	var target:DisplayObject = event.target as DisplayObject;
	var touch:Touch = event.touches[0];

	//User Has Pressed on a Piece
	if(touch.phase == TouchPhase.BEGAN){
		dragTarget = target;
		//Ask SpriterClip to exclude the clicked piece,
		//this means it's position will no longer be updated internally.
		brawler.excludePiece(target);
	}
	//User has released a piece
	else if(touch.phase == TouchPhase.ENDED){
		dragTarget = null;
		//include the piece back into the animation
		brawler.includePiece(target);
	}
	//User has moved the mouse while a piece is being dragged
	else if(dragTarget && touch.phase == TouchPhase.MOVED){
		//Convert global X/Y to local
		var pt:Point = brawler.globalToLocal(new Point(touch.globalX, touch.globalY));
		dragTarget.x = pt.x;
		dragTarget.y = pt.y;
	}
}

View Source On GitHub

Most of that code is handling the Starling touch interactions, but you can see that we simply call excludePiece(), and then modify the position of the sprite any way you see fit.

You can see that in action here:

Wrapping Up

We hope that you find SpriterAS useful, and if you would like to contibute, definately feel free to submit pull requests to the repository:

  • https://github.com/treefortress/SpriterAS

Future versions of Spriter will add support for Bones, and other various features. We’ll hopefully be able to update SpriterAS to support them, but will also be busy making games. If the community gets there before us, we’ll happily integrate your changes into the main branch.

Thanks for visiting!

Share this:

  • Twitter
  • Facebook
  • Google +1
  • Tumblr
  • Reddit
  • Email
← Bardbarian is Evolving
Creating dynamic TextureAtlas’s in Starling →
Shawn

Monster habs fan, mobile developer, and father of 3.

  • jacksondunstan

    It looks like you got a lot farther than I did with Spriter+Starling, but perhaps there are some ideas and/or code (MIT license, so feel free) that you can glean from my own implementation. Here’s an article I wrote about it last month:

    http://jacksondunstan.com/articles/2093

  • Sammy Joe

    Well done guys! I’m glad to see Spriter gaining so much traction.

    I recently completed my own Spriter/Starling plugin titled SpriterMC.
    http://www.sammyjoeosborne.com/spriterMC

    I especially like your include/exclude API (something I was working on!)

  • katopz todsaporn

    is this free to use? i’m read at kickstarter and getting confuse already

  • Pingback: Introducting SpriterAS – Play Spriter Animations with Starling | TreeFortress « eaflash

  • Tronster Hartley

    Great set of features; it will be interesting to see how the JS version turns out. Unless I’m mistaken, I believe the term “Paper Doll Animation” is already in use in the game industry to describe this technique. (Or at least we’ve been throwing that term around work for years.)

  • http://profile.yahoo.com/RBV4IYOLQCYLYPOVLOSZAFF4CE inasia

    I’ve noticed that Genome2D also has a Spriter plug-in.

  • Fredrik Schultz

    I’m currently playing around with SpriterAS, which is really cool! But I’m wondering if it’s possible to interpolate between animations (so that the current pose tween to the next animations start) to make a smoother transition?
    Thanks!

    • http://www.mikegaboury.com Mike Gaboury

      It’s definately on the wishlist but not something it supports at the moment.

  • pelusota

    This looks amazingly good!

    Let me try it!

  • Follow Us

    facebookgooglersstwitter
  • Recent Posts

    • Tutorial: Texture Compression in AIR (Using ATF)
    • Bardbarian Sneak: Kamikaze Unit
    • SoundAS – A modern AS3 SoundManager
  • Copyright © 2013 TreeFortress.com. All Rights Reserved
    loading Cancel
    Post was not sent - check your email addresses!
    Email check failed, please try again
    Sorry, your blog cannot share posts by email.