Gamasutra: The Art & Business of Making Gamesspacer
arrowPress Releases
July 23, 2014
PR Newswire
View All
View All     Submit Event





If you enjoy reading this site, you might also want to check out these UBM Tech sites:


 
Five Coding Lessons Iíve learned from Joe Houston
by Kain Shin on 01/27/13 08:49:00 am   Expert Blogs   Featured Blogs

The following blog post, unless otherwise noted, was written by a member of Gamasutraís community.
The thoughts and opinions expressed are those of the writer and not Gamasutra or its parent company.

 

I have been a professional game programmer for over 12 years... but in the first weeks of working at my most recent job, I’ve come to realize just how much I’ve internalized at my previous job from sitting next to a certain ex-coworker for the last three years. It is enough to make me want to write about it and specifically credit him by name.

I no longer work with Joe Houston. It is unlikely that I will ever work with him again... I have quit that job we had together in order to pursue an opportunity in a completely different city, and he has recently quit that job to find his calling as an indie game developer. Assuming both of us succeed in our life goals, we will probably never work together again.

His worthy Kickstarter campaign ends on February 13th, 2013:
http://www.kickstarter.com/projects/1599677835/unwritten-that-which-happened
I backed it.


Lesson #1: Defer Your State Transitions
To calibrate our vocabulary, here is a gross simplification of what I mean when talking state machines...A finite state machine is used to keep mutually exclusive bodies of code decoupled from each other and bookended by predictable “Enter()” and “Exit()” functions for each state. Technically, a switch statement inside an Update() function can be a state machine, but when your states become more complex, you will want to encapsulate that code within “class State” and make sure it is managed by “class FiniteStateMachine”.

In a State Machine, you have two very different choices on how state transitions work... or in other words, what happens inside the scope of “FiniteStateMachine::RequestStateChange( stateID )”

Instant Transition Deferred Transition
  • State changes happen instantly at the time of the change request.
  • A call to GetCurrentState() on the next line of code would return the value of the new state.
  • State changes happen at a consistent spot within the update loop (PostUpdate) no matter where in the loop that change request was made.
  • A call to GetCurrentState() on the next line of code would return the value of the old state, and so you may feel the urge to implement “GetCurrentOrPendingState()”.
Pros for Instant Pros for Deferred
  • Easy call stack.
  • Intuitive mental model.
  • Initially faster to implement
  • Easier to trust the state snapshot as an atomic indivisible frame of execution
Cons for Instant Cons for Deferred
  • Current state is ambiguous if you request state change while inside a state's update function.
  • Unpredictable state snapshot because transition can occur at any time while that snapshot is being affected by other systems and entities within the game world. In other words, the order within a for or while loop can yield very different gameplay results.
  • Bugs that come from this may not be 100% reproducible due to the many factors that govern order of processing within an iteration loop of the different objects and systems within the simulation.
  • Call stacks resultant from state transitions will not include the actual request that instigated the transition since that happened sometime in the last frame
  • Must deal with all issues stemming from mismatch of expectations between pending state and current state, such as requesting a state change when one is already pending.
  • Dealing with the issues above require cooperation and buy-in to the deferred mentality from all programmers interacting with the state machine.

Joe and I had a debate about this early in the project. I was in a hurry and wanted a simple call stack. He insisted that we’d pay for that simple callstack down the line with very difficult and subtle bugs. His explanations detailed those listed above. After that, I was convinced that deferred was the way to go. There were other types of bugs coming from adopting deferred transitions (listed in Deferred Cons above), but in the end, it was the right decision.

A bug in which the state is wrong is much easier to fix than a bug in which a state is sometimes partially wrong depending on the order that objects get handled within a loop.

If you happen to find this explanation a bit too simple, you can read deeper thoughts on this subject from the source:
http://stateofhouston.com/2011/04/10/thinking-your-way-out-of-tick-order-hell/

I also wrote a somewhat robust Deferred FSM here:
http://ringofblades.org/Blades/Code/DeferredFSM_CPP.zip
You are welcome to download, pillage, and modify that code (and its comments) in whatever way suits your needs.


Lesson #2: Favor Blending Forces Over Logic Branching When Driving A Vector
Have you ever had a direction vector that was influenced by many things? Maybe when X happens, the vector is in one direction and then when Y happens, the number is smaller and/or in another direction?

Instead of making a state machine or mess of if-else statements to determine the value of that floating point number, consider visualizing a force that represents X and a force that represents Y. Instead of turning those forces on and off at the right moments, make X and Y get stronger with proximity to relevance and gradually zero out when they get far enough to become irrelevant. X and Y can add up to reach an equilibrium.

Examples of Blending being better than all-or-none branching logic:

  • Steering Influences, such as pathfinding mixing with combat formations
  • Camera Influences
  • Animation influences

Blending force model strategies are not the panacea for all situations. Sometimes, you honestly do need priorities to gate contributions from certain influences, but this is definitely a good place to start when designing any code architecture that must convert input from many systems into a single output.

If you are given a spreadsheet of situations by a designer that dictate the value of certain resultant numbers by row and column, then maybe it is time to consider a mathematical model that can be continuous from parameters 0.0 to 1.0 instead of an approximation governed by a table. A cooperating systems designer may resist at first, but eventually thank you for this.

This mental transition to start with force blending instead of turning switches on and off is now a natural instinct for me, but it initially required jolts of reasoning from various sessions in which I “borrowed Joe’s brain”.


Lesson #3: Step Mode

  1. Press a certain key and the game pauses
  2. Press that same key again and the game advances ONLY one frame
  3. Press that key as many times as you want to advance the game one frame at a time
  4. Press another key to resume the game
  5. Make sure the framerate is capped to 1/30 seconds per step delta or whatever you think normal simulation should be
  6. For a nice twist: Allow other debug tools such as “freeflymode” to work in combination with this in order to be able to teleport the player anywhere within a single frame.

This is an indispensable debug tool that I and other programmers ended up using many times to investigate aberrations in systems that drove state changes as well as anything else with very intricate events happenings between frames A and B. Joe is the one who introduced the value of such a utility to me after implementing it within our codebase.


Lesson #4: Seize Opportunities to use Square() Instead of Sqrt()
It’s the little things.... This habit finally sunk in after numerous code reviews.

Square root is a more expensive CPU operation than multiplication.

  • SLOW:if ( myVector.Length() < myValue )
  • FASTER:if ( myVector.LengthSquared() < Square(myValue) )

The difference in cost between Square and Sqrt (hidden within myVector.Length) is meaningful enough to show up on profilers running benchmarks on 2012 hardware.

Those uses of Length instead of LengthSquared can add up, and so I now favor this pattern when doing distance-related comparisons as well as interpolations that allow quadratic ratios instead of linear ratios.


Lesson #5: Supporting Data Inheritance is Critical
One way in which programmers use class inheritance is to avoid the need to copy and paste code when sharing functionality between derived class types.

Systems designers have the same need to do this with the complex balls of data they create. They make a ball of data that represents a base object, and then one or more other balls of data might represent derived versions of their original ball that inherit properties from that ball or one of its ancestors.

Programmers have a choice of using inheritance or composition to share functionality between objects. Designers are sometimes only given the choice of using composition to manage their data.

A lack of support for data inheritance will result in designers copy-and-pasting large complex balls of data when they need to make a variant... and this will likely result in that odd bug that only happens in one level because they missed a spot or a spot changed in an ancestor somewhere up the tree that exists only in their head or wiki page at best.

The importance of data inheritance did not become a prevalent concept for me until Joe implemented it and others used it, including myself.


Being THAT Guy
I learned more from Joe than the five lessons listed here, but those other lessons are either too specific to the game we were working on or too esoteric for consumption by the general public.

Writing this memorializes my time working with a person that I consider the best game programmer that I have ever worked with. It crystallizes the lessons I’ve taken from him and reminds me that I am constantly evolving with the people around me.


Related Jobs

Sony Online Entertainment, San Diego
Sony Online Entertainment, San Diego — San Diego, California, United States
[07.22.14]

Intermediate and Senior Database Tools Programmers
2K
2K — Novato, California, United States
[07.22.14]

Senior Engine Programmer
Turbine Inc.
Turbine Inc. — Needham, Massachusetts, United States
[07.22.14]

Cloud Solution Architect
2K
2K — Novato, California, United States
[07.22.14]

Senior Animation Engineer






Comments


Axel Cholewa
profile image
Interesting article! But I do have a problem with one sentence (probably nitpicking, but I'll write it anyway):

"Programmers use class inheritance to avoid needing to copy and paste code when they need to share functionality."

That's not everything inheritance is for. Before trying to make games as a hobby I was working in particle physics using a huge code library for analysing our data. The library was written in C++. Unfortunately, before people started writing said library, they were coding only in Fortran. So the creators of a program library used by tens of thousands of scientist was written by C++ beginners who had no clue at the time what object orientation really meant. And it showed.

For example, you had a class called Graph. It was used for drawing graphs. So far so good. But it inherited from a class called AttLine - short for line attribute. The idea was that a Graph shares functionality with line attributes -color, thickness, style etc. - and in order to save code they let Graphs inherit from AttLine. But of course, that does not make sense at all! A graph IS in no way an attribute of a line, and therefore it shouldn't inherit from AttLine.

Inheritance is supposed to create an "is a" relationship, while my example is a clear case of a "has a" relationship. A Graph should "have a" line attribute, but it "is no" line attribute.

Phew, needed to let some old steam off ;)

That said, good luck with your future endeavors :)

Kain Shin
profile image
You speak truth. I did not realize that there was an implication of using inheritance for everything. I've edited that section to make it clear that I am not saying that inheritance is the only thing that programmers use to avoid copy-and-paste. Thank you.

tony oakden
profile image
Great little article. Some good tips and some good revision. I agree with #2 and it's interesting because it reminds me of something Chris Crawford said in his book, Chris Crawford on game design. Can't remember the exact quote but his point was that in game design systems it is much better to use continuous functions than conditionals because conditionals cause abrupt changes in stats which the player will usually find and either exploit or be irritated by. The real-world doesn't usually have such discontinuities (well except for birth and death :) ).

Andrew Williams
profile image
Nice little article. Thanks.

Evan Bell
profile image
#4 is helpful to defer the "death by 1000 cuts" optimization problems. But #4 can also cause subtle issues because length^2 is quadratic not linear. I would replace it with "profile often". Even if you don't take action it is good to know where the time goes so you can make educated guesses and prioritize optimization tasks. Also helps you spot sudden and unexpected performance degradation.

My nomination for 6) Put a break point on every line of code you touch and every branch. Exercise all of them and remove them as you go. Can find a lot of easy to miss mistakes that have a habit of snowballing when you are trying to get your E3 build out the door.

Great article makes for great lunch time reading!

Ayyappa Reddy
profile image
Good Read!
but probably i couldn't get Lesson#3 though...


none
 
Comment: