Hack Then Refactor: Going from Comments to Classes*

Sep 30, 2013

Rough Drafts Should Look Rough

One of the common challenges holding up beginning game developers, much like it holds up beginning writers, is trying to get everything down right the first time, perfect immediately, as soon as it’s “on the page.” A rough draft ought to be rough, whether it’s written in code or in words. It’s the best time to be taking some chances, hashing out some incomplete thoughts to see where (if anywhere) they lead, getting it on screen as typed characters to then figure out in the following steps step how it might be better rearranged, chunked, and reorganized.

Schooling introduces everyone to some basic process steps to apply this process to writing. For practical game coding we’re mostly on our own.

Functionality First Then Revise and Edit

One process for doing that in code is something that I mentioned briefly in the refactoring entry.

Broken down here into a step-by-step expansion of the strategy:

  1. Give up pretense for elegance or doing things the proper way and just embrace the hackiness while figuring out what you want. Even if this is the first step of a larger projects, treat this step like game jam or timed programming challenge, just get it working and out of your head. Don’t worry yet about optimization, what you’ll keep, code readability, or organization, just get it working to be able to make a more informed choice about what’s worth keeping. During initial prototyping treat it less like an engineering task and more like brainstorming, sketching, or outlining. My early iterations when getting a game working can tend to look a little like this (though handling all 4 movement directions instead of just 1, and usually for a less trivial interaction):
    //// DISCLAIMER: this is just pseudocode, not valid C++, AS3, Java, etc.
    int playerX, playerY, enemyX, enemyY;
    int enemyAlive = 1;
    int playerHealth = 1;
    Bitmap backgroundFile; // loading jpg elsewhere in code

    function everyFrameTick() { // incomplete example for illustration! if(keyboard.holding(RIGHT)) { playerX++; } if(playerX > screenWidth) { playerX = screenWidth; } if(distance(playerX,playerY,enemyX,enemyY) < collisionRange) { playerHealth--; enemyAlive = 0; } drawImage(backgroundFile); if(enemyAlive != 0) { drawCircle(enemyX,enemyY,color.RED); } if(playerHealth > 0) { drawCircle(playerX,playerY,color.GREEN); } }
  2. That first step can be good for ideation, but typically results in large undifferentiated blocks of code serving mixed purposes. This definitely doesn’t scale well to a larger, distributed, or more elaborate program, but while just getting traction it has its advantages. Having all the code in one place like that can make it very quick and easy to make sweeping changes to what you’re doing and how, ways that data affect one another, and there’s no time spent hopping between files. The code at this point is small enough to keep mostly in mind all at the same time, and using CTRL+F in the text editor to jump to spots works fine but probably isn’t even necessary.

    Don’t beat yourself up over a rough draft looking like a rough draft. That is how it should be. Add short, one-line, functionally descriptive comments breaking up sections of that code. “Noun verb” form works best for this, such as “// application setup” or “particle effects update.” Rearrange line order to better group them if necessary without changing functionality. One section might be “// player movement” or “//// player bounds checking” – using multiple comment pairs as in that second example to mark subsections just as you would for an outline.

    int playerX, playerY, enemyX, enemyY;
    int enemyAlive = 1;
    int playerHealth = 1;
    Bitmap backgroundFile; // loading jpg elsewhere in code

    function everyFrameTick() { // incomplete example for illustration! // wipe screen by redrawing background drawImage(backgroundFile); // enemy movement and draw //// collision checking between player and enemy if(distance(playerX,playerY,enemyX,enemyY) < collisionRange) { playerHealth--; enemyAlive = 0; } //// draw enemy if(enemyAlive != 0) { drawCircle(enemyX,enemyY,color.RED); } // player movement and draw //// player keyboard input if(keyboard.holding(RIGHT)) { playerX++; } //// player bounds checking if(playerX > screenWidth) { playerX = screenWidth; } //// draw player if(playerHealth > 0) { drawCircle(playerX,playerY,color.GREEN); } }
  3. Carve that larger body of code into functions named after the comments you made to split up groups of the code. The code going from “// player movement” up to the next “//” top-level depth comment should be moved to a new function called “playerMovement()” which, in the example above which had a subsection marked as “//// player bounds checking” could be further split to call a function called “playerBoundsChecking()” and so on. If variables are needed among both…

(continued in ebook)

*This entry is now in the Videogame Developer’s Strategy Guide, available through membership in Gamkedo Club.

Learn and practice team game development with Gamkedo Club.
Membership worldwide. Professional support. Proven process.

Subscribe by e-mail to receive weekly updates with Gamkedo.Community interviews and YouTube training videos for game developers!

All contents Copyright ©2018 Chris DeLeon.

Site production by Ryan Burrell.