Update: added frame rate controls!

I've been heads-down on art for our upcoming free-to-play game Lunch Bug and I thought I'd take a break to show you a little tool I just put together to improve my process. Have a look:

Please note that this is just the first draft of animations. Improvements are on the way!

Interact with it

In the center of the canvas is a game piece from Lunch Bug. The text in the upper-left can be pressed to play animations:

Put : this is the animation that will play when the user puts a new piece on the board.

: this is the animation that will play when the user puts a new piece on the board. Idle : "idle" is what we're calling animations that will play randomly when no user interaction has happened for a while. Think Sonic the Hedgehog tapping his foot!

: "idle" is what we're calling animations that will play randomly when no user interaction has happened for a while. Think Sonic the Hedgehog tapping his foot! Grow : seedlings and bulbs can grow into berries , which are worth the most points in the game.

: seedlings and bulbs can grow into , which are worth the most points in the game. Shrink: berries and bulbs can be shrunk (shrank? shrenk?) into pieces worth fewer points if players aren't careful!

In Lunch Bug, pieces can be "captured" by being surrounded, which changes their appearance. Normally I could just have had a giant sprite sheet with all the images for the various types of plants, but we can't have any images larger than 1024x1024 pixels due to mobile memory restraints, which meant breaking them up into multiple files.

The complication of switching the image file part-way through the animations made using traditional animation tools difficult. That's why I built this tool! Now that I've got it, I'll be going back and iterating a few more times before the final version, so at this point feedback is especially valuable.

Where's the code?

Normally I'd be open sourcing this on the Lost Decade Games GitHub account but it's tightly coupled to our internal game engine. We may open source the engine and its tools eventually but it's getting bulldozed too often to even consider it at the moment. What I can do is copypasta the code so you can get a general idea:

define([ "djinn/Game", "djinn/assets", "djinn/Sprite", "djinn/TextView", "djinn/utils/viewEffects" ], function (Game, assets, Sprite, TextView, effects) { return Game.extend({ init: function (conf) { // Conf this._super(conf); this.view.backgroundColor = "white"; // Pull in the assets we need assets.load([ "media/images/seedling.png", "media/images/bulb.png", "media/images/berry.png" ]); // Create the sprite subject var state = "seedling"; var w = 100; var h = 100; this.sprite = new Sprite({ parent: this.view, image: "media/images/seedling.png", x: 0, y: 0, width: w, height: h, animations: { "default": { frames: [ [0, 0] ] }, "put": { frameRate: 5, frames: [ [w * 4, 0], [w * 3, 0], [w * 2, 0], [w, 0], [0, 0] ], loop: false }, "idle": { frameRate: 12, frames: [ [0, h], [w, h], [w * 2, h], [w * 3, h], [w * 4, h] ], loop: false }, "grow": { frameRate: 8, frames: [ [0, h * 2], [w, h * 2], [w * 2, h * 2], [w * 3, h * 2], [w * 4, h * 2] ], loop: false }, "shrink": { frameRate: 8, frames: [ [w * 4, h * 2], [w * 3, h * 2], [w * 2, h * 2], [w, h * 2], [0, h * 2] ], loop: false } } }).align("center", "center"); var x = 10; var height = TextView.defaults.fontSize = 32; // Put var putView = new TextView({ parent: this.view, text: "Put", x: x, }).align("center").on("inputStart", this, function () { // Setup this.sprite.completeTween(); this.sprite.opacity = 0; // Execute this.sprite.playAnimation("put"); effects.fadeIn(this.sprite, { duration: 500 }); }); // Idle var idleView = new TextView({ parent: this.view, text: "Idle", x: x, y: height }).align("center").on("inputStart", this, function () { this.sprite.playAnimation("idle"); }); // Grow var growView = new TextView({ parent: this.view, text: "Grow", x: x, y: height * 2 }).align("center").on("inputStart", this, function () { if (state == "berry") { return; } var onAnimationEnd = function () { this.sprite.off("animationEnd", this, onAnimationEnd); if (state == "seedling") { state = "bulb"; this.sprite.image = "media/images/bulb.png"; } else { state = "berry"; this.sprite.image = "media/images/berry.png"; } this.sprite.playAnimation("default"); }; this.sprite.on("animationEnd", this, onAnimationEnd); this.sprite.playAnimation("grow"); }); // Shrink var shrinkView = new TextView({ parent: this.view, text: "Shrink", x: x, y: height * 3 }).align("center").on("inputStart", this, function () { if (state == "seedling") { return; } if (state == "berry") { state = "bulb"; this.sprite.image = "media/images/bulb.png"; } else { state = "seedling"; this.sprite.image = "media/images/seedling.png"; } var onAnimationEnd = function () { this.sprite.off("animationEnd", this, onAnimationEnd); this.sprite.playAnimation("default"); }; this.sprite.on("animationEnd", this, onAnimationEnd); this.sprite.playAnimation("shrink"); }); } }); });

Might be easier to read in this gist. Note that the tool is not very robust as I didn't want to spend too much time on it…

More Lunch Bug news is just around the corner (can you say playable prototype?!) so be sure to subscribe to get it when it's hot. Thanks for reading!

Follow author @richtaur