Wednesday, May 14, 2014

GuildCraft Dev Diary: Part III

This post is about FatEnums.

I love working with C#. My fondness for the language was the primary reason I picked Unity over other game engines for this project. However, there is one aspect of the language where I feel that Java has a far stronger implementation: enums.

In C#, enums are basically just an integer + strong typing. There are some additional features you can use to mark them as bit flags or convert the integer value to and from its string name. While at first blush this sounds useful enough, its limitations quickly become apparent. Having the string representation of your enum name be what is displayed in logs or to a user is very limiting. It creates problems with spaces, punctuation, and localization. This results in writing a lot of switch statements or using the enum value as an index to a map of useful properties.
( http://msdn.microsoft.com/en-us/library/cc138362.aspx )

In Java, enums extend a special class which allows them to carry along an additional information and methods. Access to this information can be done directly from the enum, instead of using a large number of helper methods.
( http://docs.oracle.com/javase/1.5.0/docs/guide/language/enums.html )

  • Aside: The C++11 implementation of "enum class" is very disappointing. I have no idea why they allowed such a limited implementation into the specification. The steering committee should have added Java-style enums instead of just strong-typing. Their failure to do so suggests they need to research other languages.

Creating Java-like enum functionality in C# is a popular question on Stack Overflow. When I first started using C#, I was able to get most of the functionality that I was looking for by using the "public static readonly members" pattern. It didn't do everything I wanted, but it was enough for me to set the problem aside and move on to other things. Later, I implemented a version that allowed for one enum type to inherit from another. In practice, composition proved far more useful when dealing with multiple enum types that were worth joining together.

What does this have to do with building an RPG?

If you pick up an RPG rule book, you will see that it is full of game pieces that are best represented by enums. Enums that also contain extra information about how they behave:
  • Damage types
  • Feats
  • Classes / Professions / Path Options
  • Skills
  • Spell lists
  • etc.

When I work on my own projects, I try to experiment with new design patterns. In a corporate environment, there is usually an established direction for a codebase or some level of group-think that prevents exploration. With GuildCraft, I decided to try pushing the use of enums as far as I could.

Requirements for enums to be as useful as possible:
  • Concise syntax that avoids namespace pollution
  • Foreach loops and set operations
  • Provide direct member access
  • Group different types through composition
  • Switch statements

The end result is a pattern that I refer to as FatEnum and have made available via open source at my GitHub account. I call it FatEnum because it uses more memory than a standard enum. Instead of a single integer, each enumerated value stores two strings and two integers. (This is partly to allow for and speed up sorting operations between enums of different enum types.) Even though in relative terms a FatEnum takes up more memory than a normal enum, in absolute terms it is still fairly small and a fixed amount that doesn't increase as it is used. More importantly, for me the productivity benefits it offers are far more valuable than a few bytes of RAM.

I have made the FatEnum code open source and available on my public GitHub repository.
http://github.com/jameswalkoski/FatEnum )

If you are interested in how it works "under the hood" you can see that in the git repo. Here are some code snippets of it in use:

Defining a Planet enum. Categories of enum types can be implemented via interfaces.

Foreach loop and direct member access.

Set operations.

Switch statement.


Jim



No comments:

Post a Comment