Good programming style is nothing you can measure, really.
More of an art, than a science -- but isn't that true
for most aspects of game programming?
Game programming style is nothing the player might notice in the end. It might not even
be something you can teach in its whole, but I will give it a try here for some aspects.
With good programming style I mean quality in all the areas you
really don't see except when looing at the source code. Programming
style covers syntax, patterns used, the structure you break your program
into. But why even bother if there's no difference in the actual executed
game?
I think there's many reasons to bother:
- Readability results in maintainability.
- Breaking up structure in small parts increases reusability.
- By using different layers of abstraction (one describing what you want, another how you want it to be done exactly), more complex game features are possible.
- Elegant program flow through high encapsulation keeps your head free to concentrate on the actual problems ahead.
- Certain "bugs" result in unexpected, positive rewards.
Some other important issues however are not directly connected to programming style:
- Game speed; even though you may find it easier to hunt down bottle necks in a cleanly organized program.
- Program bugs; even though you will have much less in the first place, and much more to be found at compile time, when you adhere to certain standards of style.
These concepts are not exclusive to games programming, and certainly
not exclusive to Blitz Basic. Some things you find to implement easier in
a certain language, for other things there are no "native" constructs;
but if the language supports some basic features, the lack of certain
constructs won't keep you from implementing your own.
A definite lack in the current Blitz Basic language you won't find a workaround
however is explicitly declaring variables. It will be a great
time saver when debugging if it's ever becoming an option, since you will find
misspelled words at compile time.
BASIC flavors (like Blitz Basic, or QBasic), in my opinion, are ideally suited for a good programming style:
- One might argue those flavors are mostly shielding the hardware details from the programmer; that's the exact concept of encapsulation.
- One might argue those flavors are "wordy", in that they use more words than needed to express a command; but that's one thing which can enhance readability.
Some BASIC flavors don't support OOP, Object Oriented Programming. Natively, that is. Others, like Microsoft Visual Basic 6 (and soon, Visual Basic .Net), include the concept of classes and events.
Readability
Spacing
First, I think practically everybody does it, tabbing or spacing:
Not too good (example without spacing):
for x = 1 to 20
for y = 1 to 20
paintTile tile(x, y), x, y
next
next
For each wrapped scope, like the inside for a for-next loop, there should be one tabbing. If you don't use tabbing, you might use four spaces.
Better (example with spacing):
for x = 1 to 20
for y = 1 to 20
paintTile tile(x, y), x, y
next
next
The structure here is much more obvious.
Explicit names
If you didn't hear of magic numbers, you certainly
experienced them. It's those numbers used everywhere within the program
where there's no clear explanation to them.
And I don't really mean
a comment explaining what the number is. I'm not a big believer in documenting
bad source code; why not write it self-explanatory in the first place?
You might even go so far to create temporary variables just to explain
a certain calculation and break it up in its seperate parts.
Not too good (example with magic numbers):
for x = 1 to 20
for y = 1 to 20
paintTile tile(x, y), x, y
next
next
Better (example without magic numbers):
for x = tileFirst to tileLast
for y = tileFirst to tileLast
paintTile tile(x, y), x, y
next
next
You might also use a way to test the boundaries of the array
you iterate through if you want to make the loop boundaries more
obvious.
Some of the big pro's for avoiding magic numbers except instant
readability:
- Easier to search for a variable, since it's uniquely named (it may appear more than once, but always "mean" the same); a certain number may appear in completely unrelated contexts.
- Changes to the variable value (for example, a global constant) will directly apply to all related parts of the program
Vertical spacing
You can use double returns to more clearly seperate parts of the program. (There are no easy rules I would know for that, like there are in horizontal spacing/ tabbing.)
Avoiding abbreviations
Abbreviations might save time when writing a line of code (depending on the programming enviroment and its auto-completion features, or your capabilities of making use of copy-and-paste shortcuts, it might not). But it might cost a lot of time writing a long program, since readability suffers. Especially for other people reading your program; or yourself, when you dig through older code. What might seem obvious to you might not seem obvious to others; or even your future self.
Not too good (example with abbreviations):
if spX > spLim then
spX = spMax
endif
Better (example with verbose variables):
if speedX > speedLimit then
speedX = speedLimit
endif
You might make rare exceptions from the rule of avoiding abbreviations; there are idioms, variable-names every programmer might know, where the verbose version would decrease readability. (Think the "for i" loop.)
Mixed case
Variable names should be written in mixed case if they consist of
several sub-words. The first letter of each word which comes in the middle
of a variable name should be written in upper-case.
If you follow the rule
to write constants all uppercase (you might have reasons not to, to allow
for easy redefinition of the variable later on), you can use underscores
inbetween words.
Sometimes, mixing case might hint that using types
might be even better. So speedx
might become speedX
, which
might later become speed\x
.
In general, build-in commands of the language will be formatted
by the programming environment you use. Typing endif
might
result in EndIf
.
Not too good (example with all lowercase):
spritesize = defaultspritesize
Better (example with mixed case):
spriteSize = DEFAULT_SPRITE_SIZE
Breaking up structure
Of course, every good program is split up in sub-functions. They will enhance reusability and also make the program easier to understand. The smaller and more precise a function, the higher the chance you can reuse it, even from within slightly different contexts.
There is no easy rule-of-thumb as to how long a function could
be, but if you have to scroll your function to see it completely
on a 1024*768 resolution... you might ask yourself if you can find
ways to break it up into more functions.
Another good sign of an overlong
function is inline commentary within a function explaining exactly
what the subparts are meant to do. It's much more elegant
and useful to use self-descriptive function names:
Not too good (pseudo-code example with overlong function):
Function doManyThings()
;do this
foo
bar
foo
bar
;do that
foo
bar
foo
bar
;do more of this
foo
bar
foo
End Function
Better (pseudo-code example with small, precise functions):
Function doManyThings()
doThis
doThat
doMoreOfThis
End Function
Function doThis()
foo
bar
foo
bar
End Function
Function doThat()
foo
bar
foo
bar
End Function
Function doMoreOfThis()
foo
bar
foo
End Function
Note that the variables used in the three different parts have a shared variable scope in the first, bad example, and a seperated variable scope in the second, better example. Seperating variable scope clearly will prevent errors like wrong initial values of variables, or having to use alternative variable names.
To see another bonus of this approach, imagine you would need to use "do this" again:
- In which example do you think would it be easier to find the "do this" code?
(Don't forget the Blitz Basic Environment lists all function names!) - In which example you think will it be easier to fall into the trap of a copy-and-paste code approach, creating redundancy (therefore possibly multiplying future work, such as debugging and modifying)?