GDC 2010 proceedings
March 9, 2010 – 11:49 amI’ll keep updating this post with links as I find them.
Random gamedev ramblings
I’ll keep updating this post with links as I find them.
Data breakpoints are one of the most helpful debugger features when trying to hunt for memory overwrites/ninja variable modifications. In majority of cases it’s enough to set them up from debugger, however, there are situations when it’s not possible. Sometimes breaking into debugger changes program behavior (I had this problem just yesterday), sometimes we don’t want to catch every variable access, just some of them (as others are legal). In situations like that we need to set data breakpoints from code. Read the rest of this entry »
Simple technique that can help in making code more future-proof and easier to modify. Applicable in most situations where two subsystems need to exchange informations. Let’s imagine the following scenario – for some reason we need to introduce “unlimited mana” feature in our game. If hero is in berserker mode, his attributes increase, time slows down, he is able to cast spells even if he’s out of mana and his mana pool doesn’t deplete at all.
The simplest way to implement it is to test ‘am I in berserker mode’ condition in every place when we modify mana amount. Minimal example:
if (m_hero->IsInBerserkerMode() || mana > spellCost)
{
CastSpell();
if (!m_hero->IsInBerserkerMode())
mana –= spellCost;
}
It will probably work OK, however it strengthens the coupling between Spell & Hero components. Next week a designer stops by our desk and tells us about his new cool idea – mana shouldn’t be reduced if player is in berserker mode OR if he has Jewel of Mana in his inventory. We need to modify both conditions and bind both components even tighter. This will repeat itself every time we need to modify the system. How to improve it?
One way is to introduce intermediate layer between both components. We can add ‘unlimited mana’ mode to spell component, which is enabled/disabled according to specified conditions (quite rarely, probably). Now, it doesn’t need to ask about it every time. Our code snippet becomes:
if (IsManaUnlimited() || mana > spellCost)
{
CastSpell();
if (!IsManaUnlimited())
mana –= spellCost;
}
Now, every time we introduce a new condition we only need to modify higher level code, but the implementation responsible for casting spells stays without changes. Yes, we introduce additional variables, which is the hidden cost of this approach. It shouldn’t be choice that’s taken blindly, but in most situations shouldn’t be a problem (can be just one more flag).
Little challenge posted by a workmate. Good for those 5 minute breaks at work when you need to force your brain to think about something else than your current task.
Recode the following piece of code so that it doesn’t use branches/multiplies:
unsigned char* oldStart = 0;
if (m_start + size < m_bottom)
{
oldStart = m_start;
m_start += size;
}
return oldStart;
Don’t read below if you want to try it yourself.
Few weeks ago I’ve been watching The Curious Case of Benjamin Button. It is a nice movie, but also painfully long – almost 3 hours. It got me thinking – are movies getting longer & longer? I could have sworn that I have remembered them to be shorter, usually around 1.5h, now it feels like it’s closer to 2h. Obviously, this was only my impression, I had no hard data to back it up. This weekend I decided to play with Ruby a little bit and it gave me a chance to verify my theory.
Another interesting photo technique (named after Michael Orton). To be perfectly honest, I’m not that big fan, it makes images a little bit ‘cheesy’. However, it should be interesting for every graphics programmer, as the idea behind it is almost exactly the same as behind fullscreen glow/bloom effect. Basically, it’s a blend of two images, the original one & overexposed/blurred layer. Sample result:
Been playing a little bit with tilt shift photography today. My first try here (Stockholm). Not the best source image (sky especially, technique tends to look better with whole picture ‘busy’) and perhaps a little bit too strong blur, but still kinda captures the ‘fake miniature’ look:
Today I’d like to describe a relatively unknown feature of Visual Studio that can be extremely useful in certain situations – remote debugging. Remember all those situations when application work perfectly on your machine, but crashes on tester’s? “Old school” way of trying to debug this problem was to add a very detailed logging, then analyze it. Fortunately, there is a more convenient method – we can simply connect to his machine and attach debugger to running application. Setting it up is relatively simple and boils down to the following steps (machine A = tester’s machine with application running, machine B = developer’s machine with Visual Studio):
That should be all that’s needed. Just little note about symbols: when debugging native code, up-to-date symbol files (*.PDB) should be present on machine B, otherwise you’ll have problems with source level debugging. Remote debugging may be not as convenient as local debugging as in many cases it means analyzing optimized code (for bugs that only happen in final builds), but it surely can be a life saver.
(or rather: how & when to assert). Assertion is probably one of my favourite programming tools, however there are still few areas that I’m not entirely sure how to solve in an optimal way. First, let’s start with things that I’m rather convinced about, few simple guidelines for my ideal assert macro (funnily enough, during my career I’ve never seen implementation respecting all those rules at once):
// (assertion macro)
do
{
if (!(expr))
{
if (!AssertionFailureHandler(#expr, __FILE__, __LINE__))
BREAK_TO_DEBUGGER;
}
} while (false)
All your fancy code (logging etc) goes into handler, then return false (true/code/whatever you like) to indicate we should break to debugger.
Now, assuming we have our perfect assertion system, there are still some problems left to solve. Things get more complicated here, personally, I don’t have definite answers for the following issues:
T& operator[](int i) const
{
ASSERT(i < Size());
…
Imagine we use this structure to store vertices, then index it using triangle data loaded from disk. If mesh is corrupted, we get out-of-bounds access & fatal error. This also means that 100 people can’t work now, because level crashes at the start. That’s simplified example, but you get the picture. That’s another subject on its own actually – should game care about invalid data?
Object* obj = ObjectForId(m_idOpponent);
ASSERT(obj);
if (obj) // should this test be here or not?
obj->DoSomething();
Here, I’m certain: there should be no special test. If opponent ID can be invalid, remove the assert. If you’re sure it’s always valid – put the assert, remove the test, let it crash.
That’s actually one of places where I *hate* to see people “fixing” problems like that with cheap workarounds. Too many times I have seen a situation when programmer “fixed” the crash by adding if (ptr) and happily moved to other tasks. NEVER DO THIS, that’s the worst thing you can do. You don’t fix problem this way, you merely sweep the dirt under the carpet and make the real root of the bug even harder to track down (as now, it has a big chance to crash many calls later). If you are absolutely sure ptr can’t be NULL there – don’t try to work around it, it’s a fatal error and should be treated as such. Sure, you may have some higher level architecture preventing application from a hard crash (like SEH), but log it, do NOT exit quietly. Of course, try to find why ptr was NULL in the first place. Is it a bug or a valid code path? Generally, I’d say we should try to minimize number of functions that care about NULL pointers, do tests at the highest level possible. I treat if (ptr) explosion that’s sometimes visible in later stages of the project as sign of laziness and lack of understanding of program architecture. “The best” of this category that I’ve actually seen:
if (ptr == (Foo*)0xcdcdcdcd || ptr == (Foo*)0xbaadf00d || ptr == (Foo*)0xfeeefeee)
return; // YAY, I'm so smart!
My (rough) idea of good assertion/warning/error system:
Your thoughts?
I played a little bit with my Load-In-Place system. It used to only support vectors of PODs (stored by value). Now it handles also vectors of pointers & vectors of classes. Sample structure that’ll be saved/loaded automatically:
struct IntContainer
{
int* pInt;
};
struct SuperBar
{
unsigned long i;
// [Hidden]
float* p;
bool b;
signed char s;
Color color;
SuperBar* psb;
typedef rde::vector<int> tVec;
tVec v;
rde::vector<Color*> someColors;
rde::vector<SuperBar*> superBars;
rde::vector<IntContainer> containers;
IntContainer ic;
};
[...]
// (fill sb)
// Code to save this.
SaveObject(sb, ofstream, typeRegistry);
// Code to load:
SuperBar* psb = LoadObject<SuperBar>(ifstream, typeRegistry);
Cool thing is, fixup cost for loading is roughly linear (it’s constant per-pointer, no recursion), only saving gets more complicated as structure complexity increases (so it’s still good to be careful, of course). I’ve updated download package.