Archive for January, 2009

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: , , ,

Prince of Persia’s ending

I’m going to diverge from my typical theme for a bit here. After reading over a lot of the recent discussion over the newest Prince of Persia game, I was struck by how much disagreement there was about the ending. First I wrote a comment on one of the blog posts I was reading, but after some thought, I decided I should share my take on it in a more public location so people have the option to respond.

If you haven’t finished the game yet and you have any plans to see the ending for yourself, please don’t read anymore. Spoilers ahead. If you don’t care about spoiling the ending, this might not make much sense to you anyway, because I’m going to be mostly discussing the details of the story and the ending.

Read the rest of this entry »

Tags: , , , ,

Level editing III

Over the past week most of my work on the game has been on editor-related things, along with some improvements to the collision detection code.

One of the changes I had to make was support for level geometry that’s made up of multiple polygons. This allows me to have geometry that isn’t convex, like an ellipse or a concave slope. Not too much work involved, really; I just needed to add some for loops in a few places and write a simple triangulation routine.

Getting curved surfaces to work was a bit of a challenge, but that was mostly just because I had to spend an hour or two figuring out the right formula to represent the slope of the surface. Most curves ended up being concave instead of convex, as well, which necessitated the work I mentioned above.

The main thing I worked on, though, was getting everything set up so that I could do loading and saving from the editor without issues. I had to split out my level into ‘design time’ and ‘run time’ data structures so that I could load a level, run around for a few minutes, and then save it without any of the contents of the file changing. Things like the position of a switch needed to be separated from the state of the switch in a straightforward manner.

I ended up going with a simple approach based on graph traversal. I treat the level as the root of a tree and each piece of geometry or tile in the level is one of the leaves. This allows me to handle serialization generally by writing out the entire graph as XML with some type annotations, which looks like this (various crap edited out for readability):

<?xml version="1.0" encoding="utf-8"?>
<level>
  <graph>
    <node key="1">
      <node key="2">
        <node key="3">
          <node key="4" />
          <node key="5" />
          <node key="6" />
          <node key="7" />
          <node key="8" />
          <node key="9" />
          ...
        </node>
      </node>
    </node>
  </graph>
  <nodes>
    <types>
      <type id="0" name="XnaGameTest.Level" />
      <type id="1" name="XnaGameTest.LevelLayerList" />
      <type id="2" name="XnaGameTest.LevelLayer" />
      ...
    </types>
    <values>
      <Level key="1" typeId="0" />
      <LevelLayerList key="2" typeId="1" />
      <LevelLayer key="3" typeId="2" />
      <LevelTile key="4" typeId="3">
        <Position>
          <X>944</X>
          <Y>-256</Y>
        </Position>
        <Tile>bgwall1</Tile>
      </LevelTile>
      <LevelTile key="5" typeId="3">
        <Position>
          <X>1136</X>
          <Y>-256</Y>
        </Position>
        <Tile>bgwall2</Tile>
      </LevelTile>
      <LevelTile key="6" typeId="3">
        <Position>
          <X>1328</X>
          <Y>-256</Y>
        </Position>
        <Tile>bgdoor3</Tile>
      </LevelTile>
      ...
    </values>
  </nodes>
</level>

Once I have one of these files it’s really simple to load – I just create a XmlGraphReader and point it at the XML and I get out an INode object that represents the root node of the graph. In this case, I know the root is going to be a Level, so I cast it to the right type and I’m basically done.

One thing worth noting is that the XML only contains the ‘design time’ objects for the level. So, once I’ve loaded a level, I need to do some work before it can actually be played. What I do is walk over the entire graph, and for every LevelGeometry object, I call a virtual method called CreateRuntimeGeometry and pass in a reference to the game. Every type of LevelGeometry knows how to create its runtime counterpart, so the code ends up looking like this:

    public class LevelRectangle : LevelGeometry {
        public Vector2 TopLeft;
        public Vector2 BottomRight;

        protected override IRuntimeGeometry CreateRuntimeGeometry (Game game) {
            return new RuntimeRectangle(game, this);
        }
    }

    public class RuntimeRectangle : RuntimeGeometry {
        public RuntimeRectangle (Game game, LevelRectangle rect)
            : base (game, rect) {

            var poly = new DrawablePolygon(
                rect.TopLeft,
                new Vector2(rect.BottomRight.X, rect.TopLeft.Y),
                rect.BottomRight,
                new Vector2(rect.TopLeft.X, rect.BottomRight.Y)
            );

            _DrawList.Add(poly);
            _CollisionList.Add(poly);
        }

There’s some more magic behind the scenes, but that’s basically it. By splitting the objects out into two structures I have an easy way to handle saving levels and saving games, without getting state mixed up – whether a door is currently open is stored in the runtime object, but the position of the door is stored in the design time object.

One of the main bits of work left for me to do is refactoring the code so that I can get rid of the Position property of level geometry. The property has the unfortunate attribute of being used by both the editor and objects like doors at runtime, which means I can’t save it in the level file or in saved games, because it is modified both at design time and at run time. Once I’m done I expect that the design time part of it will be removed entirely, with the editor operating on the actual coordinates of the objects, and the run time part will remain but no longer be used by the editor.

If you’re curious about how the graph serialization stuff works, you can find the source code here. It has a few unit tests as well.

Tags: , , ,

Level editing II

Over the past few days I’ve done some more work on the level editor. The main things I tried to get working were macrotile editing and geometry editing. I did some work on making it easy to work with multiple layers of macrotiles, by building support for things like snapping the cursor to the edges of other tiles when placing tiles, so that it’s easy to create seamlessly tiled environments.

After I got that working, I started building basic support for editing level geometry. I managed to get a basic implementation of that working so that I can create rectangles and triangles, and reposition/delete existing geometry. I still have to come up with a good technique for saving out levels once they’ve been loaded, since up until this point I was building levels by hand in SciTE. Since lots of level geometry changes after being loaded, I need to come up with a straightforward way of storing the original, intended values, so that loading and immediately re-saving a level does not modify it.

One of the things I had to figure out was how to write the code for handling user input in the geometry editor, since it can take multiple clicks to define a shape like a triangle. The approach I ended up using is based on tasks, and it looks like this in practice:

protected IEnumerator<object> PlaceLevelTriangle () {
    var a = GetMousePosition(true);
    XnaPoint b, c;

    using (var f = CaptureMouse())
    while (!f.Completed) {
        b = GetMousePosition(true);
        _IncompleteGeometry = new LevelTriangle(SnapToGrid(a.ToVec2()), SnapToGrid(b.ToVec2()), SnapToGrid(b.ToVec2()));

        yield return new WaitForNextStep();
    }

    b = GetMousePosition(true);

    using (var f = CaptureMouse())
    while (!f.Completed) {
        c = GetMousePosition(true);
        _IncompleteGeometry = new LevelTriangle(SnapToGrid(a.ToVec2()), SnapToGrid(b.ToVec2()), SnapToGrid(c.ToVec2()));

        yield return new WaitForNextStep();
    }

    _IncompleteGeometry = null;
    c = GetMousePosition(true);
    var vA = SnapToGrid(a.ToVec2());
    var vB = SnapToGrid(b.ToVec2());
    var vC = SnapToGrid(c.ToVec2());

    if ((vB - vA).Length() > 0.0f && (vC - vA).Length() > 0.0f && (vC - vB).Length() > 0.0f)
        AddGeometry(new LevelTriangle(vA, vB, vC));
}

The CaptureMouse function attaches special input handlers to the three mouse buttons that eat all mouse input, and returns a future. When a mouse button is released, the handlers are removed and the future is completed. This allows me to yield a task until the mouse is released, or poll every frame to see whether the mouse has moved. This gives me a simple way to handle multi-click input and prevent clicks from accidentally setting off other code.

Tags: , , ,

luminance is Digg proof thanks to caching by WP Super Cache