LDG

Play around with the Lunch Bug animation preview tool

by Matt Hackett, 2012 Apr 11
How to Make a Video Game All By Yourself

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:

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

LDG © 2022 • BlogTerms of ServiceVideo Policy • v2.1.2