Thank you QLab! But our princess is in another castle!

Thank you, QLab!"

One of the design techniques I like to employ when working with QLab is to simulate a multiplane camera by stacking PNG images up in layers, and manipulating them with Fade cues. For simple motion, this can be a very effective, pleasantly lo-fi way to work. After seeing me demonstrate this technique using a few images from Snow White and the Seven Dwarfs, the first ever cel-animated feature film, my colleague Andy Leviss wondered aloud how far one could go using QLab as a live animation tool. Could we, for example, make a video game? How about a classic? How about Super Mario Bros.?

Mario snapshot

Challenge accepted! The first task was to make Mario walk. Mario, it turns out, is animated with only twenty four images for the entire game:

Mario graphics

To keep things simple, I elected to animate only Super Mario, and only in Level 1-1, which meant my palette was only six frames. Of those six, only three are used for walking, so my walking loop turned out to be fairly simple:

Walk loop

Each frame is displayed for .1 seconds, then that frame is stopped and the next frame is shown for .1 seconds. A Start cue at the bottom of the list (cue 1.7) restarts the loop. All Mario images are on layer “top”. I then made a copy of this group with Mario rotated 180 degrees about the Y axis, so that Mario can walk to the left as well.

Now Mario can walk, but he can’t stop yet. If I stop the walking loop, Mario just disappears. I needed to add a Mario standing still cue:

Stop

This stops the walking loop, arms and disarms some other stuff that I’ll talk about in a second, and then shows an image of Mario standing still. The programming to make Mario duck is almost exactly the same, just with a different image.

Next, I produced a simplified background image, without pipes, bricks, or holes in the ground:

Background

The image is displayed on layer 0, and there are two groups of Fade cues to slide the background left or right, depending upon the direction that Mario is walking:

Slide loop

This one is a little interesting for a couple of reasons. First, it uses relative geometry fades, so each of those Fade cues is a -5 pixel translation on the X axis. Each time they’re triggered, the background slides five pixels to camera left over a duration of .1 seconds. But why are there two fades? This is due to a fundamental rule in QLab: starting a cue that’s already running has no effect. For this reason, you cannot make a loop of one cue auto-followed by a Start cue that re-triggers it. When the Start cue triggers, it tries to restart the cue which has just ended. Since there is in fact a tiny amount of time that a cue remains active after it finishes playing, the Start cue cannot restart it. I could have added a tiny pre-wait to the Start cue, but that caused the motion to become stutter-y. So instead, I simply put two Fade cues, one after the other, and adjusted the amount of the translation and the duration of the fades until the motion looked correct when played together with Mario walking.

The really fun stuff came when I starting to work out how to make Mario jump. Here’s the cue programming:

Jump

The first cue is a Group cue set to start-all-children-simultaneously, and it stops whatever other Mario cues might be playing (walking left, walking right, stopped facing left, stopped facing right, ducking left, and ducking right.) The next cue is a Group cue set to start-first-and-go-to-next. Within that Group, the next cue displays the jumping Mario image. The two Fade cues animate the Y translation to create the actual jumping motion. Here’s a look at the curve shape I used to create an acceleration and deceleration that looked most similar to the one in the original game:

Jump curve

Next, the Stop cue stops the jumping Mario image. What happens after that, though, depends upon the Arm and Disarm cues I mentioned earlier.

See, if Mario jumps while standing still, he needs to return to standing still when he lands. But if he jumps while moving, then he needs to return to walking when he lands. Therefore, contained within the Group cues that handle walking and stopping are Arm and Disarm cues which target the last two cues in the jumping Group. When the walk Group is triggered, the “walk after jump” cue is armed and the “stop after jump” cue is disarmed, and when the stop Group is triggered, the opposite occurs. Those two cues are Start cues which target those same walk and stop Groups.

This flip-flop arm and disarm technique is used in several places to control the flow of playback.

The final piece of the puzzle was how to operate this workspace in a way that feels like playing a video game. I needed an individual control for each action, plus I needed to be able to prevent conflicting actions from happening at once in a way that didn’t feel forced. Enter the Logitech F310 USB gamepad:

Gamepad

This baby sends HID messages (the same kind that a keyboard or mouse uses) in the exact pattern that I needed: each button has two unique messages, one for press and one for release, and the four-way controller (sometimes called a hat-switch) has one message for each direction, and then a fifth message for releasing any of the four directions.

Using an open-source HID-to-MIDI mapping program called MidiHID, I mapped each control that I wanted to use to a MIDI Note On message, then assigned those MIDI notes as MIDI triggers to the relevant cues.

If you’re following along at home, any regular MIDI device that sends note on and note off can be used in place of this setup.

Wrap that all up, and you get a playable, though simplified, version of Super Mario Bros, using nothing but PNG images for source material, and nothing but Video, Fade, Stop, Start, Arm, Disarm, and Group cues.

I hope you have fun with this. If it inspires you to create anything crazy, write to us and tell us about it!

Here’s a short sample of how this looks in use. To get the full nostalgia effect, I filmed the screen with my iPhone, which turns out to be a surprisingly effective way to simulate a lousy NTSC television from the 1980s.

(The screen capture process introduced some stutters in this final demo; the actual animation is smooth.)