Archive for February, 2009

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

Wireless internet seems like a scam

Over the years, I’ve owned maybe a dozen different devices with built-in WiFi capability, along with a few add-on devices like USB and CF cards for accessing WiFi networks. 802.11a, 802.11b, 802.11g, 802.11n. Windows 2000, Windows XP, Windows Vista, Linux. Desktop, Laptop, etc. You get the idea. Variety.

All of them have pretty much sucked. Bad drivers, flaky connectivity, bad performance, increases in power usage (well, at least for the laptop ones where I could tell), and general all-around worthlessness. Their only advantage was that I didn’t have to plug an ethernet cable into my hardware – and let me tell you, that is nice – but the downsides often meant that it was easier to run a couple hundred feet of CAT5 than to actually get the wifi working.

I’m at my wit’s end. I have no idea why this s–t doesn’t work and I have no idea how to fix it. My near-inescapable conclusion at this point, after hassling with wireless internet for over 5 years and never having a good experience, is this:

Wireless internet is a waste of money and seems like a scam.

You spend a bunch of money buying wireless network equipment, spend your time configuring it, and then get unreliable performance. When you try and get support from network hardware vendors, you get screwed, because they rarely release firmware updates and their support phone lines (if they even have one) typically have extremely long hold times and require you to call when you should be at work trying to get things done.

Running CAT5 between commodity routers is cheaper, faster, and easier. The only downside, as far as I can tell, is that you can’t post to twitter from your toilet.

Well, maybe it’s your router.

I’ve owned half a dozen different routers, all from reputable brands with high average ratings on reseller sites and such. All of them have been pretty much equally terrible. I would love to blame individual brands but they all seem to suck equally, so there’s no point.

Well, maybe it’s your location.

I’ve had my WiFi equipment set up in 4 different residences, all in different locations – suburban, urban, rural, and in the most recent example, high-tech (I can get Google WiFi if I go to a park nearby. No idea how reliable it is…) No tangible difference in my experience with WiFi at any of those locations, though obviously intangible things like my Signal to Noise ratio varied.

Well, maybe you don’t know how to configure it?

Sure, I’m definitely not an expert. But I’ve put a lot of effort into it. I’ve tried various combinations of channels, transmit speeds, encryption configurations, and such on each WiFi access point I’ve owned. Some of those options made a tangible difference; for example, turning off encryption entirely (yes, entirely) reduced my average latency on my current router by 10-15ms, which was definitely worth it. I have to use VPN or SSL for any sensitive traffic now, but I was doing that already, so f–k WEP and f–k WPA.

But for the most part, none of that s–t did anything. I still get f–king packet loss when sending ping packets to a router that’s 50 feet away from me, and my transmit/recieve rates still randomly drop for no obvious reason, and I still occasionally have to hard-reset the settings on my router because it decides to stop accepting connections of any kind over wifi at the drop of a hat.

I could blame the devices I’m using to access my WiFi networks, but that doesn’t really work either. I’ve tried every possible driver setting and configuration change on my machines to see if it made a difference, and it didn’t. I’ve used multiple personal machines, along with machines from work, other people’s machines, and devices like an XBox 360 or iPhone. All of them get the same unpredictably terrible performance out of WiFi.

You just need better equipment!

I can buy that, really. But the two different places I’ve worked that had corporate WiFi networks have both suffered from the same reliability and performance issues with their WiFi that I have, despite professional operations/IT staff using those networks and putting their money and time into getting them to work. I don’t think it’s just about the equipment.

I’m convinced that it’s possible to construct a working, high-quality WiFi network – the one at Sun’s Executive Briefing Center is amazing, and I didn’t have any problems with it at the last SHDH – but I’ve seen more completely useless WiFi networks than I have successful ones, and even the useless ones are pretty expensive to set up and run.

Well, WiFi works great for me!

Really? Awesome! Please share some tips or suggestions, or perhaps point me to the hardware configuration you’re using. I’d love to try and reproduce your experience.

My current residence is one where it’s extremely difficult to run ethernet cables to my desktop and other hardware, so I’m kind of stuck with WiFi, which is what makes it so annoying. In most cases, I’d just run a cable and forget about it, like I ended up doing with all my previous WiFi hardware. No such luck here.

For those who feel like sharing their advice, here is a summary of my current configuration:

Netgear WNR834B wireless router, running in 54Mbps (G) mode. Located out in the open, up on a shelf. Line of sight to the machines I’m using is clear, with the exception of a floor between the router and my desktop. Plugged into a power socket it’s not sharing with anything other than my DSL modem.

Linksys WMP54G PCI adapter, in my desktop. No PCI IRQ conflicts or anything like that. Tried with both the included drivers and Windows XP’s built-in WiFi support. I have a high-quality wifi antenna attached to the adapter, sitting about 4 feet away from my desktop, positioned in order to get the best signal strength. The router is about 40 feet away, and as shown in the screenshots above, my SnR averages between 55dB and 70dB – full ’signal strength’ in every graphical tool I’ve seen.

One other desktop is connected directly to the wireless router via a 60 foot long CAT5 cable that we ran out a window, off the patio, and in another window to connect to the router downstairs, as an experiment. It has no connectivity issues whatsoever, despite the fact that the ethernet cable is outside.

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

luminance is Digg proof thanks to caching by WP Super Cache