Home Artists Posts Import Register

Content

The Lock-On Hat
Back in the Viewtiful Joe Commentary, I talked about programmers as de-facto game designers, since their implementation of certain systems has an implicit effect on the mechanics. I’ve been experiencing this first-hand in subtle ways which are hard to define but now that the state machine stuff is more mature, I think I have a concrete example which shows the intersection of these two fields.

Before going any further, this is still a work in progress so I might change my mind about anything said here. I haven’t sought any outside help (to maintain my impartiality if I ever return to videos) which means I’m probably not a good example to follow. Treat the rest of this post with some skepticism. I’ll try to correct myself at a later time if I have any major regrets about this system.

Since we’re mostly dealing with object types I call Actors, a state in this case is called an Action. Here’s how a fairly complicated Action looks right now.

First of all, note that nothing is really happening here. Even though this is written in GML (GameMaker Language) like the rest of the game, you can basically consider it to be data. The frame-by-frame processing of that data happens elsewhere. All we’re doing in the screenshot is defining how we want the character to behave while in this state. The logic which makes that happen is large and subject to change so I won’t explain it now. That said, I’ll try to do a quick overview of this Action in plain English, line by line. Here’s what it all means:

Clear out the previous Action and set the player’s sprite to sprPlayerSlide.

Set the priority.

Set the duration.

Apply a forward Impulse to the x axis, with a value of 7, only while grounded, with the curve curPlayerSlideAccel, over the full duration of the action.

Apply a friction Impulse to the x axis, with a value of 0.3, only while grounded, with the curve curPlayerSlideFrict, over the full duration of the action.

Play an animation over 30 frames, which advances based on time, holds on the last frame and has a specific curve to it, curPlayerSlideAnim.

Spawn a hitbox between frames 3-999 (end) of the animation, with a specific size and offset. If the hitbox connects with something, perform the SlideHit Action.

Spawn dustcloud particles between frames 3-6 of the animation, with specific probabilities and offsets.

Do not spawn those particles if airborne.

If the Action reaches the end of its duration, perform the SlideRecovery Action.

Lock the player’s x orientation so they cannot turn around while in this state.

By combining all of these things we get the slide you can see in the second state machine video.

For the programmers in the audience: Yes, I know those constructors are quite large which makes the values impossible to understand at a glance, particularly for the hitbox and particle spawners. I tried doing it the other way, by only passing in necessary variables and then manually declaring each option so it would be easier to read. The problem was that I kept forgetting which variables I wanted to set. Eventually I opted to go this direction and I find it easier so far, it’s certainly a tradeoff though. At least the IDE provides me with an argument list so it’s easier to read than it might seem from the screenshot above.

For the non-programmers in the audience: I’m sure this mostly looks like nonsense and you might be wondering why anyone would want their code to be this way so let me emphasize something: Everything that makes a slide a slide is there in that PerformSlide function. When you consider all the things we’re defining, 11 lines of code is not much to ask. In fact it’s hard to imagine how it could be smaller. Of course there’s a whole bunch of supporting code that you’re not seeing but if I sit down at my computer today and want to make a slide move, those 11 lines are all I would have to type.

Naturally, not every state will use every feature. There’s an override system for priorities, room for as many different action transition triggers as needed, different ways to respond to terrain collisions, the ability reverse the actor’s x orientation, use shaders, have an animation oscillate and even more.

On a larger project I would probably try to store this some other way (such as XML) so that a designer could edit it even more easily and do less damage if they mess up. Even better than that, it would be nice to have a custom UI for this stuff since much of what you’re seeing could be presented as a drop-down list, checkbox or slider instead. Still, since I’m the only one that will use it I’m alright with just typing stuff for now. Wearing two hats has its advantages.

How it feels to wear two hats. On a good day at least.

As you might imagine, it takes a lot to get a character moving smoothly, colliding, animating, spawning hitboxes/objects/particles, reacting to hitbox collisions, using shaders and transitioning to other states. Especially with the kind of nuances I’ve built into the impulses and animation. You can get tripped up by all sorts of things. For example, the first pass at the hitbox spawner only tried to spawn the hitbox on the very first frame where it could appear. In other words, in the slide example above, we would only attempt to spawn the hitbox when the 3rd frame of animation appears since that’s when the hitbox is supposed to start. This seems to make sense but imagine a scenario where a player is in the middle of an aerial slash then hits the ground. We want to preserve the animation so we might already be on frame 4 or 5 of the slash. If the hitbox was supposed to be spawned on frame 3, then we would never spawn the corresponding grounded slash hitbox. That's just one example, the point is there are many potential problems which appear if you’re not careful. In fact, much of my time over the past few months has been spent gradually discovering and smoothing out such issues. Thankfully, by taking this data-driven approach, I don’t have to duplicate that work for other actors. If I want an enemy to have a similar slide move, I just write those 11 lines of data and it gets processed by the system properly, with all the bells and whistles. The hope is that this will enable rapid experimentation, thereby leading to more/better/deeper enemies since they’re so easy to create. I’ve made a few enemy prototypes already and it’s holding up so far, long may it continue.

I’m sure there are kinks and missing features (sound is not implemented, although there is a sound shaped hole for it to slot into) but with any luck it’ll escape a total overhaul. Hopefully you now have a decent overview of how it looks at least. With that in mind, I’d like to call your attention to one aspect in particular. Notice how the hitbox spawner and the particle spawner both refer to animation. This reliance on animation more-or-less emerged by itself but I’ve decided to embrace it, partly because it puts a design constraint on the game: Hitboxes, projectiles and particles cannot emerge without an accompanying sprite.

Before I explain what I mean, it’s important to clarify something else. 2D animation fundamentally works like a flip-book or zoetrope. You draw a bunch of pictures and display them one after another to give the illusion of motion. For an animated film, the framerate is usually 24fps and sometimes characters will animate at that exact rate which animators call “animating on ones”. Conversely, much of the time it would be too painstaking to update the character every single frame, therefore the animator will strategically choose when to repeat the same image for multiple frames in a row. If the character stays in exactly the same position for two consecutive frames they call this “on twos” and so on. The 24fps cap of film can already be prohibitively strenuous for animators to hit the “on ones” ideal so the 60fps typical of games is practically insurmountable. Simply put, sprite-based characters end up displaying duplicate animation frames for characters a lot. If you’re still having a hard time picturing what I mean, think of Mr. Game and Watch in Super Smash Bros. Everyone else animates smoothly but he gets stuck in identical poses for long periods of time. The same is true for all 2D characters, Mr. Game and Watch is just an exaggerated version.

This could be an animated gif for all you know.

Now, imagine we’re designing an enemy which throws a projectile. For whatever reason, we already know that we want the projectile to appear exactly 12 frames after the enemy initiates the throw. If the enemy’s sprite doesn’t change between frames 11 and 12, then the state machine cannot spawn a projectile. This is restrictive but it’s also how I think a sprite-based game should work. As a player, visual information is really all I have to understand what state an enemy is in. If an enemy looks identical on frames 11 and 12, then there’s no reason to think that a projectile or hitbox could be spawned there. This rule is built into the state machine at a fundamental level which means if we really want to have that projectile spawn on exactly frame 12, then the animation for that actor needs to be tweaked so their sprite actually changes on that frame or a new sprite needs to be inserted into the animation which plays on that frame. Either way, some kind of visual update must occur with the actor’s sprite, there’s no way around it.

As you can see above, animation is currently considered to be a subset of action, along with many other properties. Animation is special though because hitboxes, projectiles and particles are tied to it. While I imagine a 3D engine would be similar, the nature of 2D animation really clarifies something about the way visuals inform a player’s understanding of the mechanics. Of course we can still update actor positions on every frame since we want the smoothest possible motion but it makes sense to me that nothing else should be able to happen while the sprite remains the same. Players deserve an updated sprite if we’re about to throw a projectile or hitbox at them. This is the overlapping of hats because I approached this topic from the programming side but it’s really a design problem.

I must admit, I had some hesitation about taking this approach. Partly because programmers are generally advised to avoid this sort of coupling wherever possible and partly because the word “animation” seems sort of frivolous, perhaps because it’s most commonly associated with light-hearted animated films. There’s a temptation to think that updating the player’s sprite is really no big deal - just a visual flourish - but at this point I’m convinced it’s a crucial piece of the mechanical puzzle. I suspect there are many games which work this way, many which don’t and many which had this intention but suffer from mistakes. If you rely on a person to manually align each sprite change with each hitbox then you will inevitably end up with mismatches, especially as the number of enemies/actions increases and things get tweaked. It was important to remove that possibility.

One reason I find this topic interesting is because, if I were just the designer, I could now create character actions without ever considering this constraint. A designer using my system might even become annoyed that they can’t just type exactly when a projectile will spawn in absolute terms, it could be seen as finicky. In reality, this almost certainly saves work in the long run since actions can have their duration adjusted by changing a single number and more importantly, it’s much more consistent with less room for human error.

That's the theory anyway. I’ll know soon enough whether this was all a waste of time because the next step for Betelgeuse is to implement enemies. There are still many minor issues on my To Do list and I’m not sure I’ll ever be totally satisfied with collisions but it would now be more valuable to press ahead with generating content so we can see if this thing has legs. As I outlined previously, I consider Betelguese to be the safest bet of the four, at least mechanically speaking. I don’t anticipate abandoning it any time soon but if the worst comes to pass then the knowledge and codebase I’ve developed should prove useful for other projects too. Whatever the outcome, I’ll let you know.

Comments

Anonymous

I appreciate you linking everything to animation. As someone who often plays games based off aesthetic, visual queues are very important to me.