Part 10: Making a spaceship work.
30 Years Ago:
Deepest Blue progress.
I now had spaceship with different engines, power and weapons. Rather than having a hard coded spaceship control algorithm I had to figure out how all the parts are going to effect the behaviour of each ship. I had to work out how a spaceship works in my game universe. You need enough true physics for a game to be believable but can invent technology to make it work as a game. That's why I like fantasy or scifi scenarios it gives you lots of licence to design things they way you want them. In reality space is so big it would be boring for a game. who wants to take years to get to the nearest planet? When you thrust forward in space you accelerate. Stop thrusting and you keep going at the same speed. Most games treat space as if it is air and slow craft down when the engine stops. When you turn unless you thrust in the new direction you just turn. I had already experimented and decided that I wanted a space feel with drifting but would let the engines have different grades of vectored thrust to help go round corners. For weapons I needed to animate the gun turrets and put the bullets in the correct place and make them fire in the right direction so they fire out of the guns. That involves some complex interaction with the custom 3d graphics of the ships. To put off this hard bit I thought I would add a HUD attitude display while churning it over in my mind.
"The old issue of 3d weirdness ..."
"The old issue of 3d weirdness ..."
I wanted a display of attitude as flying in space is very disorienting and it would help me tune up the engine routines. I wanted a usable minimal HUD rather than over the top artists pretty but in the way design. Rather than reinvent the wheel I looked up pictures of fighter HUDs. I implement something similar to a F14 HUD but found I had the old issue of 3d weirdness when pointing straight up or down. This is due to way attitude is measured as yaw pitch and roll. Roll can be thought of as the angle of the wings to the horizon (or any line of latitude). As you approach straight up or down this measurement becomes meaningless. Roll cannot be defined for straight up and down and changes rapidly near these points flipping as the craft tips on its back.
The resulting movement on the HUD was distracting. I could find no pictures of fighters pointing straight up or down so perhaps they have the same issue. I decided to replace the final steps of the pitch ladder with a compass rose representing the "pole" which looks nicer as it spins. Perhaps I can sell it to NATO and make some money.
I found some NASA data modelling the galaxy so converted this and wrote a routine to generate star fields and dust clouds. I need some faint graphics to overlay so the transparent layers build up and haven't installed my graphics program onto the laptop yet so set this aside, the code seems to work ok. I really must have a huge session on graphics soon then I can do some sexy screenshots.
Time for the big one..
The big challenge this month was to get the custom spaceships to work in the game. Its one thing writing code for fixed data that can be tuned but I had to cater for ships of any size, power, thrust and configuration of guns. That meant every routine had to be as general as I could make it. Data structures had to cater for different numbers of items which may change during the game.
I had already written a collection class I called pointer list which managed data existing as a list of pointers. The routines could create a list of a given size, add, remove items very quickly and increase the size of the list if needed. This gave a good combination of flexibility and speed and was also very easy to use. So I decided that each class of system would be represented by a pointer list. So far I have lists for weapon mounts, electronic systems, engines, energy generators/batteries and shields.
I had already anticipated the next challenge. Things like guns had to know where they were and where they were pointing. Also I wanted to animate things such as gun turrets so needed to be able to add extra transformations such as turret turning and gun elevation. I had cut my plot routines into two parts so I could run all transforms and store matrices for each part. Then the game and plot routines could both refer to this data.
And I thought this month would be less technical..
The first thing I tried to animate was a gun turret. And I thought this month would be less technical being back in the game code! When I tried to turn it on its axis it rolled. After a bit of head scratching I realised my routine adding custom transforms was incorrect: it had to add a custom turn before the part was oriented. This meant writing 3 more matrix transform routines to prefix a transform rather than append a transform. This was faster than the standard way of forming a another matrix to pre-multiply the parts orientation matrix.
On the fun side I could now drive big ships about with loads of guns and shoot at enemy ships blowing them into fragments. Shame the turrets refused to point in the wrong direction. I had installed a targeting routine based on the ships own fly to target routine but it was failing miserably. I played around with it for ages and finally twigged my mistake. I had used the guns orientation to drive the turret which was a different way up to the gun. I used to moan at artists drawing graphics in different orientations and now I had done the same. Things like guns and turrets might be anyway up on a spaceship so I got it working using the turrets matrix. Then the turrets tracked targets and blew them out of the void. I celebrated by taking my cruiser round and obliterated the AI ships that were still programmed to face towards the target (me). Now I will have to write a cruiser routine that tries to face the maximum number of guns to the target.
Collections.A collection is an object which allows you to manage multiple data items .If you are using a language like c# you will have at your fingertips a set of ready made collections. I am old school and write in C and like to write my own as part of my core code. Then I know exactly how they work and how efficient they are. They are a key part of my reusable code. I find in any program you need several different sorts of collection. There is usually a trade off between flexibility and efficiency. I like to write Initialise and Terminate routines for each so all memory is systematically allocated and de-allocated efficiently and under my control.
The simplest form of collection is usually supported directly by the language. In C it is efficient as long as you use a pointer to access a member rather than keep using an index. Use the index once to access the first member you want then increment the pointer to get to the next member rather than having to use an index again. This saves the code having to recalculate the address each time which involves a multiplication. An array is most useful when you have a fixed limit to the number of members. For large arrays I like to allocate memory rather than use fixed memory making the code size smaller. All you really need to manage an array is a count of members used and the maximum number of members and possibly a pointer to the array.
Arrays of Pointers
I call this a pointer list. The array contains pointers to data rather than the actual data. This is slightly slower to use than a direct array but is more flexible. If you want to reorganise the data to can just sort the pointers. \You can if needed allocate memory for each data item separately making it very flexible. My implementation also allocates the list of pointers and managers the size of the list. If needed it can reallocate a larger pointer list coping over the old pointers without disturbing the actual data. All you need is a used count a size count and an address of the actual array of pointers.
I have a more heavyweight version of this collection I call Group that can apply a function to all group members or sort the members.
Forward linked list.
Each data member contains a pointer to the next item in the list. Fast and flexible it allows sequential processing of the list. To add or remove members in the middle you have to know the address of the preceding item so this is stored as you walk through the list. I generally have a managing object which points at the first item and maintains the count of items in the list and the previous item processed.
Double linked list
Each data member has a pointer to the previous and the next item. This allows you to easily add items before or after any item. You have to be careful fixing up the pointers when you add or remove items but you only need to write this once. The manager object has pointers to the first and last items in the list as well as managing the count.
Each data member has pointers as in a double linked list but in addition has a child and a parent pointer so acts as the list manager of its offspring. These are really flexible for building up complex data trees with no size restrictions. I use these for Form parts which are hierarchical and my main game objects.
I tend to use the simplest kind of collection that the data needs. If you provide the same methods for each collection it is really easy to upgrade a collection to a more complex collection. I have a meta data description for each collection so I can simply load or save any data in a collection. Any addresses are converted to cross references on saving and reconstructed on loading. I have used these routines to load data from the internet. I just have to reformat a little to conform to my loading standard and provide metadata describing the order of fields and the data types.