Part 21 Building mega bases in space.
Graftgold Memories.In the early days ordering things was a bit of a hit and miss affair. Many of the firms supplying hardware add-ons or software were small businesses and sometimes the quality was less than desirable. I once bought a music development program that ran on the C64. It was just about impossible to use as it did not gate the key input. That is if you pressed a key sometimes it would register many times. Normally you gate the input so you do not recognise a key down until you read a key up. I did notice at the demo of this software that the demonstrator had a weird keyboard style flicking his finger at each key to get a short sharp staccato press. As the commands were all single key presses it caused all sorts of problems if you typed normally. When I complained that it needed some improvement , helpfully explaining how they needed to program the input routine, the company refused to admit there was any problem. They said they could find bugs with any program if they fiddled about with it. Not ours! We took quality control very seriously and would not release a program with a known flaw. We tested each others programs trying to find issues, not trying to make them work. Hewson then gave the programs a second level of testing but found very few bugs. More often they would come up with ideas for improvements and we would discuss these. Sometimes they found the control too hard or wanted the gameplay tweaked. Usually very few changes were made, they trusted we knew what we were doing and that paid off.
Later other publishers would try to push us to out their ideas into a game and that usually did not work. I remember one game where the publisher wanted us to add a host of embellishments to the main player control. Bear in mind that we had designed all the maps for the existing tight control mode. So we added new animations for skidding to a halt, acceleration after a slight skid when going into a run etc like a cartoon character as they demanded. We changed all the maps as jump gaps were affected. The control mode was then sloppier than before but quite pretty to watch. We sent the game off for their testing and they cam back and said it was better before. At least they admitted their mistake but it had cost maybe a man month of work. Sometimes simple is best. \I remember a space game with a moving ship at the bottom of the screen and testers of a publisher agreeing it was the best control mode they had seen and praising the clever programming. I pointed out that it had no acceleration and just moved left and right at a set speed so was actually the easiest way to move a sprite.
Deepest Blue Progress.So I have ships that can be built from predefined parts, each with various functions such as proving energy, shields, weapons or just hull structure. I have a space divided into regions connected with warp paths that have to be discovered. I have a set of AI routines to navigate , fight , trade between bases but need to add more detailed stuff such as asteroid clearing, picking up containers. The big thing that is missing is the space bases. These are an essential piece of the game plan. They process the various resources to make other resources. The trade is between these plants and that makes money which pays for the ships. Ultimately its the resources that the fighting is over and the bases are the things that have to be protected. All this is designed to give the world an organic life cycle which acts as a background and purpose for the action.
HUD Docking Display
I had some test bases in the game to work on the AI ships docking routines. I thought I would take a break from the hard stuff be programming the player docking routines. I wanted a HUD display the looked pretty but also clearly helped the player to dock at the correct angle and position. Sometimes things you think are going to be simple turn out to be a great deal more complicated. Docking requires controlling relative speed and angular momentum in 3 dimensions, that's 6 different things to monitor, plus the 3 special distances so you stop at the right point. I had a look to see the display they use dock to the international space station. The lack of graphics on some of the displays looks really ancient, just lists of numbers.
I tried a couple of approaches. One gave a HUD display that showed the a dynamic path leading from the ship to the docking point. I started using a homing algorithm then realised this was not good enough. You not only have to arrive at the correct place, you have to be in the required orientation. This was not trivial, the easiest way seemed to be to start at the dock point in the correct direction and work toward the ship.
I also tried a virtual runway approach were I displayed a stylised runway on the HUD , fixed in space. This had the advantage of being simpler. However with the player ship visible on screen when the ship turns the camera shifts position to stay behind it. (Imagine the camera on a stiff pole extending out the back of the ship and slightly upwards). The shifting of the camera makes the runway turn as you would expect but the parallax effect with the ship causes it to appear to swing across the ship when making course corrections. It was just not easy to use in this camera mode which is quite popular.
"shades of Uridium "
I also tried using the standard attitude HUD which shows an artificial horizon and an attitude ladder. This was promising but did not show enough information. I tried various combinations but it was all too easy to give too much information. I started to realise half the problem is that docking is a tricky operation. Landing is bad enough. I have often crashed on a flight sim after a successful mission especially if the frame rate is slow. I thought that maybe I should get some big bases in the game and try runway landing. It would be fun to have huge bases and runways that you could fly down and strafe, shades of Uridium or StarWars.
The problem with huge graphic models is they need to be made of smaller chunks so you can do things like collision testing or visibility testing. You don't want to plot polygons not in view so it make sense to discard parts of a model that are off screen. I already had a system of building ship models from ship parts but thought I would go a step further. Base would be made up of macro pieces that were effectively like a ship made up of parts. These macro pieces would be self contained base parts that had a specific function such as converting raw ice asteroids to water. Then several base sections could be joined to form mega bases rather like space cities.
Mega base editor.
This meant tweaking the space dock ship editor screen. It need to be able to work in three modes.
The existing one to make a ship out of parts.
A new mode to build base macros out of parts.
A new mode to build mega bases from base macros.
It need a new plot routine to orchestrate the drawing of the mega base. This was fairly painless.
The most difficult part was joining the pieces together. I remembered all the fun I had trying to get the ship parts to fit together. Now I had to figure out the positions and orientations when the parts themselves are composed of parts in various positions. I ended up revising the original routine to make it easier to use. Each part had a set of joins with angles and positions. I had to promote the required joins to the macro part so that could go through the same routines. After much head scratching and drawing diagrams it all came together.
Back in The Game.
Now the big test, create a mega base and save it out and see if it appears in the game. This revealed a few bugs in my reorganisation over the last few months. New code had to be added to process a mega base, treating it as a collection of bases that shared resources. Finally the base appeared on screen. The collisions did not always work but it was being drawn in the right place. I switched on my guns and gave it a pasting. The bullets were not always finding their mark but the feeling was good. Now to add some test runways and get the docking/landing working for the player. Then I need to revise the AI docking to use the new system.
Andrew recently was tweeting that there ought be more resource about debugging given that technology of debuggers has changed so much over the years. I only use a fraction of the capability of Visual Studio most of the time. Here are some of my techniques.
Debugging is rather like detective work. The process begins by identifying there is an issue with some code. Debugging is the collection of evidence regarding the issue, using this to form a hypotheses of what may have happened and devising tests that are devised to disprove any incorrect hypothesis , eventually proving the correct hypothesis.
So where do you start? The first step is to understand what the issue is. To do that you need to know what is supposed to happen and what has happened. It may be the issue is actually what it is designed to do, either because the design is wrong or your expectations are wrong. If you are not working with formal design specifications or documentation then you need to rely on your understanding of the code and its annotations.
Testing is a vital part of collecting evidence. You can narrow the investigation down by trying to identify specifically when an issue occurs. Chance can lead you in the wrong direction, if you form an idea of what made an issue occur make sure you prove it by trying it again with and without your idea .
When a fatal issue occurs it is important to collect as much information as you can before retrying and destroying the evidence. If it is a rare issue it may not occur for ages. Try to understand at least the end sequence of events that "caught" the issue. Don't be tempted to make a fix before proving you know what is happening. It is all too easy to make a bug worse.
In the worst issues the whole thing goes pear shaped and destroys any evidence. Record what happened and retry. Try to narrow down the possible cause. If this does not help use a binary chop approach. Take out half your program and retest. Keep reducing the code until it works. Put the offending code back and start taking bits of that out. Thus narrow it down to a small section of code.
So what evidence can Visual Studio provide? The main things I use are the stack display, tracing ,breakpoints watching variables and watching memory.
If it is intact the stack shows the nested calls the program made to get up to the present position. You can click on any line in the stack display to change the stack frame. This puts the view of the relevant code in the code display and allows you to view the local variables as used by that stack frame. Thus you can see if the correct values were passed into the call. It allows you to quickly navigate up the chain of calls to get a feel of what is going in, giving you the context of the current processing.
A well designed and written program has good call names with definite tasks so it is easy to see what is happening from the stack. For some classes of bug the stack may be corrupted so shows nonsense. When this happens immediately suspect local variables because they are stored on the stack as part of the stack frame. If you have a local array or a pointer to local variables you can easily write over the call return addresses on the stack. So when the stack appears to be rubbish immediately suspect local variables being misused. Never return the address of a local variable to a calling routine. The variable does not exist after the return instruction. Finding out which routine is causing a stack corruption is hard. Use the binary chop method or clues from any data you can look at.
This shows a block of memory accessible from your program. You can drag pointers from the code window and drop them onto the memory panel to display the memory where they are pointing. You can copy and paste names or addresses or type them in the Address. The slider is awkward to use as it scrolls too quickly for a small window. Instead use the cursor keys to scroll or page through the memory. What I like about this display is that changes are shown in red. If you suspect you are writing out of bounds look at the memory and see the red changes. Look slightly above and below where the data should be to check it goes in the right place. You can even change the data if you type over it, but take care.
Sometimes I need to debug why a field gets a strange value in it. I will set up a memory window then do a course trace until it changes. Then I will repeat this time stepping into the call that caused the issue.
I tend to use the watch display more than the local display as I can just select the things I want to monitor rather than seeing everything. If you always use the same names for your main pointers then you can use the same watch over many calls following the processing. You can change values by overtyping, which can be useful to set up specific test conditions. Changes are again shown in red.
A typical use of watch will be to quickly check all fields of an initialised structure hold reasonable values rather than hex CDCD in debug mode. You can also easily spot released structures with values of FEFE.
Note the easily read 4 character name that identifies the structure in my example. It is useful to add these, at least in debug mode, to help you find your way about and easily spot a mistaken object. It can be hard to remember which is which sometimes when doing operations like collisions involving more than one object.
Tracing can be time consuming but is essential for proper testing and debugging. I like to trace all new routines to see them working as I intended. This often gives me insights into how to improve them to work better or be more robust. I put breakpoints (F9 or click margin) on all code routes and remove them as they are traced. This is a simple way of code coverage. Any remaining breakpoints are on code that has yet to be exercised to is not yet prove to work. I use the trace along with the other displays to see what is happening when.
A typical problem of tracing is that you may be interested in just one specific object amongst a huge number making it difficult to trace. You want to skip over all but the chosen object. You can do this with a conditional breakpoint or by inserting an extra if statement isolating the object, To set a condition right click on the breakpoint and choose condition. You then type your condition as a C expression. There is an option to break if the evaluation changes, useful for finding when a variable is unexpectedly changed in a huge loop that would take ages to trace.
You can also break when a line is executed a number of times by selected Hit Count. You can disable breakpoints with useful conditions that you might need later.