Part 24 Docking HUD display

Graftgold Memories

Graftgold became well known for its conversions of arcade games such as Flying Shark and Rainbow Islands.  It all began when Graftgold signed a deal with Telecomsoft.  Initially Graftgold was to develop a number of Spectrum and C64 titles for our first ever advance. This allowed us to expand which meant renting office space and buying pc's for cross assembling. Telecomsoft had been let down for the C64 Spectrum and Amstrad versions and needed a very quick job done. They offered Graftgold a large cash sum if we could finish in 6 weeks. Dominic Robinson and John Cumming had just joined Graftgold after leaving Hewson. We looked at the game and decided to go for it. The game was similar in some ways to Uridium so Dominic had a strong idea of the way to approach the scrolling graphic engine. He used a cell based system where background cells could be replaced by active cells when sprites moved over them. The screen consisted of a huge array of cell addresses which pointed to background or active cell graphics.

It was an adventurous start, we had a strict deadline and were using a new development system called PDS.  We needed PDS to fix some show stopper bugs very quickly , especially the one that prevented us from saving and loading files to floppy disks. None of our PC's had hard disk drives being a new technology. We managed to  convince PDS to fix the system after a few frantic phone calls from a phone booth, as we were still waiting for a line to be installed.

We settled into a method to convert arcade games which we used for all our conversions. We would learn how to play the game and then play the game all the way through videoing the game. We always insisted on having an arcade machine to refer to. That was the way to make sure the feel of the gameplay was  accurate.  John  would use the video to copy the graphics for the backgrounds and the sprites.  He used to write graphics utilities on the Atari ST in BASIC that allowed him to speedily put background maps together. We used to use Dpaint to draw the graphics.

The patterns of AI  movements were copied from the video and translated into movement pattern data. It was essential to emulate start points , flight paths and speeds of enemies and bullets.

It was an important phase of Graftgold's development as a company. We had to act as a team each person doing their bit to get the conversion completed in time. I was finishing Magnetron on the C64 at the time as well as managing the company. Our Amstrad developer turned out to be a washout so he was quickly sacked and I did the Amstrad version when Dominic had finished. Amstrad was also a Z80 machine so most of the code could be used without change. It was the graphics and sound routines that were the main changes. I just converted each of Dominic's graphic routines to cater for the Amstrad's screen format and the game came to life extremely quickly. I had previously converted Ranarama to Amstrad so knew my way around the machine.

I used to like doing arcade games for a change. It is much easier having a design to copy rather than trying to make up the game as well as code it.  I used to put new staff on conversions if I could, to get them used to the Graftgold way of working, before immersing them in the difficulties of developing an original game. It also taught staff the elements that go into a game.

Deepest Blue Progress

I have been writing my new game for two years and it is beginning to feel like it will take forever. I seem to write reams of new code but the game does not change much. That is the nature of software development. It can take ages to crack a hard problem or to find an elusive bug , but at over times things can happen really quickly. Or you can slide back down the well you have painstakingly been climbing up!

I though I had better try to sort some of the persistent issues I had dodged trying to get things done. If I get stuck I find it  better to do something else for a while and sleep on the problem. It is amazing how sometimes in the morning you have worked out the answer while you were sleeping.  Sometimes though inspiration fails and I move onto something else for a while.  

To move from one play area to another I have designed a warp sequence which is meant to work like a 3d version of the tunnels in Avalon. The player sees a twisting warp path (like flying down the vortex of water flowing down a plug hole. The issue I had was the 3d collisions with the warp path edges were not working properly. The ship was meant to bounce to keep it in the warp path, but the ship ricocheted uncontrollably tumbling down the warp hole. So back to basics I revised my 3d maths to work out how you do a reflection off a tilted plane. In 3d things can get complicated so easily but if you break it down into components (X,Y,Z) the answer can be simpler.    I use the internet a great deal for education, the best articles are by other programmers who can explain things in easy terms. The hardest articles are by mathematicians. While their answers may be more accurate they are hard to understand and very often impractical in a game .

Another issue I decided to knock on the head was my docking HUD display.   I remember how hard the docking was  in Elite, or how hard landing in a flight sim.  I spent ages watching videos of fighters landing on aircraft carriers and of commercial flights landing. I wanted a practical display that made sense in space. In the end I combined features of several HUD's.  I coded the dock process to emulate a control tower giving an approach heading and permission to land. The player has to fly to a waypoint so they approach from the correct direction then uses the HUD to maintain an approach path. For the first time I managed to actually land my ship on a space station. I coded the routines to be general so they took into account the position of the dock point on both objects.  So I should be able to have ship to ship docking or landing on other ships. Also the main dock routines are the same for picking up cargo containers or other objects.   

While I was working on the HUD I thought I would have another crack at a nasty issue with the attitude display.  It was the old gimbal lock issue.As you point a spaceship or plane straight up or down  the compass direction and Roll angle become increasingly irrelevant. The worse case is heading straight up or down, then you are not travelling in any compass direction and the Roll angle is indeterminate. This leads to strange effects on an attitude pitch ladder that shows the angle of roll.
As you approach up or down it starts changing rapidly. I solved the issue by considering up/down maxima as zero roll than smoothing out a roll angle to zero as the ship approached the maxima. You still get a strange effect where the display has to reverse as you "go over the top" but that is correct. If you loop there is a point at the top when no roll turns into 180 deg roll.

I went back to the AI routines to make sure the changes I had made to make the docking work had not adversely affected the AI movement. Alas something I had done was preventing bases from building AI ships so I retraced the boot up sequences.  I found bases were putting all their spaceship parts on the market rather than building ships with them. That was accidental, they were just following the generic plant processing routines. I thought I may as well let them buy and sell parts from each other to speed production  in areas where ships were needed. Its funny how some design features seem to design themselves once you have the basic patterns in place.

I got bases building ships again and attached a camera to the first one built so I could see what it was up to. It flew to a warp hole then the game crashed as it did not know how to get to its destination. It turned out that it had picked up a Seiddab mission and was trying to fly to a system it knew nothing about. I had forgotten to put a faction selection in the assign mission routine. While fixing this I decided I may as well have each alliance have its own mission list.

Time to get the AI ships going down warp paths. That was all coded but not fully debugged. I found some issues with the generating of the scenarios. The initial factions are meant to know about everything in their initial space sector and some vital data was missing. In this game most of the data is computer generated which in the long run will save time.

Programming Tips

This time I thought I would pay tribute to the Z80 chip that started it all off for me and look at the evolution of graphics routines that essentially means moving  rectangular blocks of data around. My Z80 is very rusty but hopefully I can recall how I did it.

An ideal screen would be mapped out in machine memory  as we see it on the screen starting with the top left most pixel first and reading left to right along the first line of pixels. The next line and the next in the order we see them on the screen.

Then to paint the whole screen in order all we would have to do is.

Set a register the to screen address
Loop through lines
  Loop along a line through pixels
      set a pixel on or off
      add 1 to the  screen address
  End Loop
End Loop

In Z80 that is simple. It is built to move data around. Z80 is designed so its registers are 8 bit but some can be used in pairs to address data.  Its main registers are A,B,C,D,E,H,L each of which can hold a number from 0 to 255. However they can be paired up to maker 16 bit numbers which can be use as  data addresses. The Z80 was designed so that HL are paired as a 16 bit input  or output address , DE is usually an output address and BC  is used for 16 bit counts.  A is the accumulator a special register used to do 8 bit  maths or logic operations.

For a binary screen such as the Spectrum pixels are organised in bytes of 8 pixels.

So to set a single pixel the code would be
LD HL,ScreenAddress   ;set the screen address
LD A,FFH                      ;set all bits of the data hex FF
LD (HL),A                      ;move the data to the screen

to loop along a line of 32 characters we set up a loop by using a register as a count. The normal register for an 8 bit count is B.

LD HL,ScreenAddress   ;set the screen address
LD A,FFH                      ;set the data
LD C,32                           ;set the count
Byte:                                ;label to loop to
LD (HL),A                      ;move the data to the screen
DEC C                             ;decrement the count
JR NZ, Byte                      ;if the count is not zero jump to Line

Z80 is beautifully simple and intuitive to use. People have the idea that assembler is complicated, it is quite the opposite. It is a set of simple instructions.

So to paint our ideal screen we just use a bigger count which we can put in the BC register pair.
LD HL,ScreenAddress   ;set the screen address
LD A,FFH                      ;set the data
LD B,192                          ;set the line count to 8*24
Line:                                ;label to loop to
LD C,32
Byte:                               ;set the byte count
LD (HL),A                      ;move the data to the screen
DEC C                            ;decrement the count
JR NZ, Byte                      ;if the count is not zero jump to Line
JR NZ, Line

Note we could just make one loop if we set BC to 192*32. However when you decrement a register pair it doesn't set the zero flag so a JR Z instruction will not work. You would have to test the B register to see when it is zero.

If you tried this routine on the Spectrum it paints the first row of the first character line then the first row of the next character line for a third of the screen then fills in the second pixel lines etc. When you load a program if it has a loading screen you will be familiar with this strange stripy effect when the screen is loaded in address order. It is because the Spectrum pixels are not in the ideal order.  However if we are just interested in copying or painting the whole screen we need not worry about that as it sorts itself out by the time it is done.

Z80 is designed to move data. You can set HL to the address of the data to copy, DE to the address of where you want the data and use a LDI instruction. The LDI is the equivalent of
LD  a, (HL)
LD (DE),a
except it does not actually use the A register. That is a powerful instruction. It sets the parity bit when BC has reached zero so you can set up a loop using JP E ,Line

To save the time it took to do the JP E instruction we would code a list of LDI instructions. So the paint screen routine could be something like

 LD HL,ScreenAddress
 LD DE,ScreenAddress+1
 LD A,0xFF
 LD BC,32*23
LDI  ;copy 1 byte forward
 JP E Loop
LDI ;last 7 bytes

An even faster method used a special register called the stack pointer. Its proper use is to maintain the address of memory where data is stacked whenever a call is made , so the CPU knows where to return to. You can also put a value on the stack using a PUSH instruction. So by storing the value of the stack register  (and later restoring it) you can temporarily use it. If you set it to the end of the screen data (as stacks work backwards) you can then just push a register pair repeatedly onto the screen.
LD (SPStore),SP
LD SP,ScreenEnd
LD C,n    ; n = half the count depending on how many pushes we code
PUSH    ;the larger the list the less is the loop overhead but the bigger the routine is
JR NZ ,Loop

The funny address order of the screen could pose a problem when drawing just part of the screen. One way I used was to type in a  data list of the funny addresses of each pixel line, the for any line on the screen I could just look up the start address.  One trick we used was to align this sort of table on a 256 byte boundary so it had address such as  CE00.   Then you could load the H register in the high byte of the table address (in this case CE) and the L register with the look up index, in this case the Y
value to draw. So to get the screen address for any Y row at byte X.
LD H,TableHigh
LD L,YValue
ADD A,XValue


Popular Posts