Dungeon Ho!: Unity – Dev Blog #9

Welcome back, flag flippers! Rather than leave you all hanging, I decided to follow up yesterday’s dev blog with a short detour into the wonderful world of boolean operators. This is how the Entity flags are stored, so it’s a pretty useful topic for the aspiring Roguelike programmer. I first learned how to do this by browsing the Quake II source code nearly twenty(!) years ago, but obviously the technique goes back way farther than that.

As I’m sure you already know, numbers are stored in variables as binary values, which means that they’re converted from base 10 (1, 2, 3, 4, etc…) to base 2 – so, ultimately, they’re represented as strings of 0’s and 1’s.  Boolean operations let us act on these variables at the bit level (IE, on the 0’s and 1’s themselves).  We can use this trick to store a bunch of values in one single integer (or double, if you need more) that we’d normally need a stack of boolean variables to represent.

The trick is, each bit’s position in the integer is equivalent to the number 2 raised to a particular exponential value.  For example, 20 is 1. 23 is 8.  When you convert those resulting numbers to their binary representations… they just so happen to place a single 1 into the integer at the exact position of the exponent (starting with zero, of course; computers are funny that way).

For example, as I just said, 20 is 1.  1 in binary is, obviously, 1 – which means that if we were to represent the other 31 bits in the integer (assuming a 32-bit integer) with zeroes, we’d have:

00000000000000000000000000000001

23 is 8.  8 in binary is 1000.  So, once again, padding that out with zeroes we have;

00000000000000000000000000001000

Get the picture?  If we look at each ‘1’ in our integer as a separate flag, we can store 32 different flags in a single number!

…and that’s what we do.  Here’s the EntityFlags.cs file.

public class EntityFlags
{
    public static int EQUIPPABLE                 = (int)Mathf.Pow(2, 0);
    public static int CURSED                     = (int)Mathf.Pow(2, 1);
    public static int CORPSE                     = (int)Mathf.Pow(2, 2);
    public static int UNDEAD                     = (int)Mathf.Pow(2, 3);
    public static int NO_DROP                    = (int)Mathf.Pow(2, 4);
    public static int EQUIPPED                   = (int)Mathf.Pow(2, 5);
    public static int TWO_HANDED                 = (int)Mathf.Pow(2, 6);
    public static int CONSUMABLE                 = (int)Mathf.Pow(2, 7);
    public static int REQUIRES_AMMO_ARROWS       = (int)Mathf.Pow(2, 8);
    public static int REQUIRES_AMMO_BOLTS        = (int)Mathf.Pow(2, 9);
    public static int STACKABLE                  = (int)Mathf.Pow(2, 10);
}

Note that I could have done the math myself and simply defined the values as their actual base 10 equivalents (eg. set the value of CURSED to 2), but why bother?  That’s why we have computers.  They don’t make mistakes.

So, how do we set the flags?  Well, remember your boolean functions.  If you OR a 0 with a 1, you get 1.  So if we take a flag variable (such as the flags member of our Entity class) and simply OR it with one of the flag constants, if the flag bit isn’t already flipped, it will be.  For example;

Say our entity.flags variable is currently set to some binary value;

00110001110000000000000000011000

…and we want to set the CORPSE flag (22, or 4 – which is 100 in binary).  If we OR the two variables together (the relevant bits have been bolded)…

00110001110000000000000000011000 - (original number)

00000000000000000000000000000100 - (flag value)

00110001110000000000000000011100 - (result)

See what happened?  The 0’s in the flag’s value didn’t affect the already-set 1’s in the original variable because 0 OR’d with 1 is already 1.  And since the original value had 0 in the relevant position, ORing it with the 1 from the flag flipped the bit to 1.

Sorry to belabor that point but sometimes it takes a while for people to wrap their heads around it.  I know it sure did for me back in the day.

Anyway, now that we can do that, unsetting a flag is just as easy.  We just use an “exclusive OR” instead of the “inclusive OR” that we used to set it.  The exclusive OR only outputs 1 if both bits are different, so if we OR the flag with the variable and that bit is already set, then both elements of the OR are 1, so the resulting bit gets flipped back to 0.

And finally, we can see if a flag is set by ANDing the flag with the variable, since AND only produces a 1 if both bits are the same.  So ANDing the flag will produce a 1 in the flag’s bit position if the flag is set (since both the flag and the bit already at the flag’s position are both 1) and 0’s everywhere else because the 0’s in the flag ANDed with the existing bits in the variable will “clear” all of the other bits; 0 AND 0 are still 0, while 0 AND 1 are also 0.  That means that if the flag had been set, the result of the AND will be a non-zero value (since we’re left with the single flipped bit) and a 0 if it hadn’t (since all the bits have been ANDed to 0).

Clear as mud?  Fortunately, the code to handle these three operations is dead simple to implement. Here are the relevant functions from the Entity class. Naturally, the variable that stores the set flags is called flags.

public void setFlag(int flag)
{
    flags |= flag;
}

public void clearFlag(int flag)
{
    flags ^= flag;
}

public bool hasFlagSet(int flag)
{
    return ((flags & flag) != 0);
}

 

That’s all for today.  Tune in next time for a quick dive into the dungeon’s entity-querying methods and a brief intro to the UI system, which will be necessary to understand the dialog we’re going to build in order to (finally!) pick up items.

-Steve

Advertisements
Categories: Development, Dungeon Ho!, Unity | Leave a comment

Post navigation

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

Blog at WordPress.com.

%d bloggers like this: