First Steps
This episode is a bit techy, showing how started to organise the game
engine. Doing that puts off decisions about the game for a bit longer.
Which game shall I write for instance. Keeping options open is good for
the engine, I want to reuse it so it has to handle any game I want to
write. Then if I come up with a really good idea I can just get cracking
and get the game development flowing without being sidelined to writing
support routines. There is always the pitfall that you end up endlessly
working on the engine and neglect the game.
I remember Simulcra was a bit like that, Dominic left Graftgold before he had finished the game but it gave Graftgold the best ever game engine that we used for the next ten years.
The first task was to set up a development environment. I decided I wanted to write in C not C## , I have plenty of experience in both but favour the simplicity ,run time and flexibility of straight C though I tend to code in an object oriented fashion. I also wanted to use DirectX having experience in past versions. My other prerequisite was it had to be free. I had retired so could not afford licences. It also had to work on my laptop running XP. There I found my first major problem. The latest DirectX tools and Visual C compiler needed at least Vista. I decided to go to the next best option and settled on using DirectX 9 and Visual C version 10 until I could afford a new laptop. That is bound to cause rework later on, perhaps I should have stayed on at work for a bit until I upgraded.
"My laptop is closed in disgust, time for bed! "
Downloading everything from the Microsoft site was confusing getting the versions I required but reasonably straightforward. Getting any of the Direct X samples to compile needed a combination of science and alchemy. I just don't know how anyone new to programming would ever cope. It took me ages to get rid of message such as "no known reference to such and such". Yet a search could find the label. It reminds me of the Morecombe and Wise line "I'm playing all the right notes, not necessarily in the right order". After much playing about with the Visual C setup to pull libraries from the correct places in the right order I managed to compile a single sample. Then I find the next one doesn't compile and I can't remember what I have done differently. My laptop is closed in disgust, time for bed!
I have the same struggle to make my own code combined with a bit of sample directx code compile. It's like extracting teeth trying to get information about the errors I encounter. I find no end of posts with people experiencing the same sort of issue but few explanations of solutions. Eventually each issue is resolved until I have zero errors. That's a great feeling. Now I can start to really code.
Many months later
After a regular Microsoft system update I find my code is complaining of duplicate references in Direct X.
After much investigation I find it has changed the version of one of the modules in the system folder that conflicts with my version of DirectX. I finally solve this by changing some names in a DirectX module by hand to fix the incompatibility. Why do they have to change names of things?
"They seem to shuffle the settings around just to keep you on your toes."
When I used to program on the Spectrum at first I had to assemble my code by hand and then type in hex code in basic REM statements. The challenge nowadays is that the tools are so sophisticated that it is daunting at first trying to get learn all the options and tabs and secret settings so you can control your project. I have used Visual C++ for years and on the whole its a good robust tool but finding where some settings are kept needs a lot of patience. With each new version they seem to shuffle the settings around just to keep you on your toes. Getting a good structure for your development is essential from the start. I make up folders for the basic sections of the development I want to keep separate. I decide to have all the reusable system code in a separate solution split into projects with its own testbed project that will allow me to test each routine as I write it. I start of with the most basic of C programs that just starts and ends without doing anything. To create the skeleton of all the projects folders I need an idea of the largest building blocks.
This bit at least is easy for me having worked on so many game systems. The list contains project headings like Display,Sound,Input,Shell and Testbed. Shell is the lowest level project that will be referenced by everything. Display Sound and Input will be isolated from each other.
"It is like building a cathedral from matchsticks"
Its a daunting task when you start a development. It is like building a cathedral from matchsticks o the .but you don't yet know what it will look like. If you try and design the whole thing in detail from the word go completing the design is as hard as just ploughing into the code. The solution is to break the problem down into manageable chunks that you can think about. Then you can tackle the chunks one at a time , breaking them down into manageable pieces that can be written and tested independently so you can see progress.
In practise that is harder than it sounds. If you are not careful each chunk needs things in another chunk until its like the proverbial pile of spaghetti. You then cant write any of the chunks as they all need each other You need to be able to separate the innermost functionality, what I call the core systems. Then you can build on these foundations. I nearly always start off an engine in the same way. To write any major piece of code you need to be able to debug it to see whats going and you need to control memory allocation.
So for the debug I wrote a series of #define macros that let me easily put log messages and assertion tests anywhere in the code. I use macros as a wrapper to the debug routines for two reasons.
1. I can remove any of the debug code with a compiler switch without having #if DEBUG all over the place in the code.
2. I can automatically feed in the module name and line number by using __FILE__ and__LINE
#define LOG_FATAL(Bool,Text) LogFatal(__FILE__,__LINE__,(Bool),(Text))
#define LOG_WARN(Bool,Text) LogWarn(__FILE__,__LINE__,(Bool),(Text))
#define LOG_WARN_1(Bool,Text,Parm) LogWarn(__FILE__,__LINE__,(Bool),(Text),(Parm))
#define LOG_WARN_2(Bool,Text,Parm1,Parm2) LogWarn(__FILE__,__LINE__,(Bool),(Text),(Parm1),(Parm2))
#define LOG_WARN_3(Bool,Text,Parm1,Parm2,Parm3) LogWarn(__FILE__,__LINE__,(Bool),(Text),(Parm1),(Parm2),(Parm3))
One of the limits of C macros is there is no way to handle a variable amount of parameters. In direct call you can use the ellipsis .... as a final parameter and C knows that it has to handle in indefinite amount of parameters. This is used mostly for sending embedded data for insertion into text lines. The you can put meaningful messages such as "The speed (2345) is too big.". This was an absolute requirement so I tried for days to trick C into taking an ellipsis ... as part of the macro. A #define is really just a text substitution that happens before the line is compiled. Unfortunately the ellipsis breaks the syntax so it just does not compile properly. I really tore my hair out over this one, I tried all sorts of schemes, searched the internet but in the end was defeated. The solution was to write a different macro for each number of parameters, simple but clumsy. Sometimes I hate C. I like consistency.
Another really useful routine to write at the start is a really decent timer routine that measures in microseconds. Again I wrap calls to these in #define macros. My system allows you to "instrument" code by placing time macros that categorise the code that follows. The macro calls a routine that gets the time at that points and adds it to the category of the previous time call.
example.
TIME("draw");
//drawing code here
TIME("after");
Thus you can easily profile slow code to see which bits take too long. I then print the results to a log with a special call.
It felt great to get these written and tested. By putting two TIME calls next to each other I could measure overhead of the call. It would be self defeating if it was a significant time. I had my first bit of code up and running and furthermore could use it to assist debugging more complicated code.
The next system I tackled was a memory manager. It is really easy in C to have a memory leak , ie loose memory by forgetting to free it. I like to wrap the standard memory allocation and free calls with my own routine. Then I am in control. In the good old days C memory allocators were not good at putting freed blocks back together, keeping them as two smaller blocks. So I am in the habit of grabbing the memory I require and managing it myself. This means I replace most of the alloc and free calls and any overhead that goes with them. It allows me to define my own system, I like to name each chunk of memory while debugging so when things go wrong I can identify memory block. I put a safety marker at either end of the block to detect accidental overwriting, and chain the blocks with a simple forward pointer to the next block.
This is all hidden from the calling routine, it just gets a pointer to the memory after the special header information. I like to keep an account of how many blocks, how many bytes and the largest memory used.
I also crafted routines to just walk the chain of blocks testing they are not corrupt and to log all used blocks. At the end of the program I then can print the chain of used blocks and it should be empty, if I have remembered to free each block.
Bit by bit I got these routines working and although I had no display I could at least allocate blocks of memory , write a log file to prove it was working, free them up, check they were all gone. Its a very fulfilling feeling when you get a tricky bit of code working.
Every know and then while tracing Visual C just locks up at a random place in the code. I have to reboot when this happens so cannot investigate the cause. A debugger is meant to provide a safe environment for you to run a program, it should never crash whatever the bugs are in your code. I tried using a Visual C debugger to debug Visual C but could not get anywhere. I open up a case with Microsoft but get no helpful advice. Random problems are the worst sort of problems. I cannot rule out that somehow my code is breaking the debugger. I have plenty of experience finding the most devious of bugs. One last resort is to start removing code till the problem disappears. I end up with hardly anything left. Gradually I narrow it down. I find I have not allowed for the end of string marker at the end of the memory name, that's such a stupid mistake that has cost me days of frustration. Fixing it eliminates the problem. At my last job I wrote macros that wrapped every string call to check the string length. I vow to sort out a safe string system as one of my next tasks. My only consolation is that the debugger should not have just crashed on a completely unrelated line of code, I never got to the bottom of how the debugger was effected. I hate that. Maybe the freeze was caused by something else. Time will tell, to date it has not happened again.
"Programming a game is more like running a marathon."
Its funny how I have sort of regressed programming wise. At Graftgold in the early days I used to work strictly 9 to 5 with an hour lunch and morning and afternoon 1/4 hour tea breaks. That was so my 2 year old son could understand when he could see me. Now I often program into the night like a bedroom programmer. When the flow is there you just have to go. I do try not to overdo it though. I have seen too many programmers burnt out by working too late. Programming a game is more like running a marathon than a sprint. You have to conserve your energy for the long haul.
"One night the kids barricaded the front door so I couldn't go back to work."
In the later years at Graftgold we often needed to work into the night. I used to start do the 9 to 5 have a break then come back and work to early morning. It takes its toll. One night the kids barricaded the front door so I couldn't go back to work. Little hint like that tells you that something has to change.
---------------------------
I remember Simulcra was a bit like that, Dominic left Graftgold before he had finished the game but it gave Graftgold the best ever game engine that we used for the next ten years.
The first task was to set up a development environment. I decided I wanted to write in C not C## , I have plenty of experience in both but favour the simplicity ,run time and flexibility of straight C though I tend to code in an object oriented fashion. I also wanted to use DirectX having experience in past versions. My other prerequisite was it had to be free. I had retired so could not afford licences. It also had to work on my laptop running XP. There I found my first major problem. The latest DirectX tools and Visual C compiler needed at least Vista. I decided to go to the next best option and settled on using DirectX 9 and Visual C version 10 until I could afford a new laptop. That is bound to cause rework later on, perhaps I should have stayed on at work for a bit until I upgraded.
"My laptop is closed in disgust, time for bed! "
Downloading everything from the Microsoft site was confusing getting the versions I required but reasonably straightforward. Getting any of the Direct X samples to compile needed a combination of science and alchemy. I just don't know how anyone new to programming would ever cope. It took me ages to get rid of message such as "no known reference to such and such". Yet a search could find the label. It reminds me of the Morecombe and Wise line "I'm playing all the right notes, not necessarily in the right order". After much playing about with the Visual C setup to pull libraries from the correct places in the right order I managed to compile a single sample. Then I find the next one doesn't compile and I can't remember what I have done differently. My laptop is closed in disgust, time for bed!
I have the same struggle to make my own code combined with a bit of sample directx code compile. It's like extracting teeth trying to get information about the errors I encounter. I find no end of posts with people experiencing the same sort of issue but few explanations of solutions. Eventually each issue is resolved until I have zero errors. That's a great feeling. Now I can start to really code.
Many months later
After a regular Microsoft system update I find my code is complaining of duplicate references in Direct X.
After much investigation I find it has changed the version of one of the modules in the system folder that conflicts with my version of DirectX. I finally solve this by changing some names in a DirectX module by hand to fix the incompatibility. Why do they have to change names of things?
"They seem to shuffle the settings around just to keep you on your toes."
When I used to program on the Spectrum at first I had to assemble my code by hand and then type in hex code in basic REM statements. The challenge nowadays is that the tools are so sophisticated that it is daunting at first trying to get learn all the options and tabs and secret settings so you can control your project. I have used Visual C++ for years and on the whole its a good robust tool but finding where some settings are kept needs a lot of patience. With each new version they seem to shuffle the settings around just to keep you on your toes. Getting a good structure for your development is essential from the start. I make up folders for the basic sections of the development I want to keep separate. I decide to have all the reusable system code in a separate solution split into projects with its own testbed project that will allow me to test each routine as I write it. I start of with the most basic of C programs that just starts and ends without doing anything. To create the skeleton of all the projects folders I need an idea of the largest building blocks.
This bit at least is easy for me having worked on so many game systems. The list contains project headings like Display,Sound,Input,Shell and Testbed. Shell is the lowest level project that will be referenced by everything. Display Sound and Input will be isolated from each other.
"It is like building a cathedral from matchsticks"
Its a daunting task when you start a development. It is like building a cathedral from matchsticks o the .but you don't yet know what it will look like. If you try and design the whole thing in detail from the word go completing the design is as hard as just ploughing into the code. The solution is to break the problem down into manageable chunks that you can think about. Then you can tackle the chunks one at a time , breaking them down into manageable pieces that can be written and tested independently so you can see progress.
In practise that is harder than it sounds. If you are not careful each chunk needs things in another chunk until its like the proverbial pile of spaghetti. You then cant write any of the chunks as they all need each other You need to be able to separate the innermost functionality, what I call the core systems. Then you can build on these foundations. I nearly always start off an engine in the same way. To write any major piece of code you need to be able to debug it to see whats going and you need to control memory allocation.
So for the debug I wrote a series of #define macros that let me easily put log messages and assertion tests anywhere in the code. I use macros as a wrapper to the debug routines for two reasons.
1. I can remove any of the debug code with a compiler switch without having #if DEBUG all over the place in the code.
2. I can automatically feed in the module name and line number by using __FILE__ and__LINE
#define LOG_FATAL(Bool,Text) LogFatal(__FILE__,__LINE__,(Bool),(Text))
#define LOG_WARN(Bool,Text) LogWarn(__FILE__,__LINE__,(Bool),(Text))
#define LOG_WARN_1(Bool,Text,Parm) LogWarn(__FILE__,__LINE__,(Bool),(Text),(Parm))
#define LOG_WARN_2(Bool,Text,Parm1,Parm2) LogWarn(__FILE__,__LINE__,(Bool),(Text),(Parm1),(Parm2))
#define LOG_WARN_3(Bool,Text,Parm1,Parm2,Parm3) LogWarn(__FILE__,__LINE__,(Bool),(Text),(Parm1),(Parm2),(Parm3))
One of the limits of C macros is there is no way to handle a variable amount of parameters. In direct call you can use the ellipsis .... as a final parameter and C knows that it has to handle in indefinite amount of parameters. This is used mostly for sending embedded data for insertion into text lines. The you can put meaningful messages such as "The speed (2345) is too big.". This was an absolute requirement so I tried for days to trick C into taking an ellipsis ... as part of the macro. A #define is really just a text substitution that happens before the line is compiled. Unfortunately the ellipsis breaks the syntax so it just does not compile properly. I really tore my hair out over this one, I tried all sorts of schemes, searched the internet but in the end was defeated. The solution was to write a different macro for each number of parameters, simple but clumsy. Sometimes I hate C. I like consistency.
Another really useful routine to write at the start is a really decent timer routine that measures in microseconds. Again I wrap calls to these in #define macros. My system allows you to "instrument" code by placing time macros that categorise the code that follows. The macro calls a routine that gets the time at that points and adds it to the category of the previous time call.
example.
TIME("draw");
//drawing code here
TIME("after");
Thus you can easily profile slow code to see which bits take too long. I then print the results to a log with a special call.
It felt great to get these written and tested. By putting two TIME calls next to each other I could measure overhead of the call. It would be self defeating if it was a significant time. I had my first bit of code up and running and furthermore could use it to assist debugging more complicated code.
The next system I tackled was a memory manager. It is really easy in C to have a memory leak , ie loose memory by forgetting to free it. I like to wrap the standard memory allocation and free calls with my own routine. Then I am in control. In the good old days C memory allocators were not good at putting freed blocks back together, keeping them as two smaller blocks. So I am in the habit of grabbing the memory I require and managing it myself. This means I replace most of the alloc and free calls and any overhead that goes with them. It allows me to define my own system, I like to name each chunk of memory while debugging so when things go wrong I can identify memory block. I put a safety marker at either end of the block to detect accidental overwriting, and chain the blocks with a simple forward pointer to the next block.
This is all hidden from the calling routine, it just gets a pointer to the memory after the special header information. I like to keep an account of how many blocks, how many bytes and the largest memory used.
I also crafted routines to just walk the chain of blocks testing they are not corrupt and to log all used blocks. At the end of the program I then can print the chain of used blocks and it should be empty, if I have remembered to free each block.
Bit by bit I got these routines working and although I had no display I could at least allocate blocks of memory , write a log file to prove it was working, free them up, check they were all gone. Its a very fulfilling feeling when you get a tricky bit of code working.
Every know and then while tracing Visual C just locks up at a random place in the code. I have to reboot when this happens so cannot investigate the cause. A debugger is meant to provide a safe environment for you to run a program, it should never crash whatever the bugs are in your code. I tried using a Visual C debugger to debug Visual C but could not get anywhere. I open up a case with Microsoft but get no helpful advice. Random problems are the worst sort of problems. I cannot rule out that somehow my code is breaking the debugger. I have plenty of experience finding the most devious of bugs. One last resort is to start removing code till the problem disappears. I end up with hardly anything left. Gradually I narrow it down. I find I have not allowed for the end of string marker at the end of the memory name, that's such a stupid mistake that has cost me days of frustration. Fixing it eliminates the problem. At my last job I wrote macros that wrapped every string call to check the string length. I vow to sort out a safe string system as one of my next tasks. My only consolation is that the debugger should not have just crashed on a completely unrelated line of code, I never got to the bottom of how the debugger was effected. I hate that. Maybe the freeze was caused by something else. Time will tell, to date it has not happened again.
"Programming a game is more like running a marathon."
Its funny how I have sort of regressed programming wise. At Graftgold in the early days I used to work strictly 9 to 5 with an hour lunch and morning and afternoon 1/4 hour tea breaks. That was so my 2 year old son could understand when he could see me. Now I often program into the night like a bedroom programmer. When the flow is there you just have to go. I do try not to overdo it though. I have seen too many programmers burnt out by working too late. Programming a game is more like running a marathon than a sprint. You have to conserve your energy for the long haul.
"One night the kids barricaded the front door so I couldn't go back to work."
In the later years at Graftgold we often needed to work into the night. I used to start do the 9 to 5 have a break then come back and work to early morning. It takes its toll. One night the kids barricaded the front door so I couldn't go back to work. Little hint like that tells you that something has to change.
---------------------------
I always loved Diary of a Game features back in the likes of ZZAP! 64 and Crash. This brings back those memories, and promises to be a fascinating read!
ReplyDelete