The Importance of Genre

Over GDC, I talked to a lot of people about genre, being generic, and how important that actually is.

Being a part of a genre (and being generic) is sometimes looked at as a bad thing, especially in many indie developer circles. But, understanding genre, and what genre you'll fit into is extremely important.

Genre is one of the major ways people connect to something new. By comparing their experience to things they've seen in the past, audiences can quickly form a basis for any deviations from generic form. If you present something entirely new, or something that deviates so far from genre that it gives no tangible attachment points, anyone can get easily confused and frustrated. This is consistent in all mediums, including writing, music, movies, and games. Whenever you hear about a work or medium that is "ahead of its time" this is usually because it gave audiences nothing to latch onto from a generic perspective. It takes time for people to be eased into new conventions. Genre is one of the major things that helps them do that.

I came to this realization during GDC, and it explained one of the reasons why I felt Fire Hose's first game, Slam Bolt Scrappers, did not do well. Among other problems, it didn't give players any generic hooks. And in places where a generic hook might have been made ("Ah this is like tetris!") it turned too far away from the convention ("but you make squares, not lines, and of matching colors."). In its attempt to mash up two genres, it missed the boat on creating the generic hooks for either one. When you approach SBS, it's confusing, not necessarily because the game itself is confusing (though it is), but because it doesn't offer a single place were a player can say "AH! This is like Game X." This is something that's worn as a point of pride by many indies, but really shouldn't be.

This is not to say games need to be completely generic, in fact just the opposite. AAA has found itself relying too much on generic convention to hold the weight of gameplay, and it is starting to backfire, if only slightly. When you rely too much on genre, the medium starts to stagnate. People know they've seen it all before, and feel no need to engage with new products. It's only when creators twist generic conventions in interesting ways that the medium can evolve, and even create new genres.

More recently, I've been thinking about the future of the living room, and how the various media companies are vying for dominance. I think that the future of media in general is going to rely on a company taking advantage of their individual genres of consumer fulfillment, and expanding to service new needs and new opportunities. The company that does that well can "win the living room," but all three top contenders are still far from giving us the "genre" that we can attach to, and a few have even taken a step backwards in the process. But, I think I'll leave that to another post.

Using Waf to build an iOS Universal Framework

I've used a lot of build systems, and so far my favorite (when I'm "forced" to use something outside of Visual Studio) is probably waf.  Besides being based on a proper scripting language, waf doesn't feel like it's doing a whole lot of "wicked voodoo magic" behind the scenes (like scons sometimes does).  It's also fairly easy to extend, and we used it to make and deploy all of our Nacl executables.

Today, I figured out a way to make waf do some interesting things concerning iOS and creating libraries / frameworks.

iOS Universal Frameworks require that you create libraries for 2 versions of the simulator (i386 and x86_64) as well as 2 versions of ARM (armv6 and armv7) if you want to run on all of those platforms.  Each of these libraries is then linked together using a tool called lipo.  Waf can easily build one library, but building multiple, especially as part of one build step, can be tricky.  What you do is create a separate environment for each target, then link them together in a single build step. Here's how it goes.

First, waf doesn't understand how to handle .m / .mm files and pass them on to clang properly, so we want to teach it how to do that. The simplest way to do that is like so:

@TaskGen.extension('.mm')
def mm_hook(self, node):
    "Bind the c++ file extensions to the creation of a :py:class:`waflib.Tools.cxx.cxx` instance"
    return self.create_compiled_task('mm', node)

class mm(Task.Task):
    "Compile MM files into object files"
    run_str = '${CXX} ${ARCH_ST:ARCH} ${MMFLAGS} ${FRAMEWORKPATH_ST:FRAMEWORKPATH} ${CPPPATH_ST:INCPATHS} ${DEFINES_ST:DEFINES} ${CXX_SRC_F}${SRC} ${CXX_TGT_F}${TGT}'
    vars    = ['CXXDEPS'] # unused variable to depend on, just in case
    ext_in  = ['.h'] # set the build order easily by using ext_out=['.h']

This will use whatever the CXX compiler is set to (which we'll set to clang) using MMFLAGS over CXXFLAGS or CCFLAGS. Everything else will stay the same. This can be added anywhere in the file, but I tend to add it under configure, before build.

Next, we set up configure to create an ios base environment, off of which we're going to base other targets.  Because I use this script to build for multiple environments on multiple operating systems, I check to make sure that this is being performed on Mac OS before creating the environment.

IOS_MIN_VER = '6.0'
IOS_SDK_LOC = '/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneSimulator6.0.sdk'
IOS_SDK_SIM_LOC = '/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator6.0.sdk'

def configure(conf):
    # ... Any other configure steps
    
    # Make sure we're running on a Mac
    if sys.platform.startswith('darwin'):
        conf.setenv('ios')
        # Set the C++ plugin here to use clang over gcc  g++
        conf.env.CXX = 'clang'
        conf.env.CC = 'clang'
        conf.env.LIPO = 'lipo'
        conf.load('gxx')
        conf.load('gcc')
        # set any CFLAGS, CXXFLAGS, MMFLAGS and DEFINES for ios in general

Next, we create an environment for each target (debug or release) and each platform (i386, x86_64, armv6 and armv7). If you're not targeting an x86_64 simulator or an armv6 device, you don't need to build these, but since post first build this is fairly quick, I just build all of them.

        for arch, tgt in itertools.product(['i386', 'x86_64', 'armv6', 'armv7'], ['dbg','rel']):
            conf.setenv('ios')
            newEnv = conf.env.derive()
            newEnv.detach()
            conf.setenv('ios_' + arch + '_' + tgt, newEnv)
            #... set any target specific flags 
            # These flags set up the sysroot for ios based on simulator or device
            if arch.startswith('arm'):
                addflags = [ '-mios-version-min=' + IOS_MIN_VER, '-isysroot' + IOS_SDK_LOC ]
            else:
                addflags = [ '-mios-version-min=' + IOS_MIN_VER, '-isysroot' + IOS_SDK_LOC ]
            conf.env.CCFLAGS += addflags
            conf.env.CXXFLAGS += addflags
            conf.env.MMFLAGS += addflags    
            conf.env.ARCH = arch

Now we have to create commands that use each of these environments. This is exactly the same as using multiple variants / platforms, if you've seen that example. The other thing we're going to do is make a LIPO command for each target, so we don't have to re-specify how to LIPO our libs together (this is useful if you've got multiple like I do).

def init(ctx):
    # any other architectures / platforms

    for arch in ['i386', 'x86_64', 'armv6', 'armv7']:
        for tgt in ['dbg', 'rel', 'retail']:
            for inheritingClass in (BuildContext, CleanContext, InstallContext, UninstallContext):
                name = inheritingClass.__name__.replace('Context','').lower()
                class tempClass(inheritingClass):
                    # this is used in the next step to actually call the step
                    cmd = name + '_ios_' + arch + '_' + tgt
                    # This must match your environment name, and sets the output directory
                    variant = 'ios_' + arch + '_' + tgt  
                    # These are for testing in your build script, if you need them
                    architecture = arch
                    target = tgt
                    platform = 'ios'

      for tgt in ['dbg', 'rel', 'retail']:
          class tempClass(BuildContext):
              cmd = 'lipo_ios_' + tgt
              fun = 'lipo_ios'
              variant = 'ios'
              target = tgt
              platform = 'ios'

Last, we create a build_ios command to build all the variants for a target (I default to release), and a command called lipo_ios to LIPO each one together. Here's how it goes.

def _lipo_lib(ctx, lib_name):
    ctx(rule='lipo -create ${SRC} -output ${TGT}',
        shell = True,
        target = 'lib%s-%s.a' % (lib_name, ctx.target),
        source = ['build/ios_%s_%s/lib%s.a' % (x, ctx.target, lib_name) for x in IOS_ARCHS]
    )

def build_ios(ctx):
    for x in ['i386', 'x86_64', 'armv6', 'armv7']:
        waflib.Options.commands.append('build_ios_' + x + '_rel')

    waflib.Options.commands.append('lipo_ios_rel')

def lipo_ios(ctx):
    """ This creates universal libraries in ios_target made from the architecture 
        builds of each lib. """
    _lipo_lib(ctx, 'A')
    _lipo_lib(ctx, 'B')
    _lipo_lib(ctx, 'C')

So this is not at all straight forward, and took a lot of trial and error, so hopefully this helps some people.

The upshot here is that waf's dependency detection in terms of libraries is way better than XCode's, so you can use multiple dependent libraries together and it will only ever rebuild exactly what's necessary. However, the preprocessor that determines include dependency only supports C / C++, not Objective-C's #import directive. So I would avoid doing this if you're working a lot with Objective-C dependencies, at least until waf gets real objective-C support.

Also, you could use these same principles to create build_ios_simulator and build_ios_device targets instead, without too much effort, but I like having all targets build into the universal libs, and going from there.

More Speaking Engagements: UVA, VCU and AltDev

I've got a bunch more speaking engagements lined up!

First up, the weekend of November 10th I'll be speaking at the Alt Dev Student Summit, with a talk titled OOP Is the Root of All Evil, of course riffing a bit off of the phrase "pre-mature optimization is the root of all evil." The idea behind this talk is that pure OOP, especially OOP that doesn't think about how data is going to be used or the performance tradeoffs incurred, is pre-mature pessimization, and that frequently the argument that OOP makes more understandable, more reusable, or more maintainable code is frequently an illusion. I'll also be looking at how certain OOP patterns actually promote bad coding practices, but are used because they're considered OK from a pattern perspective.

Earlier, on October 30th, I'll be speaking at UVA to the Student Game Developers club, doing a dry run of my talk to the AltDev Student Summit.

Lastly, on November 6th, I'll be speaking at VCU to their chapter of SIGGRAPH. This will be the same talk I gave at JMU earlier this year called Getting Started Making Games. I am still planning on recording a shortened version of this talk for posting online, and I still need to post the slides. This will hopefully happen shortly.

More on Open Offices

After my talk with Mike on Leadership, we got into a discussion about open vs. closed office spaces. Mike is in favor of the open office while I (obviously) am not. Mike's assertion (which I actually agree with) is that you can create an interuptionless culture in an open office space, and still get the benefits of an open space that removes the psychological barriers to communication. In addition, Mike asserts that, if a person knows they're going to have to have lots of uninterrupted time, they can always work from home (not necessarily true, but I'll give him the benefit here).

These are two just fundamentally different approaches: one that favors open communication, and creative collaboration, versus one that favors individual productivity, potentially at the expense of team productivity. However I believe that closed offices are better for two reasons.

First, I believe flow and interruptionless work are important to personal productivity and job satisfaction. This doesn't mean that I believe that everyone should always go uninterrupted all the time, but that a culture that minimizes these by default is important.

Second, because I believe it is easier to foster an open culture in a closed office than to foster a closed culture in an open office. What this means is that I think that you can work very hard to foster a culture of few interruptions and respecting people's time and space in an open office, but that when it comes down to it, the open office itself works against this. In contrast, while a closed office makes open culture harder, but does not necessarily work against an open culture, especially if said office provides open spaces as well.

With all of this said, Mike also pointed out one very key point:

Most likely, neither Mike nor I are ever going to be in a position to actually be able to plan out an office.

Speaking at JMU: Getting Started Making Games

Next Wednesday (October 3rd) I'm giving a talk at James Madison University to their ACM. Although they've labeled it a "Tech Talk," my actual talk is going to be much more general, titled Getting Started Making Games. It's inspired by this talk and website, but moves beyond what tools you can use and resources are available to give some real practical advice on how to make your first games, and what constitutes a reasonable first game. Then I'll tackle where you can go from there, and discuss techniques for failing early, and avoiding productivity black holes.

According to the ACM people, the talk is open to non-students so if you're in the area, drop by HHS 1302 at about 7:30 and say hi!

Thoughts on Crunch

Crunch has been coming up in the news again, mostly because of the Team Bondai issues, and generally, I like to avoid talking about issues like this on my blog, but I figured I'd share some of my thoughts and see what people think. As a disclaimer, I have never gone through what anyone would call a "death march," which is what Team Bondai had to deal with. I've had to do crunch, but never on a huge magnitude, which I'll get to. I'm also going to take what some would say is a "controversial" stance on crunch. Here's the not controversial part: crunch is always bad, never required, and always avoidable, and a team should do everything it can to avoid it. But it's not always that simple.

Types of Crunch

The thing is, there are lots of types of crunch. And they're all different, and affect the team in different ways, and on different levels. In my mind, there are three major types:

  1. Self-imposed crunch: This is the crunch that some people in the game industry do on their own accord. They stay late every night because they want to. They want to get that one last thing in, they want to polish the hell out of whatever, or get that one last boss battle in. This is a personal choice, and there's really not much you can do about it. However, it should be clear to other team members that this type of behavior is not condoned or required. Don't reward them for this behavior. Otherwise you get into the second type of crunch.
  2. Team de-facto crunch: This is where the team "decides on its own" that it's going to crunch, when the culture necessitates it, or when management does nothing to stop it. When too many people start self-imposing crunch, other people at the team will start to look down on those that "aren't working as hard," and peer pressure will cause them to stay later, or work longer. This is particularly true in companies with lots of younger workers who are not only particularly vulnerable to this type or peer pressure (they really want to impress their superiors) and also believe that this is just the way the game industry is. If this bubbles up to management, there becomes this situation where long hours are expected, and people that don't do them are considered less valuable employees. Don't let this happen. As a manager, it is your job to make sure self-imposed crunch is seen as an anomaly, and to do your best to keep people from doing it.
  3. Mandatory Crunch: Just what it sounds like. Management, for some reason, requires longer hours. This is the most common form of crunch we hear about, and the worst. To producers / managers out there, just because you do the same hours does not make mandatory crunch okay. Actually, nothing really makes mandatory crunch okay. There are no excuses here. Mandatory crunch is always a failure somewhere down the pipe.

The first two types of crunch are cultural and occur at all studios, but can be mitigated once you recognize the warning signs.

In addition there are three levels of crunch:

  1. Mitigated crunch: The increase in hours is not huge, maybe 10 extra hours of work during the week, the length is known and can be planned for.
  2. Long crunch: The increase in hours is high. Working 60 to 80 hours during a week with no break. The length can still be known, but may be a long way off.
  3. Death march: The increase in hours is high, and there is no end in sight, or the end is repeatedly pushed back.

A Mandatory Death March is the worst type of crunch, and when we talk about "crunch" we're usually talking about mandatory long crunch or mandatory death march. There are no excuses for them, but we should take a look at the causes to figure out if they're avoidable.

Causes of Mandatory Crunch

Everyone looks at crunch as a failure of management. I agree, but there are a lot more factors to it than that. Here, in my mind are the general causes of crunch.

  1. Company Policy: The Epic model. There are no important deadlines coming up, there's nothing that's actually requiring crunch, but the company has decided to require everyone always work 60 to 80 hour weeks. Not okay. There is no excuse for this other than management being completely inept. It affects work quality and general quality of life. Don't do it.
  2. Bad Information: This is what happened to Team Bondai. They were constantly told (and believed) that the game was almost finished. In such cases, many team members, believing the end is in sight, will do self-imposed crunch, which eventually will lead to de-facto crunch. In addition, this is where a death march starts, and tends to never end.
  3. Bad Planning: This is the most common cause of crunch, and management gets rightly blamed for it all the time. But there are two types of bad planning:
    1. Bad Low Level Planning: Even the best management team can make a mistake, because they can only make decisions based on what they're told by the people below them. If what they're told is wrong, their planning will be wrong. So, if you're at the low level, don't fudge your numbers. Tell management the truth so they can make the right decisions.
    2. Bad High Level Planning: Even with all the right information, managers, especially bad managers, will plan incorrectly.
  4. Shifting Requirements: Even with good planning and good information, games are a highly creative and highly iterative product, and when something's not working or isn't fun, it has to be changed. Unlike other industries where you can work "to spec", the "spec" of a game is frequently fluid, which makes planning for the long term almost impossible. This, unlike the other causes, isn't anyone's fault, per-se, since a team can come to the mutual decision that something needs to be reworked, but it's something that frequently gets ignored while doing planning
  5. Deadline: When all is said and done, this is the real cause of crunch. When a deadline is involved any amount of error in planning or requirements hits a head when the deadline looms. Some studios are in a position where the deadline can be pushed, but not all, especially when dealing with a multi-game publisher that has to allocate resources at various times (QA, marketing, operations, etc) to make sure the game launches without a hitch, so they can move on to the next one. In these situations, moving the deadline is impossible.

The Myths: Crunch is a Given and Crunch is Always Avoidable

Both of the above statements are wrong. If the causes of crunch are listed above, avoiding crunch is as simple as avoiding those mistakes. Don't make crunch company policy, always give your employees all the information, plan well at all levels, have a solid design document, and don't have a hard deadline. Simple. But, if any of those slip, you have to make a hard decision: cut features, cut polish, or crunch.

If your company has a lot of experience or a lot of leeway, you can always avoid crunch. However, if you're a start-up working for a publisher, on a hard launch deadline, under contract, and you want to make the best game possible, chances are someone, and it's not necessarily management, is going to make a mistake, and that mistake is going to cause some amount of crunch.

Was it avoidable? Yes, probably at some point it was, and yes someone made a mistake somewhere. Crunch is always a failure somewhere down the line. But at some point in the project, it will become obvious that some amount of crunch will be needed. The next step is to mitigate it.

Mitigating Crunch

Crunch does not always mean Quality of Life or Quality of Work has to suffer, it's just that in most circumstances it does, because of the way it's implemented. I have gone through two sets of crunch, both mandatory, one long, one mitigated, and both sucked. But there are a few things that I found helped me, and I think would help others, get through any period of crunch

  1. Provide All the Information: Inform everyone how much more you expect them to work, what needs to be done, and where the deadline is. If ANY of this information changes, make sure the team knows. I have been in and heard about crunch situations where information about deadlines or cut features was withheld, and this only makes people angry
  2. Allow people to choose their extra hours: People are different and have different home lives. Some people work better in the morning, others at night, others over the weekend. Do not blanket ask everyone to stay late. Instead, let them choose when to work and they will choose times that will not only make them most productive, but will interfere with their lives the least.
  3. Allow people to work from home: Set up your infrastructure to let people work from home. Some people can be just as productive at home as they can be in the office, and this removes the stress of a long commute and improves QoL since they can be around for their loved ones.
  4. Allow people during longer periods of crunch to take some time off: This is huge for me, and I've never been anywhere that does this. If your crunch is going to last a long time, or looks like it will need to be lengthened, give people a day off. Say "Okay, everyone can take a day off free sometime in the next two weeks." Giving them a time frame allows them to plan for things like short trips, or time with their family. They'll come back rested, happier and more productive.
  5. Make sure they have and know the reward: In a start-up, the reward is in the stock, but in larger companies, make sure people know they will be compensated of extra hours, and then compensate them.

Hopefully this makes sense. I don't like crunch either, and it is avoidable for companies that have really experienced teams and a certain amount of give, but some companies do not and will not have the luxury to avoid crunch entirely. Obviously, great companies will always avoid crunch, usually because they've learned from their mistakes. But realize that a good company may crunch, but will always find a way to mitigate it when and if it happens, and will learn from their mistakes on their way to becoming a great company.

The Post Where I Update My Blogroll

I was at Northeastern last night talking with programming students about getting into the game industry.  As with most students, they were very curious about what, specifically, they should learn in order to make it easier for them to get into the game industry, and where they should learn it.  I gave some standard advice (which I'll probably write up a blog post about at some point, maybe?) but I realized that one way to find really good places to learn is through the blogrolls and recommendations of talented developers.

And then I realized I haven't updated my blogroll, probably in a year at least.  So, I've updated it this morning, fixing links to places like t-machine, removing some long dead blogs, and making a few additions.  Some recent additions:

  • Games From Within: Noel is now an iPhone developer, but his development methodologies are really what I look at in his blog.  He updates fairly frequently and his articles are almost always good.
  • Bitsquid: I'm not sure really what Bitsquid is working on, but their programming posts are fantastic.
  • Insomniac R&D: Awesome posts from Insomniac devs
  • #AltDevBlogADay: There is so much awesome in this (basically aggregate) feed that I often I can't keep up with it.
  • Brett's Footnotes: Brett is a lead programmer at Bethesda Game Studios, but wasn't there when I was.  He's awesome and his posts are always interesting.

I will do my best to keep my blogroll up to date from now on, though I can't make any promises.

Data Driven Programming Talk

I've posted my talk on getting the most out of data driven programming that I gave at Game Forum Germany a few weeks ago on my website. The talk recommends reflection as solution to a lot of the potential pitfalls of needing a single editable location for fields and properties and, interestingly, fellow Toolsmith Geoff Evans has an article in Game Developer this month about that same topic, and specifically how they solved it in Helium. So if you're thinking about using reflection to do automatic UI generation and serialization, definitely give that article a read.

I've been told that there will be video of the talk at some point, and I'll let you know when that's available.

Posted Without (Too Much) Comment

I've been talking on twitter about working on some stuff involving 2D soft shadows. I was going to write a long blog post about how I got 2D soft shadows working in XNA using the GameDev article in combination with Catalin's implementation and Christian's implementation in D. However, trying to write the post, I wasn't sure if I could make it any clearer, as I'm not sure if I understand it completely myself.

So, instead, I'm just going to post the XNA port I made almost directly from Christian's D implementation without much comment. I will say that, instead of using the images that both Catalin and Christian are using, I've move most of that into shaders. This isn't necessary, but it's in preparation for some other stuff I want to do later with this project.

So if you're interested, my XNA implementation is here. Enjoy!

Life.Update

It's been a while since I posted, but a lot's been happening. If you've visited the site, you probably noticed a theme change, but more importantly, you may have noticed I am no longer at Orbus Gameworks.

There are many reasons I decided to leave Orbus, and maybe some day I'll write a long blog post on the subject, but the short story is that I decided it was time for me to get back into creating games full time. Although Orbus focused on game middleware, the technology we were working on wasn't really game development, and I felt like I was getting more and more behind in the skills and systems that are essential to being a programmer in the game industry.

So I have moved on to another start-up, but this time a game studio: Fire Hose Games.

Working at Fire Hose has been an adjustment, though not a bad one. Because of the size of the team and the schedule, I've been forced to think and program very differently from what I'm used to. It's pushing me out of my comfort zone, which is always a good thing, if only to see different ways things can be accomplished, even if they don't jive with the way you work as a programmer.

Generally, I'm really looking forward to my work with Fire Hose, I'm excited about our product, and I'm looking forward to having shipped another game.