Posts Tagged platformer

Two-character mechanics (#2) and tilesets

I spent most of my time this week working on editor improvements, and more two-character support. One of my first steps was to pull code out of the Player class into unique classes for each of the player characters. As a result, only one of the two characters can use a grappling hook, as intended. Next, I need to start building the other character’s special ability :)

The editor work mostly went into loading art assets and properly handling levels with missing assets, since i’ve been getting more tiles from my artist of varying sizes and shapes. I ended up building a simple XML-based file format for describing tilesets, based on my serialization framework, so that I can easily point the engine at multiple textures to pull tiles from.

It works pretty well, despite only being a couple hundred lines of code, and can easily be adapted to work with the XNA content pipeline, so it’s likely I’ll stick with it. I’m considering adapting the approach to also work for character sprites, since right now I have to manually tag sprite frames with specially-colored pixels in an image editor, which is extremely awkward and easy to get wrong. Here’s a snippet from one of my tilesets:

            <TileStrip key="7" typeId="1">
                <Id>prison_column_ornate_{index}</Id>
                <Filename>prison_columns.png</Filename>
                <TileSize width="32" height="208" />
                <Border left="0" top="0" right="384" bottom="0" />
            </TileStrip>
            <TileStrip key="8" typeId="1">
                <Id>prison_column_wood_{index}</Id>
                <Filename>prison_columns.png</Filename>
                <TileSize width="24" height="208" />
                <Border left="100" top="0" right="292" bottom="0" />
                <Margin left="0" top="0" right="8" bottom="0" />
            </TileStrip>

Simple enough syntax to be easy to write by hand in a text editor, but also not too difficult to load at runtime. I’m pretty happy with it.

One of the challenges in making use of the new art assets is getting all my tiles to line up properly. They have a large variety of sizes, so the algorithm I’m currently using to select tile positions isn’t a very good fit for my needs. Right now I’ve been having to make minor adjustments to my tile positions after I place them, but in the long run I’m going to want to come up with a tile placement technique that works well for tiles of all sizes.

Tags: , , ,

Two-character mechanics #1

Spent most of this week implementing the basic framework for having two player characters. A lot of code ended up needing changes since it had assumed the existence of a single player character – things like saved games, the grappling hook, etc. By far the hardest parts to get working properly were user input and collision detection.

Getting the inactive player character to properly handle input took some significant changes since I previously assumed that I could just poll the state of the joystick – now, a class called the PlayerManager sits between the two Player instances and objects like the joypad in order to make sure that only the active player receives input events. Collision detection also required some major changes in order to allow players to avoid colliding with each other without breaking any other behaviors that depended on collision detection, like gripping onto ledges or riding platforms.

Now that I have the basics working, I plan to start working on the initial implementation of the secondary character’s AI, so it can follow you around and traverse the environment by jumping and climbing up onto ledges. I still have yet to start working on the code for the secondary character’s abilities and attributes, so right now both characters are basically identical.

Tags: , , ,

Gruedorf mini-update #2

Another slow week. Most of my work went into moving to the new object model discussed previously – splitting bits of data and logic out into the Level and RuntimeLevel classes, fixing bugs as they cropped up, and so on. One thing I had to do in order for that to work that I hadn’t anticipated was change my object model so that every object in a given level had a unique name. Previously names were optional, but in the new object model it didn’t really make sense for objects to not have a name, or share the same name. After resolving that, some other issues cropped up – for example, bounding boxes weren’t updating correctly for some moving objects like doors and moving platforms, which meant that in some cases an entity could slip through an obstruction.

Once the issues resulting from the switch were resolved, I started making some improvements to my moving platform code so it was easier to put to work. The first problem was that the use of cubic interpolation caused platforms to move at an inconsistent speed – slowing down as they approached waypoints on their path, and accelerating as they grew more distant. I spent a little time experimenting with various approaches to solving this, and ended up settling with a simple hack, where I do a binary search of the curve to find the next point along the curve that is a given distance away from the current one. While not mathematically pure, it was easy to build and solves the problem, and after implementing it I was surprised by how much better the motion of the platforms looked as a result of them moving consistently.

After this I spent some time improving the editor’s support for dealing with objects that move, making it possible to see a simple visualization of their path so you can get an idea of possible collisions and where it’s going to move.

Tags: , , ,

Gruedorf mini-update: Moving platforms and design changes

A nasty bug going around last weekend kicked my ass, so not too much to report:

Been making large architectural changes to clean up my object model, so that things like moving platforms can work correctly without too much hassle. In summary:

Previously, I had a Level class that contained all the important objects relevant to a game level. Some of these objects had ‘Runtime’ counterparts that you had to construct by calling a relevant method, for example:

Level.Entities contains LevelEntity objects, which have a GetRuntimeEntity method that returns a (Runtime) Entity object based on the LevelEntity. To call this method, you have to pass in a Game object.

There are some major problems with this approach – the need to call GetRuntime methods complicates iteration over these containers and means that Game objects have to get passed as arguments to basically any function that needs runtime objects, which is a tremendous hassle.

In order to address these problems, I’ve decided to split the Level class out into two objects: Level and RuntimeLevel. Level will contain only ‘design-time’ objects that have no dependencies on each other or on the Game object, while RuntimeLevel will be constructed from a Level by the Game and contain preconstructed run-time versions of all the objects from the Level. This will allow me to simplify lots of code by simply walking over the contents of the RuntimeLevel when I want run-time objects, and walking over the Level when I want design-time objects. It also nicely splits out some responsibilities – previously, Level had to subdivide objects like entities and geometry based on their boundaries, for efficiency purposes, which made the class far more complicated than was necessary for level editing. Now, that responsibility falls on the RuntimeLevel, so Level can just use a Dictionary to store its objects for easy lookup.

Regardless, I did get some additional functionality built since last week’s post. Here’s a video of two moving platforms following some simple paths I built using the editor’s node tools (you may notice some minor collision detection glitches; these are caused by the platforms having overlapping bounding boxes, and will be fixed by the move to a RuntimeLevel class.)

Tags: , , ,

Pathing and graphs

Mostly internals hacking this week. I started working on support for placing nodes in levels, so that I can define paths for objects like moving platforms to follow, and build graphs for the player AI to use for pathfinding.

Linking an object like a moving platform or a monster to the pathing data is an interesting challenge. The list of objects in the level and the graph that represents the pathing data are both in entirely separate objects, with no straightforward way to acquire references to each other. To complicate this further, there’s no way to guarantee that all the pathing nodes an object depends on will have been loaded before the object. This basically means that I need to go to some sort of two-stage initialization model, which is a bit of a pain.

One of the problems that this causes is that the algorithm I use to partition a level’s geometry becomes fairly useless, since it depends on knowing the boundaries of a given piece of geometry. If a moving platform is attached to a series of 8 different pathing nodes, the only way to compute the possible boundaries of the platform is to walk all eight nodes and compute a rectangle that contains all of them – probably large enough to be utterly useless for optimizing collision detection. Doors have a similar problem, but due to the fact that they move on a fixed axis it’s less problematic. This probably indicates that I need to move to a model where an object’s bounds can change on the fly as it moves, and will require some significant changes to my object model.

I also still need to come up with a good strategy for attaching metadata to the pathing nodes. Some nodes need to have information attached directly – for example, a given node might be attached to a door, so the game needs to know that the node might not be traversable depending on whether or not the door is closed. However, some data needs to be attached to the connections between nodes – for example, if I have one node on the ground and another node up in the air, the connection between those two nodes needs to indicate that the player gets from one node to the other by jumping.

If I attached that data to the nodes directly, then I would end up with a situation where the only way to move from a given node is by jumping, even if it’s possible to walk onto it from one direction, jump onto it from another, and fall onto it from above. This does still have some ambiguity issues regardless, though, since if you can get to a node by jumping onto it, the reverse action (going backwards) just involves falling, not jumping. In this case I need some way to represent connections where the action you take depends on the direction you’re going.

Tags: , , ,

Platforms, particles and precision

Not a whole lot interesting to show for this week; mostly some performance adjustments and tuning changes.

I spent a day or so wrestling with some weird floating-point accuracy issues. In one case, a routine I was using to convert a convex polygon into a list of triangles wasn’t working correctly in Release builds of the game – rounding errors resulted in a point being represented as outside a triangle when it was actually right on the edge, which ended up causing the routine to iterate forever. (Whoops.)

After a little experimenting, I ended up changing all the variables in the point-in-triangle check routine to use double precision floats, which fixed my test case. Unfortunately, I’m still pretty sure that the routine is broken; doubling the precision just reduced the magnitude of the error. This is particularly annoying since I have had trouble with rounding errors in my collision detection routines in the past, and I still haven’t found a robust approach for deailng with those either. I suspect I’m going to have to spend a while reading up on techniques for reducing error. One upside is that I haven’t seen any rounding errors while running the game on the 360, so if all goes well I won’t ever have to debug PowerPC floating point error issues on my x86 development machine – my understanding is that PPC’s instruction set for floating point arithmetic is far less sensitive than the x86′s, so I’m hoping it won’t be a problem.

I also did a little bit of experimenting with improving the quality of particle effects in the game. I recently added a little effect that causes one-way barriers to emit sparks when their passable axis changes, so that the player’s attention is drawn to the change to avoid confusion in puzzles that rely on barriers. It was pretty, but it also made it obvious that sparks were flying through walls and other geometry, which looked kind of ridiculous. So, in order to address that, I did some work on making sparks not spawn inside of geometry, and making them able to bounce off walls. The end result looked a lot more convincing, but had some unfortunate performance consequences I haven’t managed to get a handle on yet – I spent an hour or two trying to optimize the code responsible for bouncing sparks off walls, and basically got no performance improvement out of it whatsoever; I suspect the profiling I did generated incorrect data for some reason.

Implementing support for sparks bouncing off walls involved some changes to the UpdateSpark routine, mostly the addition of an obstruction test using a new type of obstruction resolver:

        protected bool UpdateSpark (ref SparkParticle particle, int index) {
            particle.Opacity -= 0.01f;

            _ParticleResolver.position = particle.Position;
            _ParticleResolver.velocity = particle.Velocity;
            _ParticleResolver.Reset();
            var result = ObstructionTest(_ParticleResolver, ObstructionFlags.Geometry);
            particle.Position = _ParticleResolver.resultPosition;
            particle.Velocity = _ParticleResolver.resultVelocity;
            if (result == null) {
                particle.Velocity.Y += 0.025f;
            }
            particle.Rotation = (float)(Math.Atan2(particle.Velocity.Y, particle.Velocity.X) + (Math.PI / 2.0f));

            return (particle.Opacity <= 0.0f);
        }

Fairly simple in concept; perform an obstruction test between the particle and nearby level geometry, and use that to determine the new position of the particle. The new resolver looks like this:

    internal class ParticleMotionResolver : ICollisionVisitor {
        public Vector2 position, velocity, resultPosition, resultVelocity;

        public void Reset () {
            resultPosition = position + velocity;
            resultVelocity = velocity;
        }

        public Bounds GetBounds () {
            return new Bounds(position, resultPosition);
        }

        public CollisionState VisitPolygon (Polygon poly) {
            Vector2 intersection;
            bool result = false;

            for (int i = 0; i < poly.Count; i++) {
                var edge = poly.GetEdge(i);
                var normal = Vector2.Normalize(edge.End - edge.Start).Perpendicular();

                if (Geometry.DoLinesIntersect(position, resultPosition, edge.Start, edge.End, out intersection)) {
                    result = true;
                    velocity -= (intersection - position);
                    position = intersection;
                    Vector2.Reflect(ref resultVelocity, ref normal, out resultVelocity);
                    velocity = Vector2.Normalize(resultVelocity) * velocity.Length();
                    resultPosition = position + velocity;
                    if (velocity.LengthSquared() <= 0)
                        return new CollisionState(true, true);
                }
            }

            return result;
        }

        public CollisionState VisitCollidable (ICollidable obj) {
            if (velocity.LengthSquared() <= 0)
                return new CollisionState(true, true);
            else
                return CollisionState.True;
        }
    }

Fairly straightforward, really: For every polygon within the particle’s bounding box, I test the particle’s velocity vector against the polygon’s edges, to see if the particle is going to intersect the edge. If it is, I move the particle so that it’s lying directly on the edge, and then compute a reflection vector so that I know which direction it’s going to bounce in. After that, I apply the particle’s remaining velocity (if any) to bounce it off the edge, and change its direction.

This new routine has the unfortunate side effect of revealing some of the rounding errors lurking in my code – sparks are able to slip through some curved surfaces since the triangles that make up their edges are so small. The end result was definitely an improvement regardless, though.

The last significant thing I worked on was getting moving platforms working correctly again. While moving platforms were one of the first things I implemented originally, A few weeks back I ended up taking them out because they didn’t make sense with my current collision detection model, and they were a constant hassle. Now that my collision detection is more sane, it was easy to adapt the existing code for doors to create a moving platform class that behaves more or less the way I want: You can grip onto the edges of moving platforms just like ledges without falling off; you can climb up onto them; you can ride them; etc. I had to do some tweaking here and there to ensure that you couldn’t ride moving platforms through geometry that’s supposed to obstruct you, and there are still some usability issues – right now the only way to control a platform’s movement is by writing complex vector expressions, which isn’t fun at all; good moving platforms really require a system for defining paths for them to follow, and that’s still on the todo list. Regardless, it’s nice to have platforms working again, since they’re an essential part of my arsenal for designing good puzzles.

Tags: , , ,

DevHouse and sundry

Last weekend, I went to SuperHappyDevHouse 30 with some friends and acquaintances. In between checking out other people’s projects, chatting about technology, and devouring delicious snacks, I got a lot of work done on my editor. The guy next to me with the hat and goggles showed off his extremely clever browser-based zombie MMO and exchanged war stories with us about the horrors of PHP.

The main thing I worked on was getting support for multiple-object selections in the editor, so that you could select multiple tiles or pieces of geometry and manipulate them all at once. After some heavy refactoring, I got it all working, making it possible to edit a property of multiple objects or reposition/delete multiple objects at once, by leveraging some of the MultiSelect support built into the WinForms PropertyGrid class for property editing and extending my existing object manipulation code to work on sequences instead of single object references. Having multiselect makes it much easier to make batch modifications in levels and quickly populate an area with objects, and is a precursor to good clipboard support.

One of the challenges involved in getting this working was the need to improve the logic behind operations like tile movement – previously, I did some work to ensure that the editor would always try and select a non-overlapping location for a tile whenever possible, even while dragging, so that it would be easy to build seamless backgrounds and objects out of individual tiles. Getting this logic to work correctly for grouped selections of tiles was a bit of a challenge and required carefully excluding certain tiles from some intersection tests and not others.

After that, I started doing some work on being able to build more varied puzzles out of the primitives I already had in the engine – making sure that doors could open in all four directions correctly, and making it possible to manipulate entities and non-colliding geometry like switches and pressure plates.

Later in the week I spent a lot of time refactoring code, moving classes around, factoring out duplication in methods, and tweaking and fine-tuning various interfaces. While making these changes I experimented with some basic ideas for puzzles and level design challenges, creating things like sequences of cascading doors or quickly-moving platforms.

After experimenting with puzzle designs for a while, I realized I really needed to get a working implementation of crushing damage and get proper support for entities being pushed around by moving geometry – moving platforms and quickly moving doors simply don’t play well unless they are able to collide with entities and move them around, and being crushed by a door isn’t very threatening if it deals no damage.

Originally, supporting these two things was a bit of a challenge – the rigid nature of my collision detection routines meant it would have been prohibitively expensive and very difficult to get right. As a result of the refactorings I’ve made in the past few weeks, however, it ended up being easy – the Visitor pattern meant that implementing this was as simple as extending the code I already had with a specialized collision visitor for moving level geometry:

internal class RuntimeGeometryMotionResolver : AbstractPolygonMotionResolver {
    public struct CrushedEntity {
        public Entity Entity;
        public float CrushDistance;
    }

    public RuntimeGeometry Geometry;
    public Entity Entity = null;
    public List<CrushedEntity> CrushedEntities = new List<CrushedEntity>();

    public RuntimeGeometryMotionResolver (RuntimeGeometry geometry) {
        Geometry = geometry;
    }

    public void Reset () {
        CrushedEntities.Clear();
    }

    public override CollisionState VisitCollidable (ICollidable obj) {
        if (obj == Geometry)
            return false;
        if (objectVelocity.LengthSquared() <= 0)
            return new CollisionState(true, true);

        Entity = obj as Entity;
        return true;
    }

    public override CollisionState VisitPolygon (Polygon poly) {
        if (Entity == null)
            return base._VisitPolygon(poly, ref objectVelocity, out objectVelocity);

        Vector2 resultVelocity;
        var result = base._VisitPolygon(poly, ref objectVelocity, out resultVelocity);
        var pushDistance = objectVelocity.Length() - resultVelocity.Length();
        var pushVector = Vector2.Normalize(objectVelocity) * pushDistance;

        var pushedVector = Entity.ApplyMotion(pushVector);
        var pushedDistance = pushedVector.Length();
        var obstructedDistance = pushDistance - pushedDistance;

        if (obstructedDistance > 0.0f) {
            CrushedEntities.Add(new CrushedEntity { Entity = Entity, CrushDistance = obstructedDistance });

            var resultDistance = Math.Max(objectVelocity.Length() - obstructedDistance, 0.0f);
            if (resultDistance <= 0.0f) {
                objectVelocity.X = objectVelocity.Y = 0;
                return new CollisionState(true, true);
            } else {
                objectVelocity = Vector2.Normalize(objectVelocity) * resultDistance;
            }
        }

        return new CollisionState(result.Value, false);
    }
}

Larger than I might like, but actually quite low on duplication, and basically a complete solution to the problem. Here’s a basic overview of how it works:

The visitor walks over all the objects in the game world that the geometry can possibly collide with, keeping track of the current velocity of the geometry and a list of all the objects it’s already collided with. Each time it encounters an object (VisitCollidable) it does some basic checks to rule out things that can’t possibly be collided with (for example, itself).

After that, it walks over each of the polygonal shapes in that object’s CollisionList and performs a basic motion resolution operation against that polygon, to determine how far the geometry can move in its desired direction without a collision. This provides a vector that can be compared against the original velocity of the geometry, to determine how far it travelled before colliding with the object. At this point, it then attempts to push the object it’s colliding with far enough to resolve the collision, preserving the geometry’s desired velocity.

If it successfully pushes the entity all the way, the entity is safely moved out of the path of the geometry and no crushing damage occurs. This nicely handles almost all of the possible situations involving collisions between moving geometry and entities, because the entity performs its own collision checks when attempting to apply the motion. If the entity cannot be pushed all the way, the visitor can perform a final calculation to find out how much motion was prevented by a collision between the entity and some other object, and at that point, the visitor adds an item to its results list recording the entity that was struck and how much force was applied to it.

Once this visitor has made a pass over all the possible candidates (or the geometry’s velocity has been reduced to zero), it has a list of objects to deal crushing damage to, and the game can continue. In practice, geometry will typically only be obstructed by a single entity, but in cases where multiple entities are all being pushed by a single large object, keeping track of a list helps make things behave predictably. The performance characteristics of this technique are quite good due to the optimizations already built into the visitor system, which makes it extremely cheap to put lots of moving geometry in a level that’s capable of dealing crushing damage.

One bug I encountered while implementing this technique was a particularly nasty edge case where the geometry’s velocity would be reduced to 0, but it would continue on its collision detection pass. It would then perform motion tests against other objects without having a velocity, and the resulting arithmetic would generate NaNs that then propagated out through the game code, resulting in any entities near the moving geometry getting teleported into nothingness.

NaNs are always painful to deal with and important to avoid, and it took me nearly an hour to finally track down this issue and make sure it wouldn’t happen again. In retrospect I should have anticipated this sort of thing to begin with, as a result of the approach to collision detection i’m using, and it’s a bit of a surprise that I haven’t had it happen before. As a result I’m going to be leaving some IsNaN checks in a few key parts of my code in order to be certain that an exception is thrown as soon as a NaN is generated, so that I’ll be immediately alerted to problems in my code without having to spot subtle issues. If you look carefully, you can actually see a monster being teleported to {NaN, NaN} in the above video.

A few of the other things I implemented this week included basic clipboard support in my editor (cut/copy/paste) and some UI refactorings that eliminated the mostly useless menu bar and added toolbar icons to reduce the amount of screen real estate used by the editor’s chrome.

Clipboard support ended up being much easier than it was in any of my previous tools development projects, due to the fact that I had already built an extremely usable general serialization framework, and the fact that I had already cleanly separated design-time and run-time game data. The WinForms Clipboard API kind of sucks, though.

A couple other random gripes, while I’m on the topic of ‘kind of sucks’:

XNA’s default Game implementation on Windows seems to entirely suppress all Win32 keyboard messages, which makes it difficult to accept text input in an integrated editor like mine. I don’t really blame them, but I wish I had realized this sooner – I had to compromise and move my property grid to a floating dialog in order to allow the user to input text into it, which is not a particularly amazing user experience.

PropertyGrid requires a saddening amount of boilerplate code to be written before you can just point it at a typical piece of game data – by default, it won’t display anything except public properties, and it makes it really difficult to provide a good UI for custom types by requiring you to do an elaborate song and dance involving TypeConverters. I ended up spending a few hours hacking together around 700 lines of code just to get it to properly handle editing fields of various types. On the other hand, multiselect pretty much worked out of the box, so it was still faster than trying to write my own property editor.

Tags: , , , ,

More entities and optimization

Most of my work on the platformer this week has been focused on improving the Entity class and all the related code. I made a number of changes and improvements to SpatialCollection aimed at improving its performance, and completely overhauled all my collision detection code to reduce the number of passes I make over the level when doing things like computing an entity’s StandingY or testing for a collision. The efforts have mostly paid off so far; my framerate with 6 entities walking around on curved surfaces right now is about the same as it was before with a single entity walking around on sloped surfaces, even though I’ve improved my collision model significantly. The optimizations ended up bringing my game’s performance profile back to where it was before the addition of entities, with the majority of CPU time being spent doing raw number-crunching to perform intersection checks on polygons.

My previous approach to collision detection was fairly brute-force: I had a few special methods defined in the Game class named things like FindObstruction and ResolveMotion, that took a bunch of parameters to control a collision detection sweep of the entire level. The addition of SpatialCollection<T> as mentioned in last week’s post made those functions faster, but that didn’t completely eliminate the performance issues – the most visible offender was ComputeStandingY. The original implementation of ComputeStandingY had to make four complete passes over the level to come up with a result, and I ended up invoking it twice per frame when updating an entity. The end result was that no matter what, it accounted for a significant amount of my CPU usage, no matter how efficient I made the underlying collision detection routine.

In order to address this problem, I set out to refactor my collision detection code. The first step was to unify the different collision test routines – they all shared many common elements, the most obvious one being the task of walking over the contents of the level. Using SpatialCollection meant that each routine had about 8 lines of boilerplate in order to do iteration, in addition to all of the code for actually performing collision tests, so my first step was to factor that out into a baseline ObstructionTest function that performed the simple task of walking over the contents of the level and invoking a ‘Collision Visitor’ delegate on each item.

    public ICollidable ObstructionTest (ICollisionVisitor visitor, ObstructionFlags flags) {
        ICollidable result = null;
        var bounds = visitor.GetBounds();

        if ((flags & ObstructionFlags.Geometry) == ObstructionFlags.Geometry) {
            using (var e = Level.Geometry.GetItemsFromBounds(visitor.GetBounds()))
            while (e.MoveNext()) {
                var current = e.Current;

                if (!Bounds.Intersect(ref bounds, ref current.Bounds))
                    continue;

                var rg = current.Item.GetRuntimeGeometry(this);

                if (visitor.Visit(rg, ref result))
                    return result;
            }
        }

        if ((flags & ObstructionFlags.Entities) == ObstructionFlags.Entities) {
            if (bounds.Intersects(Player.Bounds))
                if (visitor.Visit(Player, ref result))
                    return result;

            using (var e = Entities.GetItemsFromBounds(bounds))
            while (e.MoveNext()) {
                var current = e.Current;

                if (!Bounds.Intersect(ref bounds, ref current.Bounds))
                    continue;

                if (visitor.Visit(current.Item, ref result))
                    return result;
            }
        }

        return result;
    }

After that, I refactored all the existing routines to be built on top of ObstructionTest. After that I iteratively refined things down until I didn’t need any of the specialized routines anymore, and ObstructionTest ended up operating on specialized ICollisionVisitor objects that had two ‘Visit’ methods, one for ‘Collidable Objects’, and one for individual collision polygons. The pair of visit methods allowed a visitor to reject entire objects before any complex collision tests were performed, in addition to rejecting individual polygons due to non-collision. The use of visitor objects also meant that a visitor could record a list of the objects it encountered, or reject collision with an entire list of entities instead of just the entity that was performing the check. These improvements not only resulted in better performance, but they allowed me to address a bug in the Entity class that prevented entities from walking up sloped surfaces while the player was standing on them.

    public class StandingSurfaceVisitor : ICollisionVisitor {
        public Entity Entity;
        public Bounds Bounds;
        public List<IStandable> Results;

        public StandingSurfaceVisitor (Entity entity) {
            Entity = entity;
            Results = new List<IStandable>();
        }

        public Bounds GetBounds () {
            return Bounds;
        }

        public CollisionState VisitCollidable (ICollidable obj) {
            if (obj == Entity)
                return new CollisionState(false, false);

            var standable = (obj as IStandable);
            if (standable != null)
                Results.Add(standable);
            return new CollisionState(false, true);
        }

        public void Reset () {
            Results.Clear();
        }

        public CollisionState VisitPolygon (Polygon poly) {
            throw new NotImplementedException();
        }
    }

One of the other things I invested some time into was an overhaul of the logic for standing on surfaces. Previously, the Player class had a StandingOn property that held one of the entities/surfaces he was currently standing on. I used this to determine whether the player was standing on a pressure plate, and cause him to move with things he was standing on. The problem was that in almost all cases the player would be standing on multiple surfaces, so the nondeterministic nature of the collision detection code meant that sometimes you could be atop a pressure plate but not trigger it. I also had no easy way of determining whether entities were standing on top of each other, which meant that an entity being ridden by another entity could not walk up a sloped surface (his path would be blocked by his rider).

To solve those two problems, I defined a pair of simple interfaces called IRider and IRideable. These interfaces provided a simple way for me to manage all the logic around riding surfaces – entities could automatically build and update a list of rideable objects that they were currently occupying, and rideable objects were able to automatically maintain a list of rider objects on top of them, making it simple to exclude those riders from collision checks. This not only fixed pressure plates, but also made it easy for monsters to set off pressure plates, and meant that monsters could walk up sloped surfaces while being ridden by other entities, and that you could stack entities on top of each other to create big riding chains without any major issues (though in the video below, it was still unfinished and somewhat busted):

One of the other fixes I was able to make as a result of the new system for handling rideable surfaces was to the code for mantling up onto surfaces. Previously, if you tried to mantle up onto a moving surface, like a moving platform or an entity, you’d mantle up onto the space it previously occupied and typically fall right back down, making it pretty useless. With the new riding system I was easily able to adapt this so that you could mantle up onto a moving surface without being left behind by its motion or causing it to become obstructed. Since the mantling code doesn’t currently distinguish between different types of surfaces, this means you can basically crawl up on top of a crowd of enemies and run across it, which is pretty fun.

The video below shows how entity mantling looks, though it’s from before I implemented the riding system (so you can see it get broken in a couple places):

Tags: , , , ,

Animation and combat

Over the past week I’ve spent most of my time working on animation and basic support for combat, along with a little time invested in performance optimizations to keep the game running well on the XBox.

My initial approach to animation for the player character was to use a single task that ran in the background, periodically updating the player’s animation frame and then going to sleep for a fraction of a second. It worked, but writing all of the player’s animation logic as a single task became cumbersome very quickly – I essentially was writing a very large, very complex state machine that also had to run concurrently with the rest of the game code. The idea of adding combat animations simply made this approach a non-option – it was too complicated.

As a result, I decided to toss the animation task I currently had and start fresh with a different approach. I ended up test-driving a very simple class designed for handling framerate-independent animation playback, called an Animator. The Animator‘s responsibility is to update the currently playing animation and handle transitions to new animations. Animations are written as generators, like the original animation task, but instead of using one giant generator for all the animations, or a generator for a set of animations, each individual animation is its own generator, only a few lines long.

Every frame, the Animator ‘steps’ the current animation’s generator repeatedly until it either suspends (returning an amount of time to suspend it for, so that the Animator knows when to wake it up next), or hands off control to a new animation. In the event that a new animation is selected, the Animator switches over to it and continues stepping it. This allows for complex animation logic to work correctly regardless of the current framerate, and makes it fairly simple to isolate the details of each animation from other animations. Having the Animator responsible for the current animation means that I can also easily interrupt an animation that’s in progress, even if the animation is suspended – something I couldn’t easily do when the entire animation system was a single task (mind you, it was possible with that approach – just a pain.)

The end result is that the player’s animations now look something like this:

        protected Anim Animation_Walk_to_Stand () {
            return Animation_OneShot(AnimId_Walk_to_Stand, 50 * Time.MillisecondInTicks)
                .Chain(Animation_Stand);
        }

        protected Anim Animation_Backdash () {
            var a = Animation_OneShot(AnimId_Backdash, 40 * Time.MillisecondInTicks)
                .Chain(Animation_Stand);

            using (Finally.Do(() => {
                Backdashing = false;
                Velocity.X = 0.0f;
            }))
            while (a.MoveNext())
                yield return a.Current;
        }

        protected Anim Animation_Walk_Turn (int direction) {
            return Animation_OneShot(AnimId_Walk_Reverse, 60 * Time.MillisecondInTicks)
                .SwitchIf(Animation_Walk, () => Facing != direction)
                .Chain(() => Animation_Wait()
                    .SwitchIf(Animation_Walk, () => Facing != direction)
                );
        }

While they’re still somewhat complicated, they individual animations are much more isolated from each other than they were previously, and it’s much easier to debug and fine-tune animations than it was before.

Functions like Animation_OneShot are predefined ‘animation templates’ that take a GroupID that specifies a set of frames to play, along with a rate at which to play them. The animation template is a generator that simply yields one frame at a time, followed by the specified delay, until the animation is complete. This has the nice advantage of being composable – you can add additional logic onto an existing animation by wrapping it with another generator that yields values from the inner generator after preprocessing them, like in the Animation_Backdash function above. This also gives you a way to express logic that should be executed at the beginning and end of an animation, even if the transition is abrupt (thanks to the IDisposable pattern).

The Chain and SwitchIf functions are extension methods that operate on animation generators by wrapping the generator with a new one and returning it.

Chain‘s behavior is fairly simple – at the end of the animation, control is transferred to a new one automatically, by evaluating a function passed in as a parameter. Passing the new animation in as a parameter allows dynamically choosing a new animation based on the current state of the game, or passing control off to an animation stored in a variable.

SwitchIf is slightly more complicated – it evaluates a condition (passed in as a function) on every step of the wrapped animation, and if the condition is true, it immediately switches to a new animation, without waiting for the current one to stop. This gives you an easy way to express immediate transitions between animations without having to manually detect state changes in your game code and explicitly force a transition. This is important for doing things like switching between a standing and walking animation based on the player’s velocity – you could explicitly call the Animator.SetAnimation method to switch to the walk animation, but doing that would require you to keep track of the player’s previous velocity and detect when he’s beginning to walk, which is a real hassle. Using SwitchIf allows you to express that logic like this instead:

        protected Anim Animation_Stand () {
            return Animation_PingPong(AnimId_Stand, 50 * Time.MillisecondInTicks)
                .SwitchIf(Animation_Stand_to_Walk, () => Acceleration != 0)
                .SwitchIf(Animation_Grapple, () => Grappling);
        }

The main advantage here is that the SwitchIf clause is only evaluated while the player is currently standing, so you don’t need to keep track of any additional state. The primary downside to this approach is that it’s possible for the player’s animation to get into a ‘dead-end’ state, where there are no active SwitchIf clauses. In this case, the only way to restore the player’s animation to normal is to explicitly call Animator.SetAnimation – kind of annoying, but not any worse than the old approach. Luckily almost all my animation logic so far is easily expressed using switch conditions, so I rarely run into the dead-end problem.

Once I had this working, I went ahead and implemented a basic punching attack for the player character, along with a castlevania-style backwards dash move. Those two additions combined with the basic movement and jumping already implemented gives the player enough control to have fairly interesting fights with enemies (assuming that the enemies actually put up a fight, at least…)

The next step was to put something in that’s worth fighting with. Towards that end, I spent a few hours making lots of refactorings to my game code – pulling logic for things like movement, animation, collision detection, and so on out of the Player class and into a new base class called Entity. This required generalizing some of my existing code, and I had to fix a couple bugs I hadn’t noticed along the way, but when I was done the player’s movement code ended up a lot simpler and I was able to create a monster entity with a few dozen lines of code.

I created a new Monster class derived from Entity and added some basic AI to it: the monster walks forward in a given direction until it encounters the player or encounters an obstacle. When it encounters the player, it stands still and attempts to hit the player with its sword until the player is gone. When it encounters an obstacle, it reverses direction. Not particularly challenging to fight, but enough to be worth testing out.

One of the main details I had to tackle was how to detect a collision between the monster’s sword and the player, or the player’s fist and the monster – what I ended up doing was adding an AttackShape field to the Entity class that can optionally contain a polygon. If the field contains a polygon, every frame it will automatically be checked against other entities in the game for collision, and if a collision occurs, the entity is added to a temporary list and an ‘attack landed’ event is fired. The temporary list allows me to avoid dealing damage to an entity multiple times with one attack, so that all you have to do is hit your target with any part of your attack animation. Using a separate shape for an entity’s attack turns out to be a good idea, because it makes it straightforward to create entities that don’t deal damage on contact, which gives the player a lot more freedom when fighting by allowing them to focus on dodging actual attacks instead of having to try and avoid the creature’s hitbox.

After I had the collision detection for attacks working, I had to spend a little time rigging up the attack shapes to roughly match the player and monster’s attack animations so things would look fairly convincing.

The end result is that the player can now fight with a sword-wielding skeleton in the test level. Both of them are able to take damage from attacks and deal damage with attacks of their own. The skeleton can traverse the level fairly effectively, walking up and down slopes and dropping off ledges as necessary, which is pretty decent for a ‘filler’ monster. More advanced monsters will need actual pathfinding, and logic for chasing after the player.

The player’s health also has some simple logic implemented now for regeneration – health passively regenerates at a fixed rate, but taking damage from any source will cause your regeneration to stop, and then slowly resume after a short time. This means that your health won’t regenerate in between strikes if you’re being attacked repeatedly by a monster, but if you’re near death and in combat, dodging attacks for long enough will allow you to begin to regenerate. Having fairly rapid health regeneration is important for achieving the kind of combat I’m after – fights with small numbers of extremely dangerous enemies that require caution and rhythmic timing of attacks and dodges, instead of fights that involve plowing through dozens of skeletons or zombies like in some of the Castlevania games.

After all this, I started to have some severe performance issues – the logic in the Player class for handling sloped surfaces and motion had always been fairly high in my profiler stats, and now that I was running all that logic twice (once for the player, once for the monster) it was accounting for nearly half my processor usage on the PC, and was bringing the XBox version of the game down to the point where it could no longer consistently hit 60 frames per second – not good at all.

I ended up taking two steps to address the problem:

The first step was to solve an issue I had been anticipating to begin with – I was storing all the level’s geometry in a single List container, which meant that every single collision detection operation had to scan the entire level, regardless of the location of the object being checked for collision. I solved this by creating a simple SpatialCollection type that could store a list of objects and group them by their general location, so that I could do efficient collision checks against only nearby objects, without having to write a bunch of code. It took me a couple hours to write a good set of unit tests for the container and get it working, and then another hour or so to integrate it into the game in place of the old List. Luckily I didn’t have to alter any serialization or level loading code – just the code that did the actual collision checks.

After that, I spent another hour or so running performance profiles of the game on my PC, and optimizing the obvious hotspots. There were a few obvious things, like calling the List.Count property getter hundreds of times in the body of a loop instead of once at the beginning of the loop, and then there were also less obvious things, like the fact that I was unnecessarily recomputing the bounding box of a polygon every time it changed position (stupid…). After I killed most of the obvious bottlenecks, I ended up with a profile that looked something like this:

One thing I had to learn the hard way was that the debugger and profiler both have a tendency to suppress JIT optimizations in .NET, like method inlining. This can cause some code (like the subtraction operator on vectors) to appear much higher in a profile than it would otherwise. Regardless, though, the top dozen or so items on a profile almost always contain one or two ‘low-hanging fruit’ that you can easily eliminate from the performance profile with some quick tuning and refactoring.

I even found that it was worthwhile to optimize out things like unnecessary calls to property getters, because they tended to account for a larger performance hit on the XBox than on the PC – probably due to the nature of the XBox’s JIT and processor (which differ greatly from a typical desktop environment). Taking a look at frequently accessed properties can also be helpful since they can occasionally contain code that performs actual computation – for example, the property getter for RuntimeGeometry.Anchor in the profile above accounts for more than 3% of total processor usage. As it happens, that property getter performs some floating point arithmetic to get the right value before returning it, and I was calling it repeatedly inside of a loop (assuming that it was a simple, inexpensive field access) – so optimizing out that call turned out to be a great decision.

Once I was done with my optimization work, I had dropped my average CPU usage on the PC from 25% to 8% – around 1/3 of what it was before, which brought my framerate back into much better territory, and got the game running well on the 360 again (currently at around 20% CPU usage). This leaves me plenty of room to add new gameplay logic and graphics code without having to optimize for a while.

Tags: , , ,

Object model cleanup

Been kind of short on spare time this week, since it’s Hack Week at work and I’m getting things prepared for my housemate moving out. I spent most of my time on cleaning up my object model for level geometry so that it makes more sense and doesn’t have issues with being loaded and saved.

One of the major changes was unifying the way I store object positions and sizes. Previously, LevelGeometry objects each had their own convention, based on type – Rectangles had TopLeft/BottomRight, Triangles had A/B/C, Switches merely had a position, and so on.

The new object model is more consistent: Every piece of geometry has an Anchor, which defines the general position of the geometry. Every other coordinate (if any) is defined relative to the Anchor, which allows you to use the Anchor as a way of moving objects generally. This also means that I can get rid of the Position property I previously had, which was only used for object repositioning in the editor, and for a couple objects that moved at runtime (like doors and platforms) – objects that move at runtime can have a Position property in their RuntimeGeometry object, while the editor can adjust the Anchor property to reposition geometry.

One other challenge is that previously, I converted expressions that I loaded from the level file into functions immediately at load time, and used those functions directly to get their values. The best example is OneWayRectangles – to allow changing the direction of a barrier at runtime, based on the state of a switch or pressure plate, I made their PassableAxis an expression. In the new model, the expression lives in the design-time LevelGeometry object, but the RuntimeGeometry object is the one that needs to evaluate the expression and has the necessary context to evaluate it (for example, a reference to the Game object so that it can find other named objects like switches). This means that while the expression itself can live in the LevelGeometry object, the necessary scaffolding for compiling and evaluating expressions needs to live in the RuntimeGeometry object.

The solution I’m using for expressions right now is a bit of a hack intended to make the game code easier to write: The LevelGeometry object has fields of type GameExpression<T>, which are serialized as a single string (the expression). At runtime, when a RuntimeGeometry object is constructed from a LevelGeometry object, it finds all the GameExpression<>s defined in the LevelGeometry and hands those expressions a reference to the Game. The expression then uses the game to compile a function and temporarily stores the function. This means that the rest of the game code is now able to look like this:

public override void Update () {
    var pa = LevelGeometry.PassableAxis.Evaluate();
    if (pa != _LastPassableAxis) {
        if (_LastPassableAxis.Length() > 0.0f)
            _Game.CameraLookAt(LevelGeometry.Anchor + _Center, 3.0f);
        _LastPassableAxis = pa;
        UpdateArrow();
    }
}

The need for a comparison against the previous passable axis is kind of unfortunate, but it is at least straightforward. The main advantage of this approach is that I don’t need to do any work to keep RuntimeGeometry objects in sync with the expressions defined in the corresponding LevelGeometry object – the necessary work is done at runtime to fix it up. The downside, of course, is that this means it’s not possible to use the same LevelGeometry object in multiple Game instances, and the solution also breaks some OO design principles.

Luckily, having multiple Game instances is pretty much guaranteed to be unnecessary in my case. Design principles tend to matter a little bit, so I spent some time trying to come up with a cleaner, more pure solution to the problem, but I couldn’t find one… so screw design principles. Eliminating duplication is key.

Getting these details of the object model worked out brings me closer to being able to build levels entirely in the integrated editor without having to edit XML files, which is essential if I want to start building real game content out of multiple level files with complex object interactions and geometry, and have the rest of my team be able to make edits and contributions directly without my help.

Tags: , , ,