Archive

Archive for December, 2008

Level editing and designery things

December 30th, 2008

Over the past few days I’ve invested most of my energy in the design side of the platformer - writing up design docs for bosses and characters, roughing out the design of the game world, etc. Not a whole lot of insights to share in that area yet - most of it is in primordial first draft state, so I have yet to learn any lessons from my work, just apply previously learned ones. So far I’ve been taking a very iterative, loose approach to my design work - laying down extremely rough, simple drafts based on initial concepts and basic design principles, and then fleshing in detail and making adjustments or moving on to other parts of the design. This means that there are lots of gaps left in the design to fill in, but it also means that I can avoid having to set too many details in stone just to rework them later in response to design changes.

Here’s part of the current diagram that represents the layout of the game world:

As you can see, it’s very light on detail - the sizes and shapes of each area are not to any particular scale, and the finer details of each area are not filled in, like where walls and slopes and doors are placed. But it gives a general sense of the shape and flow of each area, along with describing each area, showing how they connect, and showing the general location of each boss.

I also started working on building level editing functionality into my game. I ended up deciding to use WinForms to build the editor UI, but still integrate it into the game itself. After a little hacking and experimenting, I was able to get controls like toolbars and listboxes hosted into the XNA game window without too much fuss. Right now tapping the ~ key opens the editing UI and pauses the game, and then tapping the key again closes it. I only implemented some of the basic UI elements - status bar showing the status of the editor (Whether it’s performing an operation like a load/save) and mouse coordinates, along with a tile picker and basic support for laying down tiles. I still have to figure out how to properly handle editing levels that are built out of variable-sized tiles, since the approach I’m using hasn’t been done very often.

I did encounter a couple problems getting it working, though - WinForms has no way to reparent a control onto another window directly, so I had to use P/Invoke to call SetParent. I also realized that since my tiles were Texture2D objects, there was no straightforward way to render them into a WinForms ListBox. I ended up building a simple tile picker myself instead, using XNA to render everything except the scrollbar. Other than those relatively minor issues, though, it was pretty easy to get up and running, and it builds nicely on top of the support I already had for reloading levels at runtime.

Gruedorf , , , ,

Mantling

December 24th, 2008

I recently hacked together a simple implementation of mantling, which allows the player to grip onto ledges while falling, and climb up onto ledges while standing below them or hanging off of them. It’s simple, but it does add a nice extra bit of mobility. It definitely helps in cases where you just barely miss a jump, because now you can grip onto the ledge and climb up instead of falling and having to try the jump again.

The implementation is pretty simple, so I’ll just summarize it here: While falling, if the player is pushing the dpad towards a nearby wall, I do some simple intersection tests to see if his feet are making contact with the wall, and if there’s room above him for him to climb up onto the surface. If both of these conditions are met, I ‘grip’ him to the wall until the dpad is released. At this point, you can press up to climb up onto the ledge automatically. Climbing up is a simple animation where I lock the player’s input temporarily and interpolate his position along a curve to bring him up onto the ledge, and restore input and normal motion/collision detection.

Gruedorf , , ,

Grappling Hook prototype III

December 23rd, 2008

Now that I’ve got the mechanics for the grappling hook roughly where I want them, I’ll try and describe the approach I used to build it. Note that this isn’t a perfect implementation by any means - it interacts badly with obstructions in some cases, doesn’t work well when attached to vertical surfaces, and doesn’t use any sort of rope or spring simulation to model the chain. However, the grappling hook in my platformer is intended to be used to swing from specifically placed ‘grapple points’ which will mostly be on horizontal surfaces, so it meets my needs.

The first step for implementing the grappling hook was to build a simple representation of the hook itself, so the player could throw it in a given direction and see it stop when it hit something. For this purpose, I created a simple GrapplingHook class to represent the position and velocity of the hook, along with other bits of state like whether or not it has made contact with a surface:

    public class GrapplingHook : IGameComponent, IUpdateable, IDrawable {
        protected Game _Game;
        public Vector2 Position, Velocity;
        public bool Retracting = false;
        public bool Stopped = false;
        public LevelGeometry AttachedTo = null;
        public float AttachY = 0;

        public GrapplingHook (Game game, Vector2 initialPosition, Vector2 direction) {
            _Game = game;
            Position = initialPosition;
            Velocity = direction * Game.GrapplingHookSpeed;
        }

The significance of some of those other variables will be made clear later. To throw the hook, the Player class constructs a hook object at the player’s throwing position and assigns it a direction to move in, based on the direction the player was aiming in when he threw the hook.

The player’s throwing position is simply the player’s actual position with half of his height subtracted, so that the hook is thrown from roughly the middle of his torso (close to his hands). This is also the position used when swinging from the hook.

        protected void LaunchHook () {
            DetachHook();
            if (Hook != null)
                return;

            var dir = (AimingWithRightStick) ? LastAimDirection : AimDirection;
            if (dir.LengthSquared() <= 0)
                return;

            var AimStart = Position + new Vector2(0.0f, -Height / 2.0f);

            Hook = new GrapplingHook(Game, AimStart, dir);
            Game.Components.Add(Hook);
        }

The direction is computed in a separate piece of logic. You can see a few other pieces of logic here - I allow you to aim and throw a grappling hook by pressing and releasing the right stick, as an alternative to using the button on the controller/keyboard. This allows you to use the left and right sticks in concert to use the grappling hook to swing between surfaces and traverse longer distances, without having to use the left stick to aim. I make sure to ignore any direction vectors with no length, since those indicate that the player released the aiming button without pushing in a direction. Likewise, if a grappling hook is already in existence, I bail out in order to avoid having multiple grappling hooks in midair at once. The DetachHook call at the top ensures that pressing the grappling hook button will retract any existing grappling hook, in order for you to be able to launch a new one.

The code responsible for aiming is fairly simple: It comes up with a direction vector based on the player’s input, and casts a ray from the player’s throwing position in that direction. The closest surface that ray makes contact with (if any) is treated as the ‘aim position’ and highlighted with a bright crosshair to show the player that the hook will land there if thrown. This is similar to the logic used by the hook itself to determine when it’s made contact with a surface and needs to stop.

With these bits of code combined, the player can now toss a grappling hook in a given direction, and the game knows how to draw the hook and chain in the correct location. The logic for allowing the player to swing from the hook is a bit more complicated.

One of the key changes I had to make was representing the player’s position while swinging using polar coordinates, instead of traditional cartesian coordinates. What this means is that instead of using X and Y axes, I’m storing the player’s location as a radius and an angle. This allows the basic motions possible on the hook to be represented simply and makes it easier to model acceleration and deceleration based on the player’s position on the chain.

Grappling hook diagram

Grappling hook diagram

As shown by this artfully constructed and COMPLETELY FLAWLESS diagram, I then map the player’s existing Velocity property to these new axes so that I can reuse sections of the existing input and motion code. Velocity on the X axis becomes velocity on the angle, and velocity on the Y axis becomes velocity on the radius. This means that the input code will do the right thing with only minor modifications - left and right to swing on the chain, up and down to move up and down on the chain. (Perceptive readers may note that this approach completely breaks if the chain is not vertical.)

While the player is swinging from a chain, the code used to perform motion and collision detection is altered (though fundamentally the same). Velocity is applied by converting the player’s current Position (in cartesian coordinates) to a position in the same format used by the player’s velocity. The velocity is then applied to that polar position to compute a new position in polar format, and then the resulting polar position is converted back to cartesian coordinates. After doing that ridiculous song and dance, I now have a pair of cartesian coordinates that I can feed into the standard collision detection algorithm, ensuring that the player at least does not clip through walls while swinging on the chain.

    public struct HookPosition {
        public Vector2 Center;
        public float Rotation;
        public float Distance;

        public static HookPosition FromXY (Vector2 center, Vector2 xy) {
            var result = new HookPosition {
                Center = center,
                Rotation = (float)Math.Atan2(xy.Y - center.Y, xy.X - center.X),
                Distance = (xy - center).Length()
            };

            return result;
        }

        public Vector2 ToXY () {
            return new Vector2(
                Center.X + ((float)Math.Cos(Rotation) * Distance),
                Center.Y + ((float)Math.Sin(Rotation) * Distance)
            );
        }
    }

To allow launching yourself from the chain in order to make it more useful, I also take the extra step of ‘converting’ the player’s velocity. When you first grab onto the chain, I convert your current velocity into polar coordinates, so that your momentum is mostly preserved. Likewise, when you let go of the chain I perform the reverse conversion, translating your momentum on the chain into normal momentum. This allows you to launch yourself up into the air a short distance by letting go while you’re at the apex of a swing, and multiplying the resulting velocity by 2 or so ensures that you get a satisfying amount of momentum out of it.

I also do some work to constrain the range of the player’s movement while on the chain. The two main constraints are that the chain cannot swing vertically above the hook, and that the chain’s length must be within a certain range. This prevents the player from horrifically exploiting the game’s geometry by using the chain to descend long distances, ascend to distant ceilings, or ‘flip’ himself up over a ledge by swinging the chain until it is nearly vertical.

Ensuring that the momentum of the player on the chain ‘feels’ right also requires a few other details - I have to scale the player’s X velocity based on the current length of the chain, so that it feels like your momentum on the chain is relatively equivalent regardless of how long it is. This is a downside to using polar coordinates instead of cartesian coordinates - the actual distance travelled by increasing your angle by 2 is determined by your current radius, so you have to take the radius into account when converting to/from cartesian coordinates. I also have to tweak the speed at which you move up and down the chain to stay consistent with what the player would expect from gravity, and make it harder to accidentally move up or down the chain while trying to swing back and forth, since it’s fairly easy to accidentally generate up or down inputs on the 360’s dpad and left stick.

Getting realistic momentum with the effect of gravity is a little bit tougher - the solution I’m using is a bit of a hack, but works more or less right. Whenever processing the player’s input, I first determine how far ‘up’ the arc of the chain he is by subtracting the hook’s position on the arc from his position on the arc. This gives me an angle that ranges from -90 degrees to 90 degrees. At this point, I can convert that distance into a multiplier that will cause the player’s momentum to taper off as the chain comes close to becoming horizontal, and likewise decrease the effect of gravity as the chain comes close to becoming vertical. This allows you to easily swing back and forth while the chain is hanging downward but prevents you from gaining absurd momentum and flipping the chain up over the hook.

The finishing touches basically involve playability concerns - making the hook automatically detach if the player tries to jump off a wall or floor, drawing the chain (this is pretty simple - I walk the distance between the player and the hook in increments of a few pixels at a time, blitting a rotated ‘chain link’ sprite repeatedly to render the chain), and making sure logic for things like wall clinging and mantling doesn’t run while the player is hanging from a grappling hook.

And finally a couple side notes:

I spent some time working on an initial implementation of falling damage and player health. It’s functioning, but still needs some polish. Right now falling 1200 pixels will kill you and any fall under 600 pixels will not injure you at all, with a linear scale inbetween - I think I want a more fine-tuned approach to falling damage than that, and I’m still not entirely sure falling damage will be beneficial to the design of the game.

I also spent some time refining the algorithm I use for collision detection, in order to address some edge cases related to sloped surfaces and the edges of surfaces. Previously there was a bug where you could ’slip through’ the edge of a rectangular surface if you were nearly intersecting another surface. This was due to a problem in my ResolveMotion algorithm that caused it to produce velocity vectors that were marked as ‘not intersecting’ when they actually resulted in an intersection between two shapes. Fixing this bug revealed some problems with my implementation of sloped surfaces that caused things to get completely busted on surfaces with steep slopes (slopes where y/x < 1, essentially). I spent about 6 hours testing different approaches to solving the problem before finally arriving at the solution I’m using now, which works correctly for all my test slopes, fixes the problem with slipping through surfaces, and still performs relatively well. As a bonus, the code is much smaller now and is actually faster than the old code in some cases. The downside is that I had to cheat to get a satisfactory result for surfaces with steep slopes.

        public const float StupidCollisionFudgeFactor = 0.1f;

Borne of necessity, I have unleashed a monster upon the waking world. It is arbitrary, hard-coded, and very, very stupid. But at least it works!

Gruedorf , , , ,

Simple Particle Effects

December 21st, 2008

After doing some more tuning on the grappling hook, I decided to take a break and work on a particle system implementation.

To handle various types of particle effects, I made a generic ParticleSystem class, where you can specify a particle type for the system to handle when creating it, along with a function to handle updating the particle, and a texture to use for drawing it:

public class ParticleSystem<T> where T : struct, IParticle {
    public delegate bool Updater (ref T particle, int index);
SmokeParticles = new ParticleSystem<BasicParticle>(SmokeTexture, UpdateSmoke);
SparkParticles = new ParticleSystem<SparkParticle>(SparkTexture, UpdateSpark);

The IParticle interface contains all the properties the particle system needs to draw a particle. So, at runtime, to update and draw each particle system, I just call the appropriate methods:

SmokeParticles.Update();

SpriteBatch.Begin(SpriteBlendMode.AlphaBlend);
SmokeParticles.Draw(SpriteBatch, ViewportPosition);
SpriteBatch.End();

The updater function is responsible for updating every particle each frame, and also determines when it’s time for a particle to die and be removed.

    protected bool UpdateSmoke (ref BasicParticle particle, int index) {
        particle.Scale += 0.05f;
        particle.Rotation += 0.025f;
        particle.Opacity -= 0.01f;

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

In my case, I made a couple assumptions that allow me to simplify things some:

  • Particles don’t ever interact with each other.
  • Particles are not modified from outside the particle system after being created.

As a result, I was able to make this optimization (among others):

To remove a ‘dead’ particle, instead of having to use a potentially expensive method call like List.Remove or having to use a garbage-heavy data structure like a linked list, I can instead swap dead particles with the last live particle. Doing this moves the live particle forward in the list so that it will still get updated, and means that the last item in the list is now a dead particle - so all I have to do is reduce the particle count by one.

To integrate the particle system with the game, I wrote helper functions for ’spawning’ particle effects at a given location, like this:

    public void SpawnSparks (Vector2 position) {
        var rng = new Random();
        Vector2 dir;

        for (int i = 0; i < 24; i++) {
            dir.X = rng.NextFloat(-1, 1);
            dir.Y = rng.NextFloat(-1, 1);
            dir.Normalize();

            SparkParticles.Spawn(new SparkParticle {
                Position = position,
                Opacity = rng.NextFloat(0.40f, 0.60f),
                Velocity = dir * rng.NextFloat(1.66f, 2.66f),
                Color = new Color(rng.NextFloat(0.8f, 1.0f), rng.NextFloat(0.45f, 0.65f), rng.NextFloat(0.2f, 0.35f), 1.0f),
                Scale = 0.35f
            });
        }
    }

This function does all the work of spawning a bunch of particles at the right location with randomized parameters, and the updater function does the work after that. The final bit of code goes in the grappling hook class:

    this.Stopped = true;
    _Game.SpawnSparks(this.Position);

The call to SpawnSparks spawns some spark particles at the location of the grappling hook when it first makes contact with a surface and stops.

After I got it working, I used it to add a few simple particle effects to go with some of the game mechanics already in the prototype. It definitely looks a bit nicer, and helps add a little more feedback for the player when doing things like performing a wall-jump.

Gruedorf , , , ,

Grappling Hook prototype II

December 21st, 2008

More work on the grappling hook. Still working out the physics and controls. The collision detection still has some issues to be resolved as well…

Gruedorf , , , ,

Grappling Hook prototype

December 20th, 2008

Not quite done with this one yet, so I don’t have much to say:

I’ve been working on an implementation for the player’s grappling hook. So far I have some of the basics implemented - aiming the hook, throwing it, the hook attaching itself to surfaces, the hook retracting so you can throw it again, and so on. I still have to figure out the majority of the physics for the hook, along with other details like preventing it from going through walls and floors.

Getting it to work has required me to make some significant refactorings to the code I use to handle player movement. So far, the code’s been becoming simpler, but I’ve had to go back and rethink some of the logic I wrote to handle acceleration and deceleration. I’ve been replacing a lot of special cases and nested ifs with expressions that make heavy use of Min, Max, Sign, and Abs to properly clamp and adjust the player’s velocity based on various inputs (is the user pressing the dpad, is the user jumping, etc.)

Gruedorf , , , ,

Camera control, performance, and pacing

December 18th, 2008

After doing some more prototyping, I started to realize that my test level had some serious pacing and playability issues. The most important issue was that flipping switches or standing on pressure plates would sometimes cause things to occur offscreen, which made it difficult to understand what was going on in a puzzle.

To try and solve this problem, I implemented a basic system for automatic camera control. When an object in the game’s state changes, it asks the Game object to make sure the player notices, by calling CameraLookAt and passing in its own position.

Every frame the game runs an UpdateCamera function that scans through all the CameraLookAt requests from the previous frame, and uses a simple algorithm to determine whether it should do anything to the camera, and if so, what point it should center the camera on:

  • At least one of the CameraLookAt requests must be for a point that is not currently on-screen.
  • If any of the requests is for a point that is on-screen, use all of the requests to determine the point to center the camera on.
  • Compute a bounding rectangle that contains all the CameraLookAt requests, and center the camera on the center of that rectangle.

One slight problem this algorithm has is that it only operates on points, not areas of interest. For now, I solve this by excluding 10% of the screen on all sides, assuming that the player is unlikely to notice things changing on the edges of the screen. In the future, I’ll probably change the algorithm to operate on rectangles. The use of a bounding rectangle also means that if a lot of things happen at once, it won’t be able to show them all to the player. I think the only way to solve that problem is to control the camera manually, either with player inputs or with carefully scripted camera controls for each level. The latter is the approach I plan to use in the future.

The camera transitions are also a bit too abrupt. One future improvement will be to adjust the speed at which the camera moves, based on how far it has to move. It might also be good to try and move the camera in advance of changes in the level, so it’s easier for the player to figure out what happened. Since one-way barriers transition instantly it can be a little hard to realize why the camera is looking at them.

protected void UpdateCamera () {
    float screenWidth = GraphicsDevice.Viewport.Width;
    float screenHeight = GraphicsDevice.Viewport.Height;

    if (CameraLookAtQueue.Count > 0) {
        var cameraTopLeft = new Vector2(ViewportPosition.X + (screenWidth * 0.1f), ViewportPosition.Y + (screenHeight * 0.1f));
        var cameraBottomRight = new Vector2(ViewportPosition.X + (screenWidth * 0.9f), ViewportPosition.Y + (screenHeight * 0.9f));

        var minPos = new Vector2(float.MaxValue, float.MaxValue);
        var maxPos = new Vector2(float.MinValue, float.MinValue);
        float duration = 1.0f;
        int offscreenCount = CameraLookAtQueue.Count;

        for (int i = 0; i < CameraLookAtQueue.Count; i++) {
            var la = CameraLookAtQueue[i];

            duration = Math.Max(la.Duration, duration);
            minPos.X = Math.Min(minPos.X, la.Position.X);
            minPos.Y = Math.Min(minPos.Y, la.Position.Y);
            maxPos.X = Math.Max(maxPos.X, la.Position.X);
            maxPos.Y = Math.Max(maxPos.Y, la.Position.Y);

            if ((la.Position.X >= cameraTopLeft.X) &&
                (la.Position.Y >= cameraTopLeft.Y) &&
                (la.Position.X <= cameraBottomRight.X) &&
                (la.Position.Y <= cameraBottomRight.Y))
                offscreenCount -= 1;
        }

        var center = minPos + ((maxPos - minPos) / 2.0f);

        if ((center.X >= cameraTopLeft.X) &&
            (center.Y >= cameraTopLeft.Y) &&
            (center.X <= cameraBottomRight.X) &&
            (center.Y <= cameraBottomRight.Y) &&
            (offscreenCount == 0)) {
            // If all the points are on screen, don't move the camera.
        } else {
            CameraStack.Add(new LookAtCameraController(
                center, duration
            ));
        }

            CameraLookAtQueue.Clear();
        }

        Vector2 pos = new Vector2();

        for (int i = 0; i < CameraStack.Count; i++) {
        var cc = CameraStack[i];
        var newPos = cc.GetCameraPosition(screenWidth, screenHeight);
        var w = cc.GetWeight();

        if (w <= 0) {
            CameraStack.RemoveAt(i);
            i -= 1;
        } else {
            pos = new Vector2(pos.X + ((newPos.X - pos.X) * w), pos.Y + ((newPos.Y - pos.Y) * w));
        }
    }

    ViewportPosition = pos;
}

Solving the visibility problem with the camera made my test level’s puzzles a lot easier to follow.

The pacing issues are more fundamental, unfortunately. My test level has a lot of boring, empty spaces to run through. One thing that will help is adding more content to the level, like enemies to fight in the empty hallways, deadly traps to avoid, etc. But even then, having to trek through lots of areas repeatedly to solve puzzles sucks, so I’m going to have to pay careful attention to this when designing and testing levels. I haven’t come up with a good strategy for making the pacing feel ‘right’ yet.

I also spent some time doing some performance tuning using the CLR Profiler and dotTrace, for memory and CPU profiling respectively - I had reached the point where my game no longer ran at 60FPS on the XBox 360, though it ran fine on my PC, which is kind of to be expected - I hadn’t done much optimization at all up until this point.

Other than basic things like tuning code in hotspots to be more efficient, I also spent a little time trying to control my allocation rate. On the 360, if you allocate temporary objects rapidly enough, the garbage collector will have to interrupt you often, causing your game to stutter. I had to hunt down a few places in my game code where I was accidentally creating temporary instances of things like delegates and boxed value types.

In one case, the compiler had made a clever optimization by moving a new statement further up in the function for performance reasons, without taking into account that it caused a function to always allocate a temporary object instead of allocating one sometimes. At first I thought this was a compiler bug, until I looked at the disassembly and realized that the only impact of the optimization was allocation - no extra code was being run, so it only had GC impact. Regardless, it was kind of unexpected - I was unable to figure it out until I started looking at disassembly in Reflector. Having CLR Profiler’s call/allocation tree helped a lot in knowing where to look, at least.

I spent a little time tuning my collision detection and motion code, by removing unnecessary calculations, adjusting caching logic, and so on, but I’m also nearing the point where I’ll need to start optimizing my collision detection algorithms instead of just making the code faster. Right now it’s entirely brute-force, but in the future I’ll need to build some sort of a scene graph or quadtree to store my collision geometry, so I don’t have to test the player against the entire level every frame. Right now my CPU usage goes down by about 5-10% any time the player is jumping/falling, just because I perform less collision checks when he’s in that state. The ComputeStandingY algorithm is also kind of a problem here, since as currently implemented it has to walk the entire level 5 times every frame.

The CPU profile wasn’t particularly enlightening - most of the bottlenecks were exactly what I figured they would be - all the looping and floating point calcluations in my motion and collision detection code. However, there were a few things I hadn’t expected to show up:

  • Actual CPU time (3-4% of total) was being spent in the constructors for Rectangle, Size, and Vector2 in my drawing routines. Totally unnecessary; most of it was a result of using ‘foo = new Vector2(x, y);‘ when I could have used ‘foo.X = x; foo.Y = y;‘ instead. I’m not quite sure why the compiler wasn’t inling these calls, though.
  • Actual CPU time (another 4-5% of total) was being spent looping over all of a level’s tilesets every frame to map a tile index to a texture and rectangle. This was stupid and extremely easy to solve - I just moved that looping operation into a function and cached the result. I could speed it up further by doing all of those lookups once when loading the level, eliminating the need for the caching logic, but it’s so low on profiles now that it doesn’t really matter.
  • The GetPolygonAxes function in my collision code had an optimization in it that turned out to be a pessimization: It did some work to avoid adding the same axis to the list twice, so that the collision detection code wouldn’t have to check it twice. On paper, this is a significant performance improvement, since it reduces a rectangle vs rectangle check from 8 iterations to 4. In practice, it actually made my performance worse, because performing an equality check between Vector2 objects has tangible overhead, as does walking over the current contents of the list. I might be able to bring it back using some sort of clever hashing technique, but for now I ended up removing that “optimization” entirely.

Here’s some footage of the camera controls in action, along with more of the test level. I edited out the boring walking sections with a sledgehammer since nobody would want to watch them - hope the cuts aren’t too jarring.

Gruedorf , , , ,

One-way barriers and serialization

December 15th, 2008

Today at DevHouse I worked on a couple improvements to the platformer: game state serialization, and one-way barriers.

Game state serialization was the first. My original implementation of saved games only stored the player’s position, so the state of things like switches and doors wouldn’t be persisted - this meant it was trivial to get yourself stuck by loading in an inaccessible location.

The approach I ended up taking was to implement a simple dictionary of named key/value pairs using .NET’s XML Serialization, and then bind that dictionary to all the named objects in the game. I used reflection to tag various properties and fields of objects to indicate that they should be serialized, so that the process of saving and loading the game state was fairly automatic.

What that means is that where before the player’s position was represented by this XML in the save file:

<SavedGame>
  <PlayerPosition>
    <X>128.449982</X>
    <Y>272</Y>
  </PlayerPosition>

Now it’s represented by this XML:

<SavedGame>
  <GameState>
    <types>
      <type id="2" name="Microsoft.Xna.Framework.Vector2, Microsoft.Xna.Framework, Version=3.0.0.0, Culture=neutral, PublicKeyToken=6d5c3888ef60e27d" />
    </types>
    <Vector2 key="player.Position" typeId="2">
      <X>128.449982</X>
      <Y>272</Y>
    </Vector2>

You’ll probably notice that the new XML is more verbose (some XML has been omitted from both examples for clarity). The primary difference is that where previously, the values in the SavedGame object were pre-defined, and had to be manually loaded and saved after XML (de)serialization was complete. Now, the game uses the key attached to each value along with its typeId to automatically locate the appropriate object and property.

Once the XML parsing is all done, the following code does all the work of updating the properties with the new values:

    protected IEnumerator<object> LoadGame () {
        var rtc = new RunToCompletion(Storage.LoadFromStorage<SavedGame>("game.sav"));
        yield return rtc;
        var sg = (SavedGame)rtc.Result;

        var bindFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy;

        foreach (var item in sg.GameState) {
            var parts = item.Key.Split(new char[] { '.' }, 2);
            var obj = FindNamedObject(parts[0]);
            var t = obj.GetType();

            var prop = t.GetProperty(parts[1], bindFlags);
            if (prop != null) {
                prop.SetValue(obj, item.Value, null);
            } else {

                var field = t.GetField(parts[1], bindFlags);
                if (field != null)
                    field.SetValue(obj, item.Value);
            }
        }
    }

A bit more verbose than I would like, mostly due to the necessity of handling properties and fields separately, but on the other hand, this code currently handles all the necessary work for loading a saved game.

After I had serialization all rigged up, I started working on implementing one-way barriers, that the player can only pass through in one direction.

One-way barriers required some changes to my existing collision detection code - I extended the existing LevelGeometry interface with a new method called ShouldObstruct. When performing collision detection and movement checks the game now calls ShouldObstruct, passing in the player’s current velocity so that the object can determine whether or not the player should be allowed to pass through the object.

Once that was rigged up, it was as simple as defining a new type of geometry called a OneWayRectangle with a configurable PassableAxis property. The player is only allowed to pass through the rectangle when the dot product of the PassableAxis and the player’s velocity is less or equal to 0. This provides the basic constraint of only being able to move through the barrier in a particular direction.

However, one problem came up after I got it working: It was fairly easy to get stuck inside a barrier by jumping while moving through it, because the barrier would reject the downward falling velocity from the jump and prevent you from landing on the ground. The solution ended up being to always allow the player to move through a barrier if he’s already colliding with it, so that once you’ve crossed into a barrier (which is only possible if you’re moving in the appropriate direction), you’re free to move in any direction until you exit the barrier.

One way barriers turn out to be useful for making environments more dynamic by changing the passable axis of a barrier based on other conditions, or by using a barrier to prevent the player from leaving a chamber once entering. They also provide a good foundation for something I plan to implement in the future - the ability to ‘phase’ through platforms by pressing down and jump, like you can do in many modern 2D platformers.

Gruedorf , , ,

Puzzles and expressions

December 14th, 2008

First of all, I figured out why the camera tended to jitter while moving up or down a pivoting platform. It turned out to be caused by the platform pivoting up into the player’s bounding box. The player’s motion code attempted to move the player and apply standing correction for platforms in the same step, and in this particular case, that would prevent the player from moving at full velocity. The solution was to change the algorithm to work like this:

  • Check to see if the platform the player is standing on has moved since the last frame. If so, adjust the player’s Y coordinate to compensate.
  • Attempt to apply the player’s X velocity, and adjust the player’s Y coordinate based on his new standing Y coordinate (for the new X coordinate).
  • Attempt to apply the player’s Y velocity (falling/jumping).

The duplication of adjusting the player’s Y coordinate twice kind of sucks, but it fixes most of the glitches when moving on a rapidly pivoting surface, and it has the added benefit of making moving platforms work a bit better.

After solving that issue, I started working on implementing an actual puzzle using the level elements I’d built so far.

The first thing I needed to be able to construct an actual puzzle was the ability to attach level elements to each other in complex ways. To do that, I started working on extending the simple expression parser I had built earlier so that I could use it to link platforms, pressure plates, switches and doors together.

The approach I ended up using is similar to the one I was using before, but it’s more sophisticated. The expressions in my level data look like this:

    <pivotPlatform id="platform1" a="8.5,11.5"  b="12.5,12"   minTilt="-3.25" maxTilt="3.25" tiltSpeed="0.065" tilt="plate1.State ? 1.0 : -1.0" />
    <door          id="door2"     a="1.75,9"    b="2.75,14"   openDirection="0,1" state="switch1.State and switch2.State" />

Parsing the expressions and converting them into something the game can use at runtime is a multiple-step process. The first step is to convert the expression string into a stream of tokens. Right now, I’m doing this with a regular expression:

(
  (?'number' (-?) [0-9]+ (\.[0-9]+)? ) |
  (?'identifier' [_A-Za-z][A-Za-z0-9_]*) |
  (?'operator' <= | >= | != | [\-+/*&|^!><=.%?:]) |
  (?'paren' [()])
)

I apply this regex to the expression string and get a string of Match objects as a result, with named groups that determine the type of each token.

After that, I scan through the token stream and convert the tokens into function objects. Number tokens get converted into functions that return their values, like this:

    public static ValueProxy MakeFloat (float value) {
        return () => value;

Identifiers get transformed in a similar, though slightly more complex manner, in order to map them to the set of named objects in the level:

    public static object MakeIdentifier (string identifier, NameResolver resolver) {
        ValueProxy result = () => resolver(identifier);

The NameResolver in use here is defined elsewhere to map a string identifier to an element in the NamedObjects dictionary:

    public Func<T> ParseExpression<T> (string expression) {
        var resolveName = (NameResolver)((name) => this.NamedObjects[name]);

These two bits of code combined allow straightforward connections between named objects in a level, without having to pay attention to order of declaration or having to load a level in multiple passes.

Paren tokens are treated as a signal to the parser that a new subexpression has started. When I hit an open paren, I recurse into a new expression parser that runs until it hits a close paren, at which point it returns a function that can be evaluated to get the result of that subexpression. (I could have implemented this without recursion, but I’m too lazy. :D )

Operator tokens get mapped to function objects based on the particular operator they represent. Most operators map to unary or binary operator functions that are predefined, like this:

    case "^":
        return (BinaryOperator)(
            (lhs, rhs) => lhs.As<bool>() ^ rhs.As<bool>()
        );
    case "!":
        return (PrefixOperator)(
            (rhs) => !rhs.As<bool>()
        );
    case ">":
        return (BinaryOperator)(
            (lhs, rhs) => lhs.As<float>() > rhs.As<float>()
        );

(The one exception is the ternary operator, which has to be converted to two special ‘placeholder’ objects that a later stage of the expression parser translates into a single callable function.)

Once all the tokens have been converted, the final stage of the expression parser starts. This stage is fairly simplistic - it scans through the stream of converted tokens repeatedly, starting from the beginning each time, looking for operations that it can collapse into a callable function. For example, if it finds:

ValueProxy:2 BinaryOperator:+ ValueProxy:4

It knows that it can collapse that string of operations into a single new function, like this:

    } else if (current is BinaryOperator && prev is ValueProxy && next is ValueProxy) {
        stack.RemoveRange(i - 1, 3);
        stack.Insert(i - 1, ApplyBinary(prev, current, next));

ApplyBinary takes an operator function and a pair of value proxy functions, and returns a new function that applies the operator to the two value proxies when invoked:

    internal static ValueProxy ApplyBinary (object lhs, object binary, object rhs) {
        var b = (BinaryOperator)binary;
        var l = (ValueProxy)lhs;
        var r = (ValueProxy)rhs;
        return () => b(l, r);

This transformation is applied repeatedly to the stack until it contains a single ValueProxy representing the value of the entire expression. You might want to note that this approach doesn’t handle things like operator precedence, type safety, or type conversion - but I don’t currently need any of those things.

One particularly important detail is that I implemented a simple . operator that allows me to access fields and properties of named objects. This allows me to build more complex puzzles by having objects with multiple properties attached to them (for example, pressure plates have both a boolean State and a floating-point Depth). The way the dot operator works is fairly simple:

When the expression parser encounters a dot operator, it scans backwards one token and tries to grab an identifier. After that, it scans forward one token and tries to grab another identifier. If this fails, the expression is assumed to be invalid. At this point, the parser has a pair of identifiers, and the one on the left has already been resolved to point to a variable. The parser constructs a special function that will, when invoked, attempt to look up the value of a named property (the name is on the right) on the contents of the variable (on the left). Right now I do this with some reflection-based code, with a simple dictionary cache to mitigate the performance overhead of doing reflection at runtime by only performing a given reflection lookup once per variable/property pair.

With all this work done, I started experimenting with building a puzzle out of the primitives I already had: Pivoting platforms, switches, pressure plates and doors. As it turns out, being able to wall-jump off the side of a pivoting platform is actually pretty cool. :D

I’m still going to need to tune the physics some before these types of puzzles feel satisfying (it’s a bit hard to properly perform a wall-jump off the platforms due to how small they are, among other things), but I’m still pretty happy with how the puzzle turned out, considering how quickly I was able to assemble it.

Gruedorf , , ,

Pivoting platforms

December 13th, 2008

Most recently I’ve been experimenting with some additional mechanics, for example - pivoting platforms:

As it turns out, building a simple pivoting platform isn’t that hard, because I can reuse most of the code I already wrote for pressure plates and sloped surfaces. Most of the side-effects come out right automatically - as the platform pivots, ComputeStandingY positions the player appropriately to match the new slope of the surface, and collision detection still works right even from below the surface.

As you may notice, though, the motion looks a bit jittery if the slope of the surface becomes steep enough, and some of the details like the speed of pivoting need more tuning for it to really be interesting. But it was still suprisingly easy to build regardless - and I’m happy with how it turned out. Next, I need to figure out how to integrate it with other mechanics - maybe a pivoting platform that triggers a pressure plate, or two pivoting platforms that have linked positions?

Unintended, yet awesome side effect: I recently discovered that if your timing is just right, you can wall-jump off a pivoting platform. I might leave that one in.

Gruedorf , , ,