Cruisin' On Auto-Pilot - Part III
We will continue with the technical discussion on the Auto-Pilot soon, but first I want to talk about something I've come to regard as The Great Auto-Pilot Hack. It's the one change made to the Auto-Pilot framework which enabled some of its most creative uses.
I liken it to having started out with a nice and sleek sports car, souping it up with some parts stolen from NASA, and eventually ending up with a space shuttle. Only that it wasn't a real space shuttle -- that's just a cool looking fiber-glass shell built feebly around the Apollo Lunar Lander.
Yep, that lunar lander. It's weird, it's ugly, and it looks to be constructed out of tin-foil, unholy amounts of duct tape, and bailing wire. Yet, it fulfilled its requirements honorably: namely, it got to the moon and didn't kill anybody. So, good enough for government work, I guess.
That's how I feel about this particular feature. It was a fantastically great addition: It unleashed the phenomenal potential of the Auto-Pilot system as an engine for animated cut-scenes; it enabled the expressivity and cinematic story-telling power of Christmas Carol's introduction sequences; and it let my imagination and creativity soar to places it just couldn't before.
However, it was still a hack. A hack grafted on with duct tape and bubble gum, and it was ugly as sin.
Before I tell you what this great big hack was, I will describe the problem which originally prompted it.
A Big Problem
From the very first article, I've emphasized the point that the Auto-Pilot is not a cut-scene sequencer or general game scripting engine; that it was purely and simply a sprite object scripting system. This may sound like a subtle distinction, but it is important.
As Charles Dickens states in "A Christmas Carol" when pointing out emphatically that Jacob Marley was, in fact, very much dead, "This must be distinctly understood, or nothing wonderful can come of the story I am going to relate."
So, per-object scripting engine, not per-scene. Got it? Alright, on we go.
As development of my very own "Christmas Carol" game (thank you, Mr. Dickens, for the inspiration) wound down to conclusion, I was left with the task of creating the final cut-scenes for each of the game eight stages. I had created already one or two simple cut-scenes, mostly as a proof-of-concept. These were very simple indeed: only one sprite entering the scene at a time, then leaving; then another one comes in, and leaves; the end.
Here's a sample of the very first sequence. The following "screenplay" is taken straight from my design document notes, and served as my guide to the Auto-Pilot script:
STAGE #1: Haunted Hollow Elf walks across, Ghost walks and looks around. - [Standard background] - Elf enters, stage left - Elf walks across stage, normal speed - Elf exits, stage right - [Short pause] - Ghost enters, stage left - Ghost walks across stage, normal speed - Ghost stops at center of stage - Ghost looks around, twice - Ghost walks across stage, faster speed - Ghost exits, stage right - End of sequence.
The newer cut-scenes I had in mind, however, were much more elaborate; having been conceived during my brief moments of down-time, thinking them up with my wife during our strolls in the park or the woods, they were intended as narrative devices: a series of short vignettes which serve to advance an overarching story. By way of example, consider the following "screenplay" taken also from my design notes:
STAGE #6: Freezer Burn Elf is introduced to Bad Toy. - [Draw a bunch of presents on left edge of stage] - [Draw sign that says "Carol" on top of presents] - Elf enters, stage right - Elf walks across stage, normal speed - Elf stops in front of presents - [Short pause] - [a little head pops up from a box] - [Short pause] - Bad Toy comes out of a box - Bad Toy walks over to Elf - Elf stares at Bad Toy, Bad Toy stares back - [Short pause] - [Bad Toy beeps] - [Bad Toy extends arm to Elf] - Bad Toy "zaps" Elf - [Elf is animated as a glowing skeleton for a frame or two] - Elf is startled - Elf runs away, very fast speed - Bad Toy chases Elf, fast speed - Elf exits, stage right - Bad Toy exits, stage right - End of sequence.
Notice the difference? Well, apart from the fact that it has a bunch more things going on, the main difference is that there is an actual interaction among the characters -- and even with the environment.
That posed a serious problem: If the Auto-Pilot scripts were truly autonomous machines (which they were), controlling the motion of a single object (which they did), and they are invoked and managed independently for each individual object (which they are) ... How on earth can we coordinate the intricate actions that they share together on a single complex scene?
Thinking about it right now, I find it very strange; the Auto-Pilot engine was almost done in by what is a very obvious lack of scope and ambition in its design. I may be a procrastinator of super-human caliber that never gets anything done, but nobody has ever accused me of thinking small, of lacking grandiose vision and ambition. Oh well.
The (Incomplete) Solution
For the very first sequence above, the most simple one, I solved this problem by using P-Machinery's timers from within the game program. Essentially, at the start of the sequence, I set two timers: one timer for when the elf enters the scene to trigger it's Auto-Pilot script; the other delayed just enough to trigger right after the elf leaves the scene and invoke the ghost's Auto-Pilot script.
If I got the timing right, both timers will fire their events at the appropriate time, and the result would look just like if the ghost is following the elf.
Finally, since the ghost was the last one to leave the scene, I set its last script statement to be the Stop command with an argument that points to a "clean-up" routine which actually ends the sequence.
All together, it looks like this:
As you may imagine, this type of work-around would just not do for the more complex sequences, in which there are many more interactions between the characters at a much granular level. A better solution was needed. Enter The Great Big Auto-Pilot Hack.
The Great Big Hack
Well, the obvious solution was to re-imagine the Auto-Pilot framework as a general "scene scripting engine" and, of course, re-write the Auto-Pilot engine itself to address and support the environment as a whole, of which a sprite object is merely one single aspect.
So I sat down one Saturday afternoon, revised all my design documents and worked furiously throughout the night so that by Sunday, I had a brand new, all-encompassing, general-purpose cut-scene scripting engine which supported complex interactions among sprites and the environment, and all!
Ha! Ha! No.
I thought that task would be so daunting and complex that I didn't even consider it seriously, to my everlasting regret. Instead what I came up with was quite an elegant hack. The engine still centered around individual sprite objects, and it still operated on them implicitly, but the hack allowed the individual objects to interact with each other and their environment.
All that raw power (for indeed it was quite raw and ran at a very primitive low-level) was afforded by a single new command added to the Auto-Pilot repertoire: Exec -- the ability to execute external sub-routines directly from a script statement.
In the actual technical implementation, the new command acted as a "call-back" or "event handler," in that the script would execute as normal, one statement at a time, until it encountered an Exec command. At that point it would call the given sub-routine, which would then do its job and dutifully return to the command executor to continue with the Auto-Pilot script.
All of a sudden, it was like you had a means to create custom command executors for absolutely anything you needed!
- The elf reached the middle of the screen and needs to trigger the release of the bad toy? Easy! Just Exec to a special routine which will freeze the elf in wait and invoke the robot's Auto-Pilot script.
- The bad toy finished its initialization and walked over to the elf, which should now cause it to react? No problem! Just Exec to another routine which will re-engage the elf's Auto-Pilot script, et voilà!
Special-purpose custom commands for any occasion and sundry!
A Hack By Any Other Name...
So if it was such a huge leap forward in functionality and such a mind-boggling enabler of creative expression, why do I talk about it as just a hack, suggesting a measure of shame and regret?
Well, because it is a hack, plain and simple. It didn't solve the problem, it just bypassed it -- and instead passed on the complexity of the solution to the programmer (me) rather than encapsulating it in the engine, where it should be. Scripting the sequences stopped being merely a set of discrete statements, and instead became actual low-level Assembly Language programming of each individual action, reaction, and coordination of objects.
It's like trying to fix the heavy congestion of a small country road by giving every motorist a pick, a shovel, and a ton of asphalt, and telling them that now they can build their own lanes; when perhaps the better and more holistic -- and dare I say, correct -- solution should have been to tear it down and build a 5-lane highway instead!
Sure, the motorists can get to their destination, maybe even faster than before; but now they all have to figure out -- on their own -- how to build a road. Worse, they have to peer inside the bureaucracy of rural county government to get a permit, coordinate among each other so that they don't end up destroying other people's lanes, and figure out how and were is best to make their roads.
The county planning commission should have done all that and the motorists should have just arrived one day to find a pristine highway with wide and open lanes for them to drive through.
And if you think that my analogy about local government bureaucracy is confusing because you don't really understand (nor care) about their responsibilities and organization, then ... THAT'S PRECISELY MY POINT. You shouldn't have to.
So there you have it, The Great Big Hack in all its naked and brilliant glory. In the end, I am immensely proud of what I accomplished, and quite satisfied with how I got there. I just still believe it was a missed opportunity and, although it did spark my creativity greatly, it was at the cost of considerable incidental effort. I can't help but think how much more I could have done had the Auto-Pilot framework been a bit more accommodating in its interface, and encapsulating in its abstractions.
To end, I will leave you with the final implementation of the animation sequence described in the second "screenplay" above. I wish to call your attention to the smooth and seemingly natural interaction between the characters, and their reactions to each other's actions. That -- and many other sequences even more complex that this one -- is the direct result, for better or worse, of the Exec command. The legacy, if you will, of The Great Big Hack.
We shall dig deeper into the technical details of each command executor, including the one for the Exec command, in future articles of this series.