About this blog
Alternative thoughts on game development
Entries in this blog
So just what is this different way of building games?
in a nutshell:
non-OO c++ syntax
ADTs (abstract data types) composed of:
* PODs (plain old data)
* standalone functions
the use of data structures statically allocated in the data segment vs dynamically allocated on the heap
procedural code calling hierarchy used to define flow control
this avoids some of the headaches related to OO game coding.
and many of the advantages of OO game coding do not apply to a small team or single developer in control of their own project, which makes this a viable alternative - but only in such cases.
if you need true inheritance and polymorphism in your code, you're going to have to do the OO thing (at least a little bit).
however i've found this method perfectly satisfactory for writing games both large and small. and I have yet to require OO syntax in my code for any game i've made, am working on now, or plan to make in the future. surely those who didn't learn programming until after OO syntax was added to c++ will find this shocking. but yes, its true. games were being written for computers long before OO syntax existed. OO was invented as a means to handle the difficulties of software development in general, with no special regard to games or their special needs. so it was a dual edged blade, OO power, but with OO issues and OO overhead. Non OO syntax if done correctly (basically in an OO style) could get the job done just fine without the issues or overhead. it was only in the "OO syntax only" capabilities that it showed advantage. and those capabilities are largely related to modification of large code bases by multiple coders over long periods of time. Almost the opposite of the single developer on a single version of a single game.
so how does one do this?
well, instead of an object, you'd have a data structure (most likely a struct) for its member variables, and a number of standalone functions that accessed the data structure (IE implement the methods as stand alone functions). you take all that and put it together along with your struct definition and any related #defines, and any other variable declarations you need, and you have the non-OO equivalent of an object - an ADT (abstract data type). then if you want to, you can put it in its own source file, with it own header, thereby turning it into a code module. other code will only be able to use the exposed API in the header file. variables declared in the module will essentially be private to the module, unless exposed through the header API. so you have nice clean APIs and data hiding, with no OO syntax, no object hierarchy headaches, no memory leaks (as the variables in the module are static, allocated at load time in the data segment, not at runtime on the heap), etc.
however, you will need to write explicit init and shutdown routines to replace any custom constructors and destructors. the nice thing about explicit init and shutdown routines is you have complete control over initialization and shutdown order of all your modules in the game.
note that there's nothing wrong with using the heap when called for. if you need a big temporary buffer, the heap is obviously the way to go. but the key word there is temporary. there's no real reason to keep permanent data structures on the heap. its adds allocation and de-allocation overhead, and introduces the possibility of memory leaks. sure, one malloc or new at game start isn't a biggie, but constantly newing and disposing every little data structure in the game is just inefficient. another place where the heap is useful/required is when you simply don't know how big the data structure must be, or want it to use all available ram. but there are very few places in a game where the maximum possible number is not known or at least estimate-able, and static data structures can be declared appropriately large to handle the worst case anticipated scenario, then either degrade gracefully, or perhaps fall back to using supplemental memory from the heap to enlarge the structure, if you want to get fancy. the trade off is some unused memory at the end of the slightly oversize static data structure, vs the overhead, possible extra code work, and possibility of introducing memory leaks using dynamic allocation and reallocation.
what it boils down to is that the heap has allocation and deallocation overhead that the data segment doesn't. and its possible to make coding errors using the heap which are not possible using the data segment. so the heap can be used, but not like a madman due to overhead, and you have to make sure you dot your i's and cross your t's.
the use of procedural code implicitly defines flow control in the program, so "game states" for flow control purposes is unnecessary. game states are then only required for the more fitting purpose of making the game run in multiple modes, such as fps mode vs rts mode in a fps/rts hybrid.
use of globals
level based vs non level based game code - the differences between shooter and simulator code
gameplay vs realism, the differences between shooter and simulator game design
Quick thoughts on writing bug free code
From a recent posting of mine:
[background=rgb(247,247,247)]And you should write bug free code as well.[/background]
[color=rgb(40,40,40)][font=helvetica][background=rgb(250,251,252)]one thing at a time, do it very well, then move on.[/background] [/font][/color]
[color=rgb(40,40,40)][font=helvetica][background=rgb(250,251,252)]programming is about precision.[/background] [/font][/color]
[color=rgb(40,40,40)][font=helvetica][background=rgb(250,251,252)]mind your p's and q's - cross your i's and dot your t's .[/background]
[color=rgb(40,40,40)][font=helvetica][background=rgb(250,251,252)]always think ahead about what you're doing and what potential pitfalls could be: ok, this call here does memory allocation i need to deal with. this other snippet is "critical section" stuff where i have invalid addresses and such and the normal rules don't apply (constructor issues), etc.[/background] [/font][/color]
[color=rgb(40,40,40)][font=helvetica][background=rgb(250,251,252)]nobody's perfect, but the only bugs in your program are ones you put in. [/background] [/font][/color]
[color=rgb(40,40,40)][font=helvetica][background=rgb(250,251,252)]so divide and conquer. modular-ize until the parts are so simple you can't F-up .[/background]
[color=rgb(40,40,40)][font=helvetica][background=rgb(250,251,252)]turn complexity into a hierarchy of layered simplicity.[/background] [/font][/color]
on hard coded vs soft coded constants
a recent posting of mine:
[background=rgb(250,251,252)]rpg/person sim, fps interface:[/background] [/font][/color]
[color=rgb(40,40,40)][font=helvetica][background=rgb(250,251,252)]~100 monster types, ~250 object types, ~60 weapon types, ~45 skill types, ~200 meshes, ~300 textures, ~ 10 materials, ~100 models, ~30 animations, 2500x2500 mile game world.[/background] [/font][/color]
[color=rgb(40,40,40)][font=helvetica][background=rgb(250,251,252)]models and animations are made using an in-game editor, and saved to disk. the list of models and animations to load is hard coded. [/background] [/font][/color]
[color=rgb(40,40,40)][font=helvetica][background=rgb(250,251,252)]meshes (.x) and textures (.bmp or .dds) are loaded from file. the list of files to load is hard coded.[/background] [/font][/color]
[color=rgb(40,40,40)][font=helvetica][background=rgb(250,251,252)]the game world is procedurally generated.[/background] [/font][/color]
[color=rgb(40,40,40)][font=helvetica][background=rgb(250,251,252)]everything else is hard coded: monster types, object types, weapon types, materials, skill types, etc.[/background] [/font][/color]
[color=rgb(40,40,40)][font=helvetica][background=rgb(250,251,252)]the reasoning is as follows:[/background] [/font][/color]
[color=rgb(40,40,40)][font=helvetica][background=rgb(250,251,252)]1. for everything hard coded, the final values will be known constants for the final release version. so soft coding is only an aid in dialing in these "tune-able constants", such as the list of all meshes to load (the game loads everything once at program start). none of this stuff changes very much, only while dialing in the game. and it doesn't change at all once dialed in. and it never changes in versions released to the public - only in updates. granted, a zipped 2K text file list of meshes to load is smaller than a 1.2 meg zipped exe, but unless your releases are minor updates, odds are the code will change too. as always it depends on what you're doing.[/background] [/font][/color]
[color=rgb(40,40,40)][font=helvetica][background=rgb(250,251,252)]2. since soft coding is not required for release, and since i have full source access, and rebuilt times are not bad, its overkill.[/background] [/font][/color]
[color=rgb(40,40,40)][font=helvetica][background=rgb(250,251,252)]3. in the end, you have to "hard code" or type in this data somewhere using some syntax. if there's no difference except running faster and less code complexity, why not just do it in the native programming language? no need to learn a scripting language, no need to write hooks and calling wrappers for game engine functions so your scripts can invoke calls to the game engine, no need to find and integrate a scripting solution into the project. no slow 4gl / 5gl / scritping BS, no need to use multiple languages to code a game, and so on.[/background] [/font][/color]
[color=rgb(40,40,40)][font=helvetica][background=rgb(250,251,252)]so i soft code the models and animations. they are the type of data (scales, rotations, offsets, mesh and texture ID #'s) that really scream for an editor. i mean could you imagine having to use a markup language to do this:[/background] [/font][/color]
[color=rgb(40,40,40)][font=helvetica][background=rgb(250,251,252)]and having to picture in your head what the model looks like?[/background] [/font][/color]
[color=rgb(40,40,40)][font=helvetica][background=rgb(250,251,252)]but everything else is more or less a set it one time and forget it kind of thing, unless you need to tweak stats. IE just add the info for a new whatever to the "list" or "database" when you add a new mesh, or monster type, or object type, or whatever.[/background] [/font][/color]
[color=rgb(40,40,40)][font=helvetica][background=rgb(250,251,252)]so its really easiest to just use code to say essentially: monstertype[new_monster].hp=100. and be done with it.[/background] [/font][/color]
[color=rgb(40,40,40)][font=helvetica][background=rgb(250,251,252)]sure you may have to go back later and tweak that "100", but only if you didn't figure out the correct value the first time. you should have the "rules" of the game figured out before you code. [/background] [/font][/color]
Well, i've done it.
I've gone CScript.
After having a C++ macro processing language at my disposal for about 15 years now, i've finanlly made the big move and fully integrated CScipt into my code development workflow. now if there was just a way to add a solution specific external tool button to the standard menu bar in VC 2012 express.....
See this journal entry from Project Z for more info on CScript....
So far, i couldn't be happier. With translation times on the order of 100,000 lines per second, the CScript macro processor stage of the new build pipeline is just a blink of an eye!.
and now i can write new code as well as modifiy existing code using CScript syntax, which is quick and easy.
Here's an example of some of the new new CScript code i'm writing now. This is an implementation of a GUI button system i added to the Z library. Granted, its only a statically allocated array with a hard coded size. but it can easily be converted to a class that gets passed a size on creation. an app will generally know how many buttons they need in a given list of buttons. in cases where they don't, a vector would do nicely in place of an array. I created it with updating the UI of the rigid body modeler/animator in mind. the modeler/animator is a linkable module with just two screens of maybe 20 hotspots each. so a single list of 100 buttons was fine for a start. in the long run, this api would evolve to have the ability to load land save lists of buttons, and store an array of button lists (or perhaps a tree for a menu hierarchy). one might even implement a callback driven event handler for it.
this CScript code is a full implementation of a buttonlist data structure with init, new, release, get, set, draw, and pick functions:
' ####################### buttons list ##############################
#d Zmaxbtns 100
i x y w h texID active
fn v Zinit_btns
c ZeroMemory &Zbtn sizeof(Zbtnrec)*Zmaxbtns
fn i Znewbtn
4 a Zmaxbtns
== Zbtn[a].active 0
fn v Zreleasebtn i a
= Zbtn[a].active 0
fn v Zsetbtn i index i x i y i w i h i texID c *text
= Zbtn[index].x x
= Zbtn[index].y y
= Zbtn[index].w w
= Zbtn[index].h h
= Zbtn[index].texID texID
ss Zbtn[index].text text
fn v Zgetbtn i index i *x i *y i *w i *h i *texID c *text
= *x Zbtn[index].x
= *y Zbtn[index].y
= *w Zbtn[index].w
= *h Zbtn[index].h
= *texID Zbtn[index].texID
ss text Zbtn[index].text
fn v Zdrawbtn i index
!= Zbtn[index].texID -1
c Zdrawsprite Zbtn[index].texID Zbtn[index].x Zbtn[index].y (float)Zbtn[index].w/256.0f (float)Zbtn[index].h/256.0f
!= strcmp(Zbtn[index].text,"") 0
c Ztext Zbtn[index].x+10 Zbtn[index].y+10 Zbtn[index].text
fn i Zisinbtn i x i y i index
cr a isin x y Zbtn[index].x Zbtn[index].y Zbtn[index].x+Zbtn[index].w Zbtn[index].y+Zbtn[index].h
As i work away here, i keep thinking of topics for this journal.
I'll start listing them here so i don't forget
i'll also try to say a few words, then cover it in more depth in a separate journal entry.
game state managers
state transitions are already implicitly defined by the natural flow control of a game. the one place i use a state manager "pattern" is in my rigid body modeler/animator. it has 2 states -- model editor, and animation editor. drawscreen calls one of two routines to draw either the model editor screen or the animation editor screen - depending on the "game state" of the modeler/animator module. it then calls one of two routines to process input as either modeler or animation editor input. this is a true "state system" design. and only natural, since you can jump back and forth between model and animation editor with a single mouse click. you see its really two programs in one, a modeler, and an animation editor. the state determines which one is active - sort of non-preemptive multi tasking - ie task switching. as you can see, it doesn't seem to make much sense to slice up a game into separate "programs" (states) each with its own input and draw routines. well actually it does.! . but a state manager isn't needed unless the relationship between "mini programs" (states) is a flip flop back and forth kind of thing. or jump around between a few. but game states are usually a combo of linear, hierarchy, and loops, not peerless network type things. so the state can usually be handled automatically by call hierarchy and normal flow control methods. Whew! more than a few words! Anyway, more on how you can usually get away without a state manger in a separate post.
writing bug free code.
there are lots of tricks that can help write bug free code. at some point, i'll do an entry on all the ones i know. in a typical large title i do, i've been blessed to average just one non-show stopping code type bug in the release versions. typos in displayed text are another thing however... .
game state managers
there was a recent thread on using game states for menus and such. can't find the post. in it, i said that the natural call hierarchy of games made state driven games overkill. in a later post, i mentioned an application of state driven that i do use: the modeler/animator module. its two programs in one, a modeler, and an animation editor, and you can switch between the two at any time. just now, i realized that caveman is also state driven! its two games in one: rpg, and person sim. so it has two states it runs in: fps/rpg mode, and "The sims" doing an a action mode. each has its own render and input methods. but most games aren't like this, and therefore are not true hybrid multi-state applications. it appears the timeto use state management is when your app is actually two or more apps of equal importance, IE they have a peer 2 peer type relationship, vs a main app and sub/mini app relationship. menus and such are sub-apps of the app that calls them. the main menu is a sub app of main. the game loop is a sub app or the main menu. the in-game menu is a sub-app of the game loop.
sub menus, stats screens, maps, etc.
- not -
main menu game loop in-game menu world map etc etc (for every screen/menu in the game!)
natural call hierarchy handles it all for you. no need to manage states. granted, it can be done that way, but encoding the natural call hierarchy as state transitions can get ugly.
here's examples of state systems that seem to make more sense to me:
fps/rpg mode person sim mode
render everything, process input, and update everything. that's what games do. this leads to a basic loop like:
while ! quitgame
[font=arial]the order of the calls:[/font]
there are only two possible orders for a game loop:
1. input - update - render
2. input - render - update
all other orderings are simply out of phase versions of these two, that start the loop in a different place in the order. IE input-update-render is functionally equivalent to update-render-input once the loop is up and running. its only in the first iteration that there is any difference.
so there are two possible orders, and for each order, three possible places to start the loop, for a total of 6 possible layouts.
so which one is best? well, the difference in where you start the loop is negligible. it only affects the very first frame of the entire game, so who cares?
And it turns out that input, render, and update are all pretty order independent of each other. You just have to call each one every time through the loop (more on that later).
so, it doesn't seem to really matter. and of the six possible orders are fine.
i personally was taught: draw the screen, process input, move everything, which is: render - input - update
so, it looks like you can use any of the six possible orders:
input before update:
render - input - update
input - update - render
update - render - input
input after update:
render - update - input
update - input - render
input - render - update
[font=arial]now, what about de-coupling, IE variable timesteps and all that jazz?[/font]
Well it just so happens that my life is full of poorly coupled game loops. not my games - games i play.
You see, i have a passion for submarine simulators. And unfortunately, sub sims tend to have poorly coupled main loops that can interfere with gameplay at the worst possible moment.
ok, lets say we have a "game" that moves a square from the left side of the screen to the right side of the screen over 1 second.
this will be our test case for performing[font=arial] [color=rgb(68,68,68)] Gedankenexperimenten [/color][/font][color=rgb(68,68,68)][font=arial](thought experiments) about de-coupling game loops.[/font][/color]
we'll start with a baseline case:
vsync on. 60Hz refresh rate. update moves the square by (screen_width / 60) pixels each update. fixed timestep, no de-coupling.
[font=arial]First - why decouple?[/font]
well, if your frame rate is jittery, de-coupling render and update will smooth things out. so you update based on frame ET.
accelerated time is another reason to decouple - in fact it pretty much requires de-coupling.
making the game run at the same speed on different PCs is the third reason, but that's not the only way to get the same speed on all PCs, and perhaps is not the best way either.
the problem with totally de-coupling render from update occurs when frame times go way high - unplayably high - like > 66 ms per frame (slower than 15 fps). Over the years, i've determined that 15 fps is the slowest a game can go and still be sufficiently responsive to play.
when frame times go high, the amount of update per frame increases drastically. to the user, this gives the perception of the game speeding up in relation to the graphics, just when the graphics are slowing down. worst of both worlds.
to fix this, you have to have an upper limit on how much update you'll do per frame.
lets apply this to the test case.
we modify out test program so update takes a frame ET as a parameter.
in the baseline case, we're at 60 fps, so update gets passed 16ms as its update ET.
in the worst case, we want to lower limit fps to say 15fps (or 20 or 30 or whatever). for 15fps, we're looking at a fame ET of 66ms max.
so we do something like:
if (ET > 66) update(66) else update(ET)
this forces a screen update after 66ms of simulation time, to provide the user with sufficient feedback to continue playing.
in general it appears that the following statements must be true for de-coupling to not have a negative impact on gameplay:
1. render speed >= update speed
2. render speed >= input speed
this gets a little tricky with variable timestep. with variable timestep, render speed must be >= update speed for the minimum playable frametime. so if you decide that 33ms is the slowest playable frametime, then render must run at least once every 33 ms of game time.
[font=arial]now, what about playing with vsync?[/font]
well, the video controller only copies data from vidram to the monitor at the refresh rate. So updating vidram more often gets you nothing. if you update vidram at twice the refresh rate, your first update gets overwritten by your second update, then your second update gets copied to the monitor. so turning off vsync and running faster than refresh rate doesn't do much.
[font=arial]now, what about input?[/font]
well, you have two issues:
1. missed input
2. unprocessed input.
if you use polling for input, its possible to miss an input event that starts and ends between pollings. The answer to this is to use an input event queue. polling is done at a very high frequency so events are not missed. events are added to the event queue. input() processes the event queue. for windows pc development, it just so happens that windows already implements polling and an even queue for you - the windows message queue.
when using an event queue, not all games process the entire queue each frame. This is fundamentally wrong. Everything in the queue is user commands that have been issued up to that point in time, so they should all be executed in turn, immediately. leaving commands in the queue is like temporarily ignoring the player's input!
[font=arial]so, looks like our simple loop actually has a lot of considerations:[/font]
1. run at the same speed on all pcs
2. smooth animation with jerky framerate
3. render at least once every 66ms (or 33ms, etc) of game game.
4. not miss any input
5. don't leave input unprocessed.
[font=arial]for running at the same speed, there are two basic approaches:[/font]
1. variable timestep
2. framerate limiter
update takes ET as a parameter, and updates the simulation for ET worth of time. unless you set an upper limit for ET of no more than about 66ms, this loop design comes unglued when ET goes high. this loop design, with no ET limit is what most sub sims use. thus, they have a tendancy to come unglued and become unplayable at the worst possible moment. You get a radar contact of a convoy ahead. you charge straight at them at flank speed on the surface in broad daylight. You close to visual range, the escort destroyers spot you and come sharing in for the attack. You order crash dive, and take her down to about 150 feet, and sail right under those destroyers! then you bring it back up to periscope depth. When you up scope, you're in the middle of the convoy, surrounded by big fat freighters and tankers! Tonnage as far as the eye can see! And the destroyers are still back where you submerged, clueless as to your whereabouts. So you start capping off torpedoes. Before you know it, you've got thousands of explosion particles going on. frametimes therefore become unstable. no problem - we have variable timestep! (yeah - right!). So now the simulation is now running faster with respect to update due to high frame ET. that means that all of a sudden those destroyers are moving much more each frame - and they're moving towards you! Then things get really busy and frame ET goes beyond playable. All of a sudden, you're dead! What happened? The destroyers made a depth charge run on you between screen updates! Obviously, killing the player between infrequent screen updates with no chance to react is undesirable at best.
so it looks like variable timestep with no upper limit on ET is a bad thing. if variable ET is used to make the game run at the same speed on different PCs, an upper limit on frame ET seems to be required for playability when the frametime goes high. But its not always bad. In console games, its not uncommon for a publisher to dictate a minimum framerate below which the game can never drop. To meet this challenge the developers must limit on-screen content. As a result ET is pretty much guaranteed to never go way high. So in these cases the upper limit on ET is unnecessary, and therefore usually omitted. but often times its also omitted in PC games where the play is not as staged, is more random, and the developers don't / can't easily control the amount of on-screen content, and the publisher doesn't dictate a minimum framerate. in these cases, the omission is a major design flaw that leads to the game loop coming unglued. with the simulation racing so far ahead of the graphics as to make the game un-playable.
this sort of takes the opposite approach from variable timestep. variable timestep speeds up the simulation in relation to render on slower PCs.
by contrast, a framerate limiter works by slowing down the entire simulation on faster PCs. depending on the target framerate, vsync can make a handy built-in framerate limiter. you just turn vsync on, and use a fixed timestep. the game is guaranteed to run at 60 fps (assuming a 60Hz refresh rate) , and to degrade gracefully under load. for slower target framrates such as 30fps, you do something like:
this gets you everything except smooth animation with a jerky framerate.
but the framerate only gets jerky when the frame time becomes unstable and starts going high. so its only the point between when things start to slow down, and when they become unplayably slow that a variable timestep for smooth animation comes into play.
if your game tends to run at a playable by unsteady framerate, then variable timestep with an upper limit on ET should smooth out the animations some by eliminating temporal aliasing. The upper limit on ET guarantees you render often enough to provide sufficient feedback for the user. of course, even with a limit on ET, render can still slow things down so much as to make the game unplayable.
if your game can run steady at a given framerate, variable timestep is unnecessary. your ET each frame is the same, so your animation each frame is the same, and therefore automatically smooth.
i use this fact to my advantage. i purposely choose a steady framerate i can run, such as 60, 30, 25, 24, 20,or 15 fps (movies run at 24 fps).
then i use a framerate limiter and fixed time step.
this gets me everything with the least amount of hassle, and i'm not trying to make the game run faster than the PC is really capable of doing on a long term steady state basis.
1. runs at the same speed on all pcs -> framerate limiter keeps it from running too fast. slower PCs degrade gracefully.
2. smooth animation with jerky framerate -> no jerky framerate, so no jerky animation.
3. render at least once every 66ms (or 33ms, etc) of game time. -> automatic with fixed timestep of 66ms, 33ms, 16ms, etc.
4. not miss any input -> i typically poll at 15Hz with no problems with missed input. missed input doesn't seem to be a big issue.
5. don't leave input unprocessed.-> since polling works fine, an event queue is unnecessary. no queue = no unprocessed input in the queue.
so overall, it looks like the best approach is to use a framerate limiter, such as vsync, or a timer based limiter if you need a lower limit.
then - if your framerate is unsteady but playable, you can either lower your max fps to a steady framerate (such as from 60fps down to 50, 40, or 30 fps), or add variable timestep with upper limit on ET. variable timestep will let you run faster (and hence smoother) when possible. the upper limit on ET defines the framerate at which render and update couple and de-couple. if you set ET limit to 33ms, render and update are coupled at 30fps and below, and de-coupled above 30fps. When using semi-fixed timestep, and no minimum framerate is mandated, coupling render and update at low frame rates is ABSOLUTELY VITAL to playability when the game slows down. If you use semi-fixed timestep and no minimum framerate, YOUR GAME LOOP WILL COME UNGLUED unless you limit overall ET to ~66ms or less!
[font=arial]all this culminates in one of two basic loop designs (fixed timestep for games with steady framerates, and variable timestep for games with unsteady framerates):[/font]
steady framerate, fixed timestep, 60 fps vsync:
or with a timer controlled framerate limiter:
unsteady framerate, variable timestep, 60fps vsync. 33ms ET limit (couples and de-couples at 30 fps):
note that i added input into the frame time. input overhead will probably be steady and low, but better safe than sorry! after all it is part of the time that the game spends not updating. to paraphrase a famous quote:
"everything except update generates ET, and update consumes ET."
[font=arial]now, what about de-coupling input?[/font]
well input has the same sort of de-coupling issues that update has. in both cases, you're relying on render to tell the user whats happening. this means you have to render at least every 66ms or so (15 fps) to be playable.
i make a lot of simulator and war games. in both types of games, accelerated time is a common game feature. accelerating time requires decoupling the main loop, or reducing render time. reducing render time usually isn't an option. that leaves de-coupling. so, you can go about it two ways: render less often -or- update more often or by a greater amount per update.
well, if you render less often, say every 10th frame, things should run faster, but now you have to wait 10 times a long to get feedback from input (such as scrolling the screen in a RTS title). and when you do get feedback, its for 10 turns worth of input. this breaks the rule of render speed must be >= input speed. input is running at 10x the speed of render, so the user doesn't get feedback on input each frame. a bad thing.
the result is you go to scroll, and it scrolls up to 10 times before you see a change. this makes it unresponsive, and easy to scroll too far.
so having input run faster than render is bad. this means you cant just draw every 10th frame. instead you must update 10 times. this is ok, 'cause you're in accelerated time. lots of stuff is SUPPOSED to happen between screen updates.
[font=arial]so putting it all together:[/font]
steady framerate, accelerated time:
note that time_multiplier works like ET and affects turn rate, movement rate, etc, in update.
if (ET > max ET) update(max ET) else update (ET)
unsteady framerate, w/ accelerated time:
if (ET > max ET) update(max ET* time_multiplier) else update (ET* time_multiplier)
not sure about that last line there, you may be able to get away with something simpler like... well, uh.... maybe you can't !
so there you have it. the basics of game loops.
[font=arial]i'm both pleased and displeased with the outcome of this investigation.[/font]
i'm pleased that variable timestep without accelerated time was one of the possible outcomes. many games use this method. and if it didn't turn up in the final analysis, then it would tend to indicate a flaw in the analysis.
i'm also pleased that an upper limit on ET for variable timestep was part of the results. This seems to be the bit missing from most variable timestep games that causes them to come unglued and blow up when ET goes way high.
even the infamous article "fix your timestep" mentions the fact that ET must never be an unreasonably large value, but for a different reason. There, they are using ET in a physics model which only works for a limited range of input values. They work around large ETs by defining a physics timestep, then consuming ET in physics timestep sized chunks. any remainder is stored until the next update, and is used for tweening graphics. but they do not set an upper limit on overall ET, so while their physics model may still work with high ET values, their game loop itself still comes unglued when ET goes high. this is probably due to the fact that its demonstration code meant to explain how to handle variable timestep in the physics engine only. their game loop doesn't even have input! at any rate, they're not PRIMARILY addressing the issue of degrading gracefully under high ETs. However, they do touch on this point with the topic the "spiral of death" - IE when you don't limit ET, and it takes longer than ET to simulate ET worth of time. This case may be only theoretical. if a game is supposed to run at 60 fps, and has lets say, a 8ms ET for render and input. that leaves 8ms to do 8ms worth of update. i'm not so sure how often (if at all) a REAL game will ever have an update that can't keep up with itself. sure, you may have a 15ms ET, leaving 1ms to do 15ms worth of update, but different ET values just change the multiplication factor, they don't change the algo. so update runs at the same speed irregardless of ET. That means you'd only get the spiral of death if the execution time of update was inherently longer than that of render and update combined. not bloody likely in a real game.
the one thing that displeases me about the outcome of this little analysis is that the conclusion that "all input should be processed in turn and immediately", at first glance, seems to go contrary to "ET based input" as proposed by L Spiro. I'm going to have to think about that one some more. I've noticed that L Spiro tends to know what she's talking about, so i may have missed something there, or perhaps the two concepts are not incompatible upon closer examination.
[font=arial]so , whats the different way to do it?[/font]
keep it simple:
fixed timestep. framerate limiter. keep your scene complexity within your max framerate. if things do slow down a bit, you'll degrade gracefully and won't ruin the gameplay experience for your user.
an interesting article i read on game loops pointed out the fact that in a game loop, if the framerate is too low, render is the only part that can give. IE reducing render time by reducing LOD, detail, effects, and clip range is the ONLY way to speed things up (assuming all optimizations have already been done). So if your framerate drops, reducing graphics detail is the only real solution.
recently i've had good luck with automatically adjusting scene complexity based on ET (using it to adjust clip ranges on the fly). This lets me run a pretty rock solid fps under all but the absolute worst conditions.
[font=arial]Important new concepts to come away with:[/font]
1. when a minimum framerate is not dictated, and variable/semi-variable timestep is used, an upper limit MUST be placed on overall ET to force screen updates frequently enough to maintain playability. This upper limit is probably no more that 66ms (15 fps) - your tastes may vary - adjust according to your tastes. With today's faster graphics, i'm moving towards a standard of 60 fps steady state and 30 fps minimum. compare that to my original standard from the late 80's of 15 fps steady state and 10 fps minimum for a complex simulation.
2. polling at speeds as low as 15fps is sufficient to avoid missed input.
3. variable/semi-fixed timestep is only required when your game runs at an unsteady but playable framerate.
4. All input available at the time of processing input should be processed.
back to sub sims as an example. Silent Hunter 4 uses an input queue. It also does not process all input available every frame. it appears to consume input at the somewhat unbelievable rate of one command per second. IE the game runs at lets say 30 fps, and the input queue is processed at 1 fps. And commands that counteract each other are not filtered out. so a left followed immediately by a right doesn't result in a right turn, it results in one second of turning left, followed by a right turn. I was taking on a convoy (same battle described earlier). when you empty your forward tubes, you turn the scope around, order all back emergency, and start driving the boat backwards at targets, and use your stern tubes. steering is the tricky part. while still moving forwards, left is still left and right is still right, but once the boat stops and starts to move backwards, the steering reverses. So you're looking at the compass heading and the TBD indicator (torpedo angle on target), trying to figure out if you should turn left or right. And once you figure out which way to turn, is port left, or is starboard now left? As a result you can easily issue commands to turn in the wrong direction. It takes the boat a few seconds to respond, meanwhile your angle on target is getting worse and worse because your not tuning the right way! Straight isn't good enough, you must turn WITH the target to get a firing solution. So in the heat of battle, you start issuing turn orders: Left! No! Right! No! Left! OK, Straight!, No! LEFT! LEFT! LEFT! LEFT damn it! , and they start queuing up. and then they start executing at ONE FPS! by the time the game once again responds to commands in realtime, you've lost your target and must start a new approach. by processing the entire queue this is avoided, as only the last valid commands issued take effect. older commands are automatically overridden by new commands . to do this one would have to use the queue to set some variable to left, right, or straight (for example) with each input, then only process the final result. last command was straight, we go straight! by polling at a reasonable speed (>= 15 fps) and processing input as it occurs, this problem is avoided entirely. no queue is needed. no queue is used. so there's no queue to leave unprocessed events in!
5. 2 and 4 above imply that an input queue is unnecessary in a game!
i think that next time i'll be taking a step back and discussing compiler settings. after all, you need to setup your compiler correctly before you can code. and that's when things start to get VERY different!
fix your timestep:
if you're game runs at an unsteady but playable framerate, you can use the semi-fixed timestep technique as described in this article to eliminate temporal aliasing and smooth out your animations. but you'll need to either mandate a minimum framerate and reduce scene complexity to match, or place a limit on oveall ET of no more than ~66ms (above and beyond the physics stepsize limit of 1/60 in the article), or your game loop will come unglued when ET goes over ~66ms. Note that the examples in the article do not limit overall ET - only the physics step size. So they WILL come unglued when ET goes high!
ET based input:
L Spiro has proposed a method of ET based input similar to ET based update. Unfortunately i am unable to find the exact link. However i did find the link to the L Spiro engine, if anyone knows the link to L Spiro's ET based input, please add it to the comments!
Code entry point
ok here we go....
that's a game, right there. this is what all games do. actually is what pretty much all programs do.
is where program execution begins, main() for example in C++.
is where the program ends execution.
this is where you display your title screen, and show any opening animation, and initialize all program level variables.and data structures. create a window, start your graphics and sound libraries, load assets (graphics and audio data files), etc.
As i provide code examples, i intend to also discuss topics related to the code.
init_program brings up the first topic:
loading of assets.
making the user wait while loading assets is bad. its usually easiest to load all assets at program start if possible. or at level start if necessary. or page assets if needed. or background stream. the slick implementation does a foreground load of enough content to
get things started while it streams the rest in the background for the remainder of the game. A single wait for asset load at program start is usually considered preferable to one per level, etc.
In a typical title, in init_program, i'll create a window, start up directx, and load meshes, textures, materials, models, animations, and audio.
release assets, shutdown graphics and audio.
not much to it. just RTFM for your libraries, and do what it says.
this is the main menu, or wrapper menu system, or "shell" for the game. Not all games have a start-up menu, but most do. For a game with no start-up menu, this would immediately run the game. A typical start-up menu will have options for starting a new game, loading a saved game, setting game options, help, and quit.
both menu options are almost the same. note that in load game, initgame is called, despite the fact that a game is being loaded. there's a reason for this. its possible to design save game file formats that automatically convert older formats to new formats. for this to work, variables not contained in the older format must be initialized. the easy way to do this is to use initgame to initialize everything, then use loadgame to overwrite just the data contained in the save file. this lets you load an old format game, and initializes the new variables with default values. when you save, its saved in the new format, which includes the new variables.
here you initialize any game level variables and data structures. exactly what depends on whether the game is mission based or not.
for a non mission based game, this is the main loop. for a mission based game, this runs the "between missions menu", which in turn calls initmission - run mission - end mission, where runmission is the main game loop.
anything that happens at the end of a single game goes here, such as a high scores display.
rungame for a mission based game:
this will have the "between missions menu". selecting "next mission" starts the next mission.
load the level, init all targets, etc.
the main game loop
up next: main game loops
Explaining the different way
I've been thinking about how to best explain this different way.
An example tutorial series where you could download and compile code would require a C++ compiler and directx.
it would also require low level helper libraries. and an example application. and a place to host the files.
i have an example application in mind that would work quite well for exploring many concepts such as movement and collision detection, AI, etc.
As a bonus, it would be fun to play as well (i used to sell a game like it).
however, that has 2 downsides to it:
1. all the files required to compile
2. its not language, library, OS, and platform independent.
for this reason, i think it might be better to use pseudo code, so the reader can implement with the language, libraries, OS, and platform of their choice.
while many of the concepts in this "different way", especially as related to coding discipline result in non-OO'ish code, i will make an attempt to give OO'ish examples where possible. After all, in the end its all just code and data.
And who knows , i might just break bad with some code before its all over.
Wow. What am i trying to say?
There's a different way to make games.
Much of the complexity of game development has less to do with the game itself, and more to do with the environment in which its created.
And much of that complexity is unnecessary for the small team or solo developer.
So i should probably start by saying that this "different way" won't work in many (most?) situations.
But when it can be used, there's little reason not to (at least that i've found so far).
So, when is it applicable?
1. when you have complete access to and control over the code.
2. when you can rely on discipline as opposed to hand holding to prevent problems.
3. when you're developing a single title as opposed to an engine, library, or purpose built reusable component.
These right there probably sum up how the development environment complicates the process.
1. when you don't have code access and control, you have to make editors and engines for the non-coders. you also have to design your code so any numbo coder on the team can use it and not blow up the game.
2. when you don't have disciplined coders, you have to do a lot of hand holding work in the form of designing "developer proof" code. Much time is spent on making it so other coders can't misuse code.
3. much work and consideration goes into design for re-use. however, not all code is reusable. not all code gets reused. its "anticipatory coding" - trying to anticipate possible future needs and designing the code to handle that eventuality. all fine and good, but in some respects, that's somewhat akin to "pre-optimizing".
So, from this list, you can see that these circumstances don't apply in all cases. If you work for a big studio, and have to deal with non-coders and numbno coders, you're done reading. This journal is not for you. But if you ever do an independent project after hours, you might find it interesting. If you're looking to break into the commercial game development industry where there are non-coders and bad coders this is not for you. but if your just looking to build a game without all the usual federcarb, this is for you.
i'll be concentrating on C++ windows directx development, but as always, most general concepts are hardware, OS, and graphics library independent.