Showing posts with label unity. Show all posts
Showing posts with label unity. Show all posts

Wednesday, January 2, 2013

Fog of War: An obfuscated defect

One of the major technical contributions I made at the end of the Underground Railroad project was to fix the fog of war feature. The designer's intent was that a player would only see the current county and one county away; if the player was at a depot of the Underground Railroad, they could see one farther. The team had already implemented a map and an invisible overlay to handle movement. The overlay had the same topography as the map that the player sees, but it was color coded. When clicking on the map, we look up the corresponding coordinates in the overlay to determine the county, as well as whether the countryside or county seat was selected. The same system is used for the popup hints: look up the mouse position in the overlay, and if its a legal target, inform the player.

The theory behind the fog of war, then, was simple. We added an opaque layer on top of the map, which I will call the fog layer.1 When the player's position changes, first determine which counties should be shown. Then, look for those counties in the color-coded overlay. Because the invisible overlay is the same shape as the actual map, turn the corresponding pixels transparent in the fog layer. Presto! We've cut out exactly the right shape in the fog layer to see only the counties that should be shown. Because the map was so big, the team came up with some appropriate optimizations, restricting the search for matching colors to those areas on or near the edges of the current camera view.

Starting the game in Jackson County, KY. The player only sees one county away.
(Freedom is North, so we don't let the player go deeper into the South.)
The team created a very domain model of the game counties, assembled using the Builder pattern in a fluent internal DSL. A representative line looks like this:

Add(Make ()
  .WithID (CountyID.DelawareIN)
  .Coordinates (40.211893f, -85.396077f)
  .SetDepot (1)
  .IsOnRiver ()
  .NorthernIndiana ())
.WithColor (35, 50, 0);

This is adding a new county to the registry. In particular, this is Delaware County, whose county seat has particular GPS coordinates, which had one depot, is on a river, and is in the northern half of Indiana. The last part constructs the color key in the county registry. Note that the actual visual color is arbitrary, since the player never sees it: all we're doing here is saying that the pixels colored as the RGB triple <35,50,0> correspond to Delaware County.

Here's where things get interesting. Unity3D has two color classes: Color and Color32. The former represents colors as four-dimensional floating-point vectors in the range [0,1], while the latter uses integer vectors in the range [0,255]. Each class has a directly corresponding constructor:

static function Color (r : float, g : float, b : float, a : float) : Color

static function Color32 (r : byte, g : byte, b : byte, a : byte) : Color32

Also, both classes support implicit conversion to the other. This means that if you have an object of one type but need the other, it will automatically do the sensible conversion—or the most sensible conversion it can.

I remember the students who worked on the domain model had some trouble understanding these different classes very early in the semester, but then I assumed that everything was taken care of. However, in trying to trace down defects in the fog of war feature, I saw some very strange color processing code. As I poked around the code, I noticed that both Color and Color32 were being used in different places. Whenever there was to be an implicit conversion, the same piece of bit-fiddling code appeared. That certainly shouldn't be necessary, since there's no way the conversion API was broken. After some exploration, I tracked it all back to the county registry builder. Here's the original implementation of WithColor:

public Builder WithColor (float r, float g, float b)
{
    Color key = new Color (r, g, b, 0);
    _county.Color = key;
    _builder.map.Add (key, _county);
    return _builder;
}

The astute reader will note that the error can already be identified. Spoilers ahead.

The call to WithColor sends in a triple of integers—35, 50, and 0 in my original example. These int values are silently converted into float, since that's what the method expects. Then, these floating-point values are passed to Color, which also expects float arguments. Remember how Color is defined? It's a four-dimensional vector of floating-point values in the range [0,1]. Yet, the Color class happily accepts the 35, 50, and 0. If you inspect this Color object or ask it to print itself, you find out that it's <35.0,50.0,0>, as you might expect. Then, if you convert it to a Color32, that object is <255,255,0>.

This also makes sense, if you think about it for a while. The Color class only really cares about values in the range [0,1], and it interprets anything above that range as being equivalent to one. So, if you drew the Color that is <35.0,50.0,0.0>, you would get bright yellow, not dull reddish green. If my students ever fully understood this, they certainly didn't articulate it or move to fix it: instead, they developed a kludge to pull the Color values out as floats and pack those back into a Color32 manually.

There are several lessons to this story. Here's what I got out of it:
  1. Read the API docs. There's no good reason to send floating-point values like 35.0 to the Color constructor.
  2. Make sure your API produces appropriate warnings when being used in such a weird way. There could have been a warning either upon sending the out-of-range values to Color or when they were used in the automatic conversion.
  3. Don't use primitive types when they are not semantically appropriate. Color should not actually take floats as arguments when what it really wants is values in the range [0,1]. So, make a class to represent that concept, and use that. Before you complain that this will impact performance, remember that premature optimization is the root of all evil.
  4. Refactor. The bizarre color-handling kludge showed up in at least two places, clearly the result of copy-paste coding. Had the original developer stopped and refactored this away, he would have at least made a better abstraction for handling the problem. In the best case, he would have recognized the problem and fixed the root cause.
  5. Automatic conversion is awful. You might think you're improving readability of your code, but only if you and the reader share the same mental model and expectations. Better to make it explicit. To me, it's similar to the desire for using static factory methods over explicit constructor calls: good naming can reveal your intention.
By the way, The Underground Railroad in the Ohio River Valley is now open to the public. Enjoy!



1 I was surprised that the students didn't understand "fog of war" as a metaphor. The original opaque image  looked like literal fog, and at first I couldn't understand why they had chosen it. I had used a technical term from game design, thinking it was common vocabulary, and this led to unexpected team behavior. Usually when this happens to me, it's computing jargon, not game design jargon!

Sunday, June 12, 2011

State Design Pattern in Unity with C#

Following up on my initial investigation into game engines, I decided that the best way to get to know Unity would be to build something with it. A few years ago, I wrote a clone of Every Extend called EEClone, which is described in this JERIC paper (ACM DL). I figured a new spin on EEClone would give me a good point of comparison, so I set out to recreate it in Unity.

As I mentioned earlier, one of the strengths of Unity is that it supports a variety of scripting languages, including C#. This intrigued me the more I thought about it, since it meant that it should be straightforward for me to apply my knowledge of Java design patterns in Unity with just a few syntactic changes. Specifically, I decided to dive into the state design pattern, which time and again I have found to be useful in practice. Just as with Java, a quick search of the Web for advice on how to implement state machines in Unity comes up with the classic procedural approach. The costs and benefits of the OO and procedural approaches are documented in the aforementioned paper; for this post, I will focus on the mechanics rather than justification.

Without further ado, this is what the game looks like. The video shows two player-initiated explosions followed by one obstacle collision.



The goal was to explore how the pattern is reified within Unity and C#, not to faithfully recreate EEClone, and so I went with a slightly simpler state diagram than in the Java version:


The astute reader will sure notice where I have cut corners. For example, PlayingState transitions directly to AwaitingRespawnState after the player sets off the bomb, without waiting for the explosion chain to terminate.  Clever handling of explosion chains and scoring was beyond the scope of this example.

The state diagram can be converted into an implementation following the Gang of Four approach, yielding this structure:

Within Unity, this entire structure (modulo the external MonoBehavior) is contained in one source file, StatefulPlayer.cs. The class diagram reveals the two defining characteristics of the state design pattern:
  1. Each state is represented as an object. In this case, each is defined as a unique class, with an abstract class holding common behaviors and implementations, the result of refactoring.
  2. States are responsible for their own transitions.
To illustrate these points, consider AwaitingRespawnState, the point of which is simply to wait for three seconds to elapse before going back to SpawningState:

class AwaitingRespawnState : AbstractState {
    public AwaitingRespawnState(StatefulPlayer p) : base(p) {}
    
    private float elapsedTime;
    
    public override void Update() {
        elapsedTime += Time.deltaTime;
        if (elapsedTime > 3) {
            player.transform.renderer.enabled = true;
            player.EnterState(new SpawningState(player));
        }
    }
    
    public override void OnEnter() {
        elapsedTime=0;
    }
}

The constructor passes its argument to the superclass' constructor, where that reference is kept in a protected field, player. The Update method overrides the default empty implementation from AbstractState and is responsible for keeping track of elapsed time. You can clearly see that when this accumulated time exceeds three seconds, we go back to a new SpawningState.

There is one point where I had to "cheat" a bit, and that is with animation termination. The transition from SpawningState to PlayingState should be triggered by the end of the spawn-in animation. However, I could not find a way in Unity for a general-purpose end-of-animation observer. I could have set a timer and hardcoded it to go off at the same time as the animation, but this would clearly be fragile to change. My solution was to manually insert an event at the end of the animation, and to have this event call the AnimationFinished method of the player—indeed, that is its raison d'ĂȘtre. This method is also used at the end of the death animation, as shown in the state diagram above.

It is laudable that Unity promotes rapid prototyping with its asset-centric development model, which allows you to go in and fiddle with values with very few restriction. Yet, I find it frustrating to have to deal with literals rather than expressions: without such abstraction, one is left thirsty for more refactoring. My approach for handling animation termination is effective, but it does mean that the entire state model has a dependency on two animations, a dependency that cannot be determined by static analysis of the C# code itself. This is an architectural weakness, but it is the cost one pays for using tools like Unity that prevent one from having to hand-code each and every animation.