One of the first things I did this week was implement support for scripted animation markers. Markers let me attach names to various parts of a sprite so that I can use them in animation and collision detection – for example, lining up a weapon with a character’s hand, or doing hit detection against a character’s hand instead of his entire hitbox.
Once I had some simple markers for the player’s feet added to his animation script, to test them out more thoroughly, I implemented support for attaching simple script triggers to a combination of a marker and a given animation frame, like this:
<SpriteMarker typeId="2"> <Name>Left Foot</Name> <Groups> ... <Group name="run"> <Frames> <Frame anchor="tl" x="80" y="170" index="0" /> <Frame anchor="tl" x="61" y="169" index="1" /> ... <Frame anchor="tl" x="132" y="143" index="6" /> <Frame anchor="tl" x="99" y="173" index="7" trigger="Footstep" /> </Frames> </Group> </Groups> </SpriteMarker>
Using the combination of markers and triggers, I was able to write a simple bit of C# to go with the animation script and spawn small puffs of smoke every time the player’s feet land on the ground during his run animation:
public void Footstep (string foot) {
if (Jumping && !JumpLanded)
return;
var pos = ResolveMarker(foot);
if (!pos.HasValue)
return;
Game.SpawnSmokePuff(Position + pos.Value - GetDrawOrigin(Animator.Frame), 10, 0.5f);
}
The real motivation behind the marker system, however, was syncing animations with the game world – I wanted the player’s grappling hook to extend from his hand, have his melee attacks actually sync up with his animation frames, and have his collision detection change realistically while crouched.
Essentially, where before I used hard-coded coordinates and percentages, now I ‘resolve’ one of the player’s named markers. Resolving a marker either returns coordinates (relative to the top left of the frame) or null, which allows me to determine whether a given frame has a particular marker attached to it. Once I have coordinates for a marker, I compute the player’s origin (used for positioning his animation frames) and use that to compute the on-screen or in-world coordinates for the marker, based on the frame-relative coordinate I already have. Given that, I can sync up things like a grappling hook or a potion with the player’s hand:
public Vector2 GrappleFrom {
get {
var handPos = ResolveMarker("Right Hand");
return Position + handPos.GetValueOrDefault(
new Vector2(0, Obstruction.Y * -0.5f)
) - GetDrawOrigin(Animator.Frame);
}
}
After using this technique to make the player’s bounding box change while crouching, I realized that by uncrouching, the player could become stuck in a ceiling. The solution to this ended up being a bit of a hack – before increasing the size of the player’s bounding box, do a quick collision check to see if the player’s current bounding box is able to move upward by that same amount. If the check fails, I now know exactly how far the player’s current bounding box would have been able to move, and I can constrain the growth of the bounding box to avoid making the player become stuck.
Once the player is out of a tight space, I can grow the bounding box to its full height, restoring sanity. It’s not perfect (and looks kind of stupid since it allows the player’s head to stick through ceilings), but it was relatively simple to implement. I suspect I’ll end up with a different solution in the final game – perhaps forcing the player to stay crouched if he can’t stand up in his current position.
During the process of rigging up the player’s animations, I spent a lot of time fine-tuning things and working out issues. To make things move quicker, I ended up adding a couple new features to my engine:
First, I added support for reloading sprites and tiles at runtime. My existing game code already was designed in such a way that this wasn’t too difficult, but I still had to do some work in order to get the rest of the way. One of the biggest changes was that I had to change entities to store a sprite’s Name instead of storing the sprite itself, so that I could replace a given sprite instance at runtime after reloading it from disk. As a result, where before I had:
this.Sprite.Animations["Walk"].Run(this.SpriteContext);
Now, I have something like this:
Game.Sprites[this.SpriteName].Animations["Walk"].Run(this.SpriteContext);
A bit more verbose, but easy to abstract out using properties and helper methods – such that actual game code can just use convenience methods, like so:
this.PlayAnimation("Walk");
And everything happens behind the scenes.
The other change I made was to overhaul my rendering model in order to make it easier to add debugging overlays, user interface graphics, and special effects without having to change the Game’s Draw function. First, I changed the signature of most of my objects’ Draw methods, such that they now take an extra parameter:
public override void Draw (DrawFlags flags) {
Then, the game code can pass a combination of flags to an object to specify which types of rendering to perform:
if (ShowHUD) {
var flags = DrawFlags.HUD | (GlobalDrawFlags & DrawFlags.DebugHUD);
RenderGeometry(flags);
RenderEntities(flags);
}
As a result, I have the ability to easily toggle off sections of an individual object’s painting code, without having to directly hook the logic into the Game object itself. This also nicely simplifies a lot of the code in my editor, since I can now do editor-related painting (like drawing bounding boxes, selection highlights, etc) in the Draw method of the object in question, instead of having to add a special function to the editor that draws those things manually, or having to add an ‘EditorDraw’ method to the objects (like I had before in a few cases, unfortunately).
With the addition of a couple extension methods to simplify things, it wasn’t too hard to wire things up inside my game objects. For example, this is what SpriteEntity.Draw looks like:
public override void Draw (DrawFlags flags) {
...
var drawPos = GetDrawPosition();
var origin = GetDrawOrigin(Animator.Frame);
if (flags.Check(DrawFlags.Entities))
Draw(drawPos, origin, Scale, Color);
if (flags.Check(DrawFlags.DebugEntities)) {
Game.SetupRenderState(true);
Game.DrawBox(this.Bounds, Color.White);
Game.CleanupRenderState();
}
It simply checks for each of the draw flags it knows how to handle, and processes them accordingly, in a given order. This gives me the ability to do multiple draw passes at once, or do them individually if I want finer-grained control over draw order. A good example of this is the HUD pass – I want HUD elements to cover all the objects in the game world, so I draw them in a separate pass. But debugging overlays like entities’ bounding boxes don’t need to cover everything, so I draw them in the same pass as the entities themselves.
For the curious, the extension methods I ended up writing look like this:
public static bool Check (this DrawFlags drawFlags, DrawFlags flag) {
return (drawFlags & flag) == flag;
}
public static DrawFlags Toggle (this DrawFlags drawFlags, DrawFlags flag) {
return ((drawFlags ^ DrawFlags.All) & flag) | (drawFlags & (flag ^ DrawFlags.All));
}
They’re fairly simple, but having them tremendously simplifies the process of working with the DrawFlags enumeration. This is one thing that I wish C# had built-in support for, but it’s at least fairly easy to add with extension methods. Unfortunately, generic types and enums don’t get along, otherwise you could have generic forms of these functions instead of having to write them for each Flags enumeration in your game.



#1 by Mike on May 23, 2009 - 12:48 pm
Quote
Hi, nice posts there
thank’s for the interesting information
#2 by Derek on May 23, 2009 - 7:47 pm
Quote
Isn’t that hitbox hack potentially dangerous? Won’t it allow you to stand up in a tight passage that the player should be forced to crawl through? I think you have the right idea with needing to check if the new hitbox is collision free before getting out of a crouch or any other type of action that changes your hitbox.
Also, is the pause that happens when you use the grapple a permanent game feature, or is that something you’re keeping in for game testing purposes. I kinda like it since a lot of grapple systems are pretty frustrating because of the difficulty of properly aiming them.
Thanks for the consistent updates. Really enjoying your blog. A lot of this stuff like the collidable enemies are things I want to put in my game. I always thought it was a shame how Castlevania has all these huge bosses, but you can never truly interact and crawl over them because CV just swats you away.
#3 by Kael on May 23, 2009 - 9:44 pm
Quote
@Derek
As you say, it’s potentially dangerous, so I will have to keep a close eye on it to make sure that it doesn’t result in gameplay exploits. For now, the simple solution is good enough since I don’t have anything like that in the tiny amount of game content I’ve built for testing purposes.
As for the pause, that’s a tough one. Initially both the potion and hook paused the game, but the problem is that pausing while aiming is a no-go in cooperative play situations, and I intend to have cooperative play support. I’ve yet to come up with a satisfactory solution for this particular design problem. One potential solution is some sort of auto-aim, but I’ve yet to try it.
The point about bosses constantly swatting you away is a good one – one of the things I’m striving to do with this game is recreate the feeling provided by boss fights like Eligor in Order of Ecclesia, where the boss itself is part of the combat environment and as such, you can utilize all the abilities you normally use to traverse the environment around you, instead of just spamming attacks and dodging projectiles.
#4 by Derek on May 24, 2009 - 4:52 pm
Quote
Interesting boss fight. Aria of Sorrow is the last CV I’ve played(No DS) so I didn’t know about that guy. Still seemed to have a bit of a swatting effect, but kinda neat how you can climb on to it’s back(I think it was the back).
You’re definitely right about the pause being impractical for a coop situation. There was a gameboy game called Ninja Five-0. It had a grapple thing too, but I found it a little easier to manage because everytime you jumped in the game, you would pause at the apex of your jump. The pause definitely helped make it easier to aim the grapple.