🎉 Celebrating 25 Years of GameDev.net! 🎉

Not many can claim 25 years on the Internet! Join us in celebrating this milestone. Learn more about our history, and thank you for being a part of our community!

Brain giblets round 3

Published April 30, 2008
Advertisement
Observation: There are two basic ways to handle planned future functionality in your code. For example, suppose you are adding scripting language bindings to a partially finished engine. The engine currently does not have sound effect support, but it will soon. Your choices are fairly simple:

  • Don't add bindings yet; when sound effect support is ready, go back and add the bindings then.

  • Add bindings now, and set up stub functions that define the sound effect interface, even though there is no code behind the stubs.


It may seem like option number 1 is a clear winner, especially when there's a lot of work to be done - adding empty stubs doesn't look "productive".


The Cold Harsh Reality: Option 1 is very unwise, despite arguments such as "YAGNI" and so on.

Suppose you have several hundred scripts being written in this scripting language, and most of them use sound effects. Now, when sound is added, you have to go back and revisit each of those scripts to add the sound effect code. By contrast, if we choose option #2, we can add the sound effect code as we write the scripts.

Note that this doesn't save us the work of testing the scripts to ensure that the right sounds are played and so on; however, this is just a simple, contrived example. If the interface is more complex, like a physics engine for instance, then it rapidly becomes much more effective to write code to a stub interface as quickly as possible.

One objection here is that option 2 might not work out so well; what if we get part way into implementing the sound effect system, and the interface needs to change? Now all those bindings are wrong, and the scripts that rely on them are wrong, too.

This objection, fortunately, doesn't hold water. All it takes is a quick run of a global-find tool, and we can immediately see all the places that need to be updated to fit the new sound effect interface. In short, it is much easier to refactor a good interface into another, better interface than it is to "refactor" no-code into some-code.


Advice and Summary: Define your interfaces between modules as early as possible, and write stubs for missing functionality. This helps keep track of what exactly is left to do, and prevents small tasks from falling between the cracks. As a bonus, this strategy works very nicely with test-driven development.
Previous Entry More giblets
0 likes 5 comments

Comments

Dragon88
I have a half-idea percolating in the back of my head about a third possibility, but by the time I think it through completely I probably won't remember this journal post, so I'll just throw a vague idea out:

Rather than actually having calls to stub functions (which would define a set interface), use some "magic comments" instead that declare consistently what you were trying to do with your sound interface at that point in the file (change panning, play sound, etc). Then when you've got the sound functionality implemented, since you used consistent comments for the same ideas, you may be able to get away with a simple global search/replace ("play sound xyz.wav" becomes audioEngine.PlaySound("xyz.wav")). You might need a special tool to run through the files and do all this, but that's not really that hard to whip up quickly.

This saves you both from having to go back through and rewrite everything when you finally get the functionality, and also from having your stubs define what your interface has to look like before you've put serious thought into the subsystem.
May 01, 2008 04:46 PM
khawk
Quote: Original post by Dragon88
Rather than actually having calls to stub functions (which would define a set interface), use some "magic comments" instead that declare consistently what you were trying to do with your sound interface at that point in the file (change panning, play sound, etc). Then when you've got the sound functionality implemented, since you used consistent comments for the same ideas, you may be able to get away with a simple global search/replace ("play sound xyz.wav" becomes audioEngine.PlaySound("xyz.wav")). You might need a special tool to run through the files and do all this, but that's not really that hard to whip up quickly.


If you have it, Visual Studio 2005 (maybe 2003 too, can't remember, and I would imagine 2008) will allow you to do this using the Task List. In addition to the "// TODO:", "// HACK:", and "// UNDONE:" tags it can automatically find, you can also create your own tags for the exact purpose you describe by going to the Tools -> Options -> Environment -> Task List settings.

Then enable the Task List window through View -> Other Windows -> Task List (or press Ctrl + T), and your tags will show up there automatically.


May 04, 2008 08:28 PM
Dragon88
Quote: Original post by Khawk
Quote: Original post by Dragon88
Rather than actually having calls to stub functions (which would define a set interface), use some "magic comments" instead that declare consistently what you were trying to do with your sound interface at that point in the file (change panning, play sound, etc). Then when you've got the sound functionality implemented, since you used consistent comments for the same ideas, you may be able to get away with a simple global search/replace ("play sound xyz.wav" becomes audioEngine.PlaySound("xyz.wav")). You might need a special tool to run through the files and do all this, but that's not really that hard to whip up quickly.


If you have it, Visual Studio 2005 (maybe 2003 too, can't remember, and I would imagine 2008) will allow you to do this using the Task List. In addition to the "// TODO:", "// HACK:", and "// UNDONE:" tags it can automatically find, you can also create your own tags for the exact purpose you describe by going to the Tools -> Options -> Environment -> Task List settings.

Then enable the Task List window through View -> Other Windows -> Task List (or press Ctrl + T), and your tags will show up there automatically.


Sounds like a somewhat better idea to me than just stubbing everything out.
May 05, 2008 04:53 PM
remigius
If you're the only one on the project, maybe it is. On the other hand, if you have to drag your scripters/designers back to their tried and tested scripts to soup in functionality later, you're wasting manhours and sowing resentment [wink] Obviously Apoch's idea does need some extremely good planning, but I think it makes for a much better workflow, especially if you consider team efforts.
May 06, 2008 11:53 AM
Dragon88
Quote: Original post by remigius
If you're the only one on the project, maybe it is. On the other hand, if you have to drag your scripters/designers back to their tried and tested scripts to soup in functionality later, you're wasting manhours and sowing resentment [wink] Obviously Apoch's idea does need some extremely good planning, but I think it makes for a much better workflow, especially if you consider team efforts.


I've never tried it, so I can't say with absolute certainty, but I believe 90% of what you would end up doing would be pretty much just a fancy search/replace. In the example we've been using, most of your calls are going to fit within a very small spectrum of your audio system's abilities. You'll want to place and play sounds, but that's about it. Most of the parameters associated with playing sound are likely to be bound to the sound effects themselves (or will just be systemwide), and it will be the exception rather than the rule where you will want to override them. So you'd only have to drag your sound designers and your scripters back into things for the exceptions, which will be the minority of the problem.

By the way, the same problem also exists for Apoch's suggestion. You can put the calls in place, but anything you can say in code you can say in a comment, even if you stub out the functions. The difference is the amount of flexibility you have with comments as opposed to stubbing: You can say in general terms "Set gain to 50%, place at the monster's position, play the sound, fade out with 0.5sec left in the sound", and not worry about exactly what function calls that is composed of.
May 06, 2008 06:01 PM
You must log in to join the conversation.
Don't have a GameDev.net account? Sign up!
Profile
Author
Advertisement
Advertisement