Introduction

Recently I have been writing some routines for a PC game that is 2D and needs some backgrounds on which to play. I had only done some space games on PC before, which cunningly only need a starfield backdrop. I used a photo I took of the Milky Way on a clear night from my garden. 


PC Monster Molecules

I knew that the time would come when I would need some nice scrolling backgrounds. I even wrote a routine to draw a background from arbitrary sized squares and cunningly used my starfield photo as the source squares, or tiles. I incorporated code to flip tiles in X or Y, and give them individual palettes, everything I could think of that the retro arcade machines could do. Everything I will need to write retro games.


Computer Hardware

Some 8-bit computers had character tile modes. That is where the screen is built from a small number of 8-bit character codes, and the video chip looks up the data to draw the characters. This may be just an alphabet, some punctuation and some rudimentary generic graphics. Forward-thinking computer designers allowed the software to redefine the character set with all user-defined graphics, sometimes in multi-colour mode so that artistic things could be done.  

C64 games were often done with user-defined graphic "tiles", You get up to 256 of them and can use split-screen techniques to change tile sets for separate score panels. The actual screen can be up to 40 tiles wide by 25 high, which means the screen size can use 1,000 tile characters, which is slightly more than the CPU could update in a 50th of a second, which is why most scrolling games have a smaller play area than the full screen. 


Game Maps

Any scrolling game will have to have a larger map of tiles that are used to create the play area. Games that only scroll in one direction can potentially unpack the map as they go along. These type of games, such as Scramble or R-Type, tend to move slowly along to reveal more new scenery. I tended to prefer a free world where you can go where you want, scrolling freely in 2 or 4 directions. That tends to mean that the whole play area map is unpacked and laid out in memory, ready to update the screen. Now these maps tend to be quite large. Since one screen is 1,000 bytes big, then a play area of tiles might easily be 16 times larger, that only gives you 4 screens wide and 4 screens high. 16,000 bytes is a quarter of your C64 used up. A game will also have a number of levels, shall we say 16? Suddenly that is 4 C64s`-worth of space used up if we didn`t somehow compress the data. In our single load games we need to be able to store all the compressed data for those 16 levels as well as having a 16K buffer for for one map, and all the game code, and all the graphics for the tiles, and the sprites.

We need to be able to efficiently compress all the level data, and additionally we need to be able to design and specify all of the game maps in the first place. Back in the day, I designed all my own graphics for fonts, sprites and backgrounds. As a programmer, I might well have my own ways of designing the level data that a more modern graphics artist would not tolerate. 

A game designer has to decide how to create the game maps and how to compress them. We used to meet other programmers from time to time and some of them would proudly tell me about their tile mapping utility they had written to create their game maps. Sometimes they might even leave them in the final game. I would always ask them 2 questions. Firstly, how long did it take them to write the mapper, and secondly, will they use the same mapper for another game? They might well answer "6 months" to the first question, noting that their utility was likely customised for their own game and not necessarily generic enough to sell to anyone else, which answers the second question: "No, it might not be used again." Knowing that I could write a whole game, draw all the graphics and create all the map data in 5 months or less, I was reticent to even suggest to the boss that I write a map editor, with no experience.


Examining Past Efforts 

As all C64 programmers know, the VIC-II graphics chip with its tile mapping capabilities can perform a number of tricks that can`t be done as easily on a bitmap screen. One can alter a single tile image and every instance of that tile on the screen gets updated for free on the next 50th of a second display frame. With that, we can animate characters, do parallax layers, make things dissolve and reappear, or move objects across the background. Tiles aren`t just for decorating the bathroom, these are alive. Programmers therefore need to reserve some tiles for graphics tricks, and it can also be wise to have control over what tile goes where in the set of tiles so that collision detections and orderly transformations can take place, such as blowing up ships on the runway. Duplicate graphics can also be used as secret markers to control game elements, such as Gribblets reserving their new landing spot as they take off to avoid collisions, and knowing when they are in the home cave. Rules are created that don`t interest graphics artists, and no-one wants to have to rearrange all the tiles after the maps are drawn. It wouldn`t surprise me if all mappers have a function to swap tiles around in the tiile sets when the programmer has another "great idea". 


Gribbly`s Day Out

My first game with a tiled background, had me falling into line with other C64 game designers and arcade games of the time. I wanted an organic look to the scenery, which meant trying to hide the square edges of the tiles. I was using sprite to background hardware collision detection for pixel-perfect collisions, which is almost free in terms of CPU time but is also painfully perfect. I had to then use the colours carefully since only two colours of the background caused collisions with sprites. 

In order to build the background maps, which had to fit into 256 characters wide by 64 high, I did some preparatory work. The edges of the buffer were filled in with side walls and a top ceiling, and then some base characters of the floor. I over-simplified the scrolling and co-ordinate system to lock Gribbly in the middle of the screen rather than locking the scroll near the edges and let him approach them. That wasted some space, sorry! I then applied a block of tiles all over the map that contained the triangular energy barriers and the control buttons. This initialises all of the buffer to the blank sky character. I believe that left a couple of uninitialised character lines at the bottom. Most of the maps would then need a nice piece of land to bounce around on, and maybe some water. 


To set up the bottom of the map, I then designed some blocks of tiles of arbitrary sizes and assigned them unique numbers and input assembler declarations of bytes for the width of the block and then for each column a height byte followed by the tile characters in the column. The blocks might only be 1 tile high and a a few tiles wide for a stretch of grass, or be a bit of lake with a large rock formation sticking up. I would have an address lookup table to get from the block number needed to the actual block data. There would then be a simple list of block numbers to fill the width of the map from left to right. 


C64 Gribbly`s Second Day Out


At this design point, I realised that there would be a lot of blocks for level land to move about on, some blocks to bolt to the sides of the map, and I would need some craggy rock formations that provided the underside of higher platforms. Following the setup of the bottom pieces, the list became busier as I then had to provide positions for all of the remaining scenery blocks. I tried to make the major rock lumps quite big, and fit together at the sides so as to reduce the number of blocks needed. The list was then made up of groups of an X position, a Y position and a block number. All of those fit in a byte each, keeping the data compact and bijou.

Next I realised that some of the scenery would cut across energy barriers in the sky, and also I would like to switch off or remove some energy barriers to make flying routes, and maybe also remove some buttons so that I could create locked areas, barriers both on and off. There were tile blocks to switch barriers off in the 3 faces of the triangles in one go. I hope I was smart enough to remove barriers and then plot in the rocks afterwards. It was easy to swap the sequence of the blocks by cut-and-pasting the list of blocks. Later blocks tended to get smaller as I might want to patch in small corrections.

Levels tended to take about a day each to enter, test and adjust. The tougher screens took longer because I had to practise getting through some of the tight gaps. If they were too tough then I might have to move a lot of scenery so I did build them up in stages and test them a bit at a time. I could correct small mistakes and adjust the difficulty at the same time.


Paradroid

I reduced the background tile maps to 160 displayed characters wide by 64 high. This game though was set on a space ship and viewed from top down. Straight lines were the order of the day. I figured I could build the decks of the ship from fixed size blocks and therefore they would all fit together. Tile blocks of 4 by 4 characters were created, I ended up with 32 of them. I already had it in mind to have a miniature deck display where the 4x4 tiles would be represented by just one character each.


C64 Heavy Metal Paradroid


I was still entering the map data into assembler source files and chose to pack the map data down some more by using run-length encoding. I may have used the remaining 3 bits of the tile codes as a count of 1 to 8, which suited horizontal walls and corridors nicely. So the maps were just a list of bytes containing a tile code and a number of them. I didn`t bother with a second list of alterations as it likely wouldn`t have saved much. It was much easier to set up the maps. I had them all drawn out on graph paper as all the lift shafts needed to fit together properly to feel like a real place. It was then a case of counting the blocks and typing them in. Naturally there were frequent errors and miscounts as the decks would sheer somewhere and I would try to patch the data and thus be able to enter multiple corrections in the source code together in one go.

I can`t remember exactly, but I would have had the whole ship entered in about a week and didn`t alter it much, if anything, as the ship design provided encounter areas of various sizes and was mostly non-negotiable in shape. I may have added extra energisers later, but that was about it.


Uridium

Uridium came along next and with shadows being cast from tall obstacles, the 4x4 block idea was going to be limiting, so I resurrected the Gribbly`s system. I created tile blocks of the height of the screen and filled the smaller map buffer, now about 19 characters high and 256 wide still, from left to right. This created the basic dreadnaught platforms with nothing on them. Then I was able to use the secondary Gribbly`s system of adding arbitrary-shaped tile blocks onto the dreadnaught platforms. I could do two levels in a day as this was a straightforward process and the difficulty of playing the levels ultimately decided their sequence in the game. While i may have had an idea to make a difficult level or an easy one, I could later compare them and swap the data lists about. 


C64 Uridium


The new level being worked on was always tested as level one. What`s the point of playing all the way through to level 11 before you get to test the new data? I would let my test team lads have a play and they would let me know if something was too difficult, or too easy!


Alleykat

I always like to change things from game to game because, to me, being accused of re-hashing the old stuff would be a horror. Also, one does tend to expend a lot of ideas in a game so it`s best to reset with something new. There might be some ideas that couldn`t go into the previous game. I went for a vertical scrolling game instead here, so I reconfigured the map buffer to be 40 characters wide and 256 high, which would have saved me some valuable bytes for extra graphics sets. There were 8 different sets of tiles in there.

I suspect that I was getting tired of entering all of the map data because I decided to write the routines to generate the race tracks from a set of parameters and some predefined shapes. I had background blocks that were up to 5 character tiles wide and 3 high, and all the 8 graphics sets were interchangeable. The tracks were then defined by how far apart the rows were and what the density of big pieces was. That also decided how many gaps there might be to drive through, how many energy blocks there might be to pick up, and the collapsibility of the pieces. 

The map algorithm cleared the buffer to the blank ground piece, then randomly set up the lines of scenery such that as it filled the lines from left to right, it would exactly fill the row with pieces, nothing cut off mid-way. Then it would sprinkle a couple of different ground tiles in the gaps between, along with energy pickups. All I then had to do was select some eye-catching colour schemes. 


C64 Alleykat


Maps then were still made from some predefined blocks of tiles, but the random number generator decided on the final layouts.   

Morpheus next, which had no tile maps.


Intensity

This game had 70 different single-screen levels, all made from tile map lists. I chose Uridium for the graphics style and the tile block methods, but added a new type of tile block, the 3-by-3 expandable rectangle. I realised that I could use 9 tiles, with the option of a ring mode with a missing middle. Now I could draw any size rectangle, right down to a 2-by-2, with a string of 5 bytes for the position and size of a rectangle and a last number for the block layout, of which there might not have been many. Likely at this time then I would also have used a simple repeat of a single block either horizontally, vertically or even both. 


C64 Intensity


Platforms could be laid out roughly with the rectangles and then decorated with details using the usual X, Y position and block number. I did then generate a height map from the tile codes and then from the height map I set up a desired altitude map around the high points. That process involved effectively plotting a tile block of heights onto the altitude map but assigning the highest value of of the tile and what s already on the map. 


Rainbow Islands

In 1989, when we worked on Rainbow Islands, we didn`t know how the level maps were laid out in the arcade machine ROM. The designers of Rainbows Islands at Taito might have had a map editor to create the backgrounds, this seems likely, and they might have been stored raw on big ROM chips on the arcade board. More likely they would at least have been compressed with a generic compressor and stored packed and unseeable. So even if we had the kit to go looking at the ROMs, we might not have found anything useful as compressed data requires the decompressor software. 

We chose to video the whole game. John Cumming: our programmer and graphics artist, set about writing a map editor using STOS, a BASIC platform with graphics additions, for the Atari ST. This took him a couple of weeks. He then had to sit there with a VCR and our video of David O`Connor, our best arcade player by far, playing through what we thought was the whole game of 7 islands. John would pause the tape and recreate the background. We did get some early level graphics sheets from Taito with the tile sets on. We still had to map them to less colours as the arcade machine could display more colours than the ST. John spent another couple of weeks generating 28 level maps of ever-increasing size.



Enhanced Rainbow Islands for PC/PSX/Sega Saturn


We then hit the problem of how we were going to compress 28 levels of data that was up to 256 tiles high by 40 wide, an estimated 450K of data that needed to fit 4 levels at a time in the target machines. These were 8x8 pixel tiles, which now seems a bit unusual given that the arcade machine had a 16-bit CPU in it, but quite possibly the graphics chip was still configured for 8-bits. We followed suit on our 16-bit platforms, knowing that we also had to produce 3 8-bit versions too. It was at this point that David observed that the graphics for the backgrounds were often built from 4 tiles in squares. These would repeat all over the place. We therefore set about writing our mega-compressor that would consider the 4 level maps of each island, looking firstly for adjacent pairs of the same characters, and substituting in a single macro code for the pair. It could then look vertically for pairs of tile codes or indeed new macro codes that occur more than 3 times. It would then substitute another macro, this time a vertical one. Then we might do another horizontal pass looking for patterns 4 characters apart. Then again with vertical. We could do up to 8 passes. 

The end result would be a table of macro pairs and spartan maps with macro codes and usually big gaps between them. We could then pack the maps down and record the lengths of the gaps as one code instead of all the blanks. On the one hand this produced excellent packing results, on the other hand it did take the Atari ST an hour of huffing and puffing to produce the data. We then had to write the decompressor code as well that sits in the game code to this day. Unpacking is pretty fast as it only has to go through a level once to put the gaps back in (starting at the end of the data), and then 8 times to unpack the macros per pass in either horizontal or vertical mode. Packing had to pass through 4 levels of island data for every macro it created, looking for every instance. 


Paradroid 90

I first tinkered about with 32x32 pixel tiles in 4 colours and had a 4-directional scrolling demo on the ST. The sprites were 16-colour but we thought that the backgrounds didn`t look 16-bit enough. There were some nice colourful games coming out and we`d have been shunned with only a 4-colour background. We were wedged firmly between a rock and a hard place, because avoiding horizonal scrolling got us grief from the Amiga crowd.  


Amiga Paradroid 90


We decided, since we were supporting the Atari ST and Commodore Amiga, that we would continue with 8x8 graphics in the same manner as Rainbow Islands. Paradroid had used 4x4 tile blocks that would suit our mega-compressor still, but now we had a mapper program and while I designed the ship layouts, we could let the graphics artists do the decorating of the levels without having to stick to the strict 4x4 tile blocks. We expanded the mapper program to support up to 16 maps for compression so that we loaded in one ship at a time. The downside of all this efficiency was that the mega compressor took 90 minutes to pack and save the map data for a 16-deck ship.

No new tile mapping techniques this time, just a lot more data.


Fire & Ice and Uridium 2

We finally went 16-bit proper and optimised our game systems for 16x16 pixel tiles. I believe we had a new tile mapper program, likely on the PC, written in DOS, being pre-Windows, and certainly in 16-bit. 

The sort of feature that you need in a mapper is to pick up a block of tiles and then plant it in multiple places, which is not so dissimilar from using instructions to plant blocks of tiles. When one uses a mapper program, inevitably a generic compressor gets used, which means you need to know how to decompress the data and have that in your game code. The latter is not necessarily available unless you write both halves yourself, or you know someone who has. We knew Factor 5, thanks lads.


a
Amiga  AGA and CD32 Fire & Ice

The maps for these games were created mostly by the graphics artists and were sufficiently detailed and varied that a generic compressor would have as good a chance as any to pack the data down well. Fire and Ice level maps were typically 8 to 10K before compression with up to 5 per land. Compressed maps were combined with land-specific graphics, sound samples, music and meanie control data and might run to a total of around 70K.


Amiga Uridium 2

The Present

Fast-forward 30 years and I find myself needing a tiled playfield for a game on PC, I`ve done a space game and now I need a top-down background that scrolls. I have the software to plot a background with tiles of any size I choose, and with typical screens being 1920x1080 pixels, I need a lot of background data. I tried my setup on a 4K monitor and my 64x48 tile map of 64x64 pixel tiles barely scrolled at all before running out of data. 

Wrestling with the enormity of the task of writing a tile mapper program, I still resist and spent a few hours writing all of the code to do what my entire 8-bit catalogue did for tile mapping. Ultimately, it came down to a loop within a loop within a loop within a loop. I can define 4x4 tile blocks and specify blocks in the Paradroid style or arbitrary blocks of tiles to be plotted consecutively or by specified positions, with or without repetition. 


PC Project 3 (Name undecided)

I then realised that I can go further with more functionality such as a flood fill, which is great for filling in the gaps in debug mode before the map is complete. Further than that, functionality is there to maybe swap in alternate tiles in areas for graphical variety rather than obliging a graphics artist, or me, to spray alternate graphics randomly. I can also flip tiles in X and/or Y and apply a 90 degree rotate, so a randomiser of solid textures might be able to arbitrarily flip certain blocks for more variety.

Here`s a teeny bit of C code that generates my test map:

long Wave1BlockList[] = {

PBlock1x1_XY(1,1,UB_Small_Tower) // Single blocks are directly specified

PBlock1x1_RXY(4,5, 34) // Including repeated ones

PBlock1x1(UB_Small_Tower)                               // Place another tile after the last block 

PBlock4x4_XY(0, 6, Crossroads_4x4)                      // Paradroid-style crossroads

PBlockNxN_RXY(12,1, RoadHorizontal_NxN)                 // Add a road to the right

PBlock4x4(Crossroads_4x4)                               // Add another crossroads

PBlockNxN_RXY(12,1, RoadHorizontal_NxN)                 // Then some more road

PBlockNxNVertical_XY_RXY(0,10, 1,12, RoadVertical_NxN)  // Add a vertical road frpm the 1st cross  

PBlockNxN_XY(14,7, ZebraVertical_1x2)                   // Put in a zebra crossing

PBlockNxN_XY(1,14, ZebraHorizontal_2x1)                 // and another zebra

PBlockNxN_XY_RXY(0,17, 1,2, LevelCrossingHorizontal_4x1)  // Add a train track across the road

PBlockNxN_XY(23,6, LevelCrossingVertical_1x4)           // Add another train track over a road

PBlock1x1_XY_RXY(12,0, 20, 6, UB_Earth)                 // Create a muddy patch of forest

PBlock4x4_XY(4,10, HedgedBuildingTL_4x4)                // Create a small hedged plot

PBlockNxN_XY_RXY(0,36, 64,1, EastWestRiver_1x5)         // Make a long river

PBlockNxN_XY_RXY(0,41, 64,1, SouthCoast_1x7)            // Make a beach and some sea

PBlockNxN_XY(27,23, HedgedBigBuilding_NxN)              // Put a big building near the river

PBlockFill_XY(49,5,35)                                  // Fill all the spare space with railway track

PBlockEnd


Torture, or what?


July Addendum

I added a postprocessor so that after the tiles I listed have been plotted in the map, I can identify block edge-pieces that have an auto-fill bit set. That way, I can, for example, have a lake or sea with an auto-fill edge that then bleeds out to any adjoining empty space. That means I don`t need to explicitly fill areas. Makes the job shorter and therefore quicker. 

As I alluded to, above, I also used another couple of ordinarily mutually-exclusive bit-settings to indicate that a block could be randomly flipped in X, Y, both and/or rotated to give variety. Tiles of one texture can generally be flipped any which way and/or rotated. I have tiles that split into horizontal bands of two or three textures so they can be randomly flipped in X, though they can be flipped in Y and/or rotated under strict control too.  These can be specified explicitly in the tile blocks, or the flood-fill requests or the final auto-fill pass, since the random flipping etc can be identified by the low-level tile plot routine. That`s where structuring the code is vital to use single functions and not repeat code.

I`m now considering how to get some initial tile data into the maps automagically as I increased my current map to 128x96 tiles, which is plenty big enough for a slower-moving game, even on a 4K screen. If I use strings of  characters to represent 4x4 Paradroid--style tile blocks, I would only need 32x24 characters. With that, I could put in the roads, railways, and basic areas on the map and then add my specific arbitrary-sized tile blocks to put in the details. I wonder whether I can get an algorithm to do that?

I am going to use the tile map to generate objects. Each tile number  can generate one type of object. I have some algorithms such as a random chance of making a tree, I can put street lamps at specific intervals and hedges round the houses or fields. That may require similar-looking blocks, some that generate one thing, some another and some not at all. It all adds to the variety of a natural environment. I may be able to introduce a height-map and tune the block tints a little to get some shape to the landscape, though I am also using the overall tint for daylight effects. We`ll see.

 

Conclusion

Whether you have a map editor or generate maps with commands is all about art versus technology. Both work and if you`re an artist who can program then you`ll likely write a map editor, whereas if you`re a programmer with some graphics savvy you`ll probably want to get on with creating some maps with data. Your data starts off compressed and you already have the unpacker. If you do want to write a map editor, then be prepared to have a lot of functionality, since we programmers do like to change our minds often.


0

Add a comment

  1. Introduction

    When you`re writing games commercially, the clock is ticking. Time is money, progress needs to be made. When you`re the only person on the job, you have to do everything. What you do from minute to minute will vary but you`re doing the game design, the programming, the testing, and the graphics. The sound and music comes in later, maybe with some outside assistance. 


    C64 Paradroid with prototype graphics

    Just taking the testing aspect, mostly in my C64 days I was checking that bug-fixes were working, then looking for new bugs, and later on when the game was mainly working I`d be checking the playability. What I didn`t immediately appreciate was that I was also practising playing and accumulating experience. Collectively, that gave me a competitive advantage, but also skewed my view of how easy the game was. Levels practised as if they were the first became familiar and were made more difficult for everyone else when they were placed in their final position.  This was an unnecessary action and a mistake. 


    Experience Points and Difficulty Slopes

    Levels of the game seen later do not have to be physically tougher for the player as they are going to be unfamiliar when first seen. That puts the player at a disadvantage. That`s where more play helps the player to overcome new obstacles. It`s already tougher, there`s no need to go mad on the difficulty. Maybe just a little tougher. Home computer games don`t have to get rid of the player when their ten-penn`orth is spent, as an arcade game has to do.


    Arcade Game Continues

    I did sometimes wonder why Rainbow Islands had so many levels when a coin would only get you maybe to the big spider boss and no further. Then I remembered that there was a continue option if you pop in another coin and before you know it; you have kept the arcade machine`s appetite satisfied for an hour. The arcade machine actually doesn`t allow continues after a certain point, so it`s even meaner. I believe that is once you make it to the 3 secret islands. Those islands are big too, so kudos to anyone good enough and rich enough who completed the arcade game.


    Rock Stars

    When I had proved to myself that I could write my Asteroids tribute in C for the PC, I realised that the game reaches a point where the levels have sufficient rocks that you will be overwhelmed. The limiting factors of arcade Asteroids was having a maximum of 4 player bullets at once, and the deadly accuracy of the space buses firing. Apparently the arcade machine tops out at about level 16, with over 30 starting rocks. Must be pretty busy by then. I am still amazed that the record-breaking game took 56 hours or so, with a score of 41 million points. Presumably that was totted up by hand as the game score is only 5 digits before it wraps. I`d love to see how they were playing. Might have been keeping to low levels and preying on space buses, but we tried and failed at that.


    April 2018 Early Rock Stars, then called Astierods


    Changes

    For my tribute, I wanted to write it how I would do it. That meant applying some changes. I never liked the Hyperspace button. Firstly, it has a 1 in 6 chance of blowing you up, and seemingly it had a 2 in 6 chance of you appearing right in front of a rock, and a 2 in 6 chance of you not seeing where you have appeared until it`s too late. I decided not to have a Hyperspace button. 


    I didn`t want to be limited to 4 bullets at a time, why not let the player fire as many as they can in this modern age? I implemented auto-fire too, at a slower rate than a player should be able to manage in desperate times.  


    I thought that a shield like Asteroids Deluxe would be nice, and rather than it be restricted by time, whereby you have to wait for it to recharge, it could steal your hard-earned points. Gribbly`s and Paradroid have ways for your score to go down as well as up, so why not? The player can`t fire while the shields are up (obviously!) but you can mow the rocks down. The shields cost goes up per level, so you have to use them sparingly. It was slightly unfortunate for new players then that the shields don`t work when they have "NULL POINTS" (in French, please).


    Wraparound bullets are troubling to me. On the one hand, it`s a useful trick for the player, indeed it led to a fix of an exploit in the arcade game where players were lurking near the screen edge hoping to ambush arriving space buses by firing off the edge and back onto the other side. Space buses were allowed to do the same, and they were deadly accurate. I decided, to be fair to the space buses, no bullets would wrap across the screen edges. That allowed me to purge bullets as they get to the edge, giving the player a way to escape chasing bullets too.


    I then thought it would add to the rocky chaos if the rocks bounced off each other, rather than just cross over in the unseen Z dimension. I then had the even madder idea of doing some gravity calculations and have the rocks attract each other. That had an unforeseen consequence that converging rocks tend to break up more readily into smaller rocks as they draw together for multiple collisions. I made gravity optional as the CPU load was considerable. Later I managed to implement multi-threading, reducing the gravity load to potentially almost zero. I kept it optional as it does slightly change the way the game plays out.


    Different Screen Sizes

    Many games play with their given screen resolution and fit the game to the screen. I decided to do it the other way. The more pixels you give the game, the bigger the play area becomes. I scale the text accordingly to keep the presentation similar, and it seemed reasonable to speed things up a little bit on the bigger resolutions. Then you get a bigger play area on a bigger resolution window or screen and the games adjust accordingly. You`ll need more rocks and more time to deal with them. Space buses can see further, just as the player can.


    2021 Presentation with final fonts



    I implemented a variable that has a value of 1.0 if the screen size is my ideal of, say 1920x1080. larger screens will scale that upwards accordingly, smaller screens downwards. I tried by pixel area, but settled on the largest ratio of X or Y pixel sizes. I use that variable as a multiplier on certain max speeds such as space buses and bullets.


    The Waterline

    Rather than designing and programming against the sound of a ticking clock, what with this just being a test project, I wondered what things I needed to do to stop the game from becoming too difficult. I knew that I could not in any case plot more and more rocks. As well as looking at the screen size, I also monitor the timing of each 60th second game cycle. I have to update and plot everything within a 60th second to keep the game running smoothly. If the game cycle finishes late then in order to reduce the chances of that happening again; I reduce the number of objects I will allow to be created in the future. Some objects are just for cosmetic effects and will not be created if the system is busy. On the opposite side, if I have created more objects than expected and still come in under time, I can increase the number of objects allowed, or the waterline, as I call it. This value depends on both the CPU and graphics drawing capabilities of the machine, either could stifle the game. It takes a little while for the waterline to settle as it may not be reached in the early levels. It is saved out so that once it is figured out, it`s a better starting point for next time.


    As I got better at the game, I started to find my limit. I would reach a certain level and then, usually at the start of the level I would lose a life. I could then mop up the level and it would repeat. I could never do a level without losing one life, and maybe two, or more. I therefore decided to set the highest wave at which I would add more rocks to just below that level. The only things that would then get me would be fatigue, lack of concentration, or ever-increasing speeds of rocks, space buses and bullets, which were very finely winding ever upwards. 


    I had to balance out the highest wave for different screen resolutions. In experimenting, I did find that sometimes it stopped too early and every level was a doddle. As well as the higher resolutions having bigger play areas and more rocks so the level takes longer, they were too easy and I completely disengaged from the game. I could play it for 2 hours and not get any fun out of it. There was no challenge. That surely must be the same feeling as applying some kind of cheat mode? There`s no challenge and no fun.


    2023 4K Rock Stars with solar system background


    I managed to select a setting that gave me a real challenge where I had to concentrate highly to survive and if any space bus sounded its arrival; I would go into a major panic. That made the game very exciting to play because once you have a hold on the level you can mop up at leisure. Now the problem is that every player will get this feeling at a different wave. In order to try to identify and hold this point, I devised a system of counting lives lost against lives gained on the level. If you lose 2 or more, you have to do the same level again. Actually that usually identifies when you`ve just gone past the sweet spot.  


    I later decided that due to my experience playing the game that I would be better at it than many people, so I dropped the wave threshold by one or two and now I generally find the game too easy again. That is how it should be for everyone else. In order to make it more difficult for us all, I then limit the maximum number of lives to 10, instead of 99, and I raised the number of points required for a bonus life by around 2.5 times, plus a bit more for larger screens; as there are more rocks, and therefore points, available.


    It takes quite a few levels to get to 10 lives in the bank, and I do give bonus points again instead of it going to 11. I have had bad games where I then get dragged down to my last life before having a good spell and getting back a wadge of spare lives again.  


    Possible Options

    One could allow the players to "bank", and fix the number of start rocks at some point during their game. That would give them control. They might then be allowed to release that when they want in order to progress. What would players do with such power? It would make everyones` game unique to them, so not able to compare like-for-like scores. 


    Just because the number of start rocks becomes fixed, doesn`t mean that other thumb-screws aren`t being tightened, but they are almost unnoticeable from level to level. Space buses and their bullets are slightly speeded up, and I do increase gravity, just a little bit. The player training continues, hopefully making them sharper.


    One can already pass a parameter to the game executable in a shortcut that asks it to reduce the screen resolution from the desktop default, or indeed increase it if the graphics card can do it. The Windowed version also has options to select a reasonable 1920x1080 window and it can be resized too by dragging, which does give the player some control over how things look and play.   


    Conclusion

    The game was the most fun when it took me right to the edge and kept me there. Any tougher and I just capitulated, any easier and I disengaged. Finding that sweet spot is the tricky bit, as everyone is different, AND, a wily player could try to fake it. Then they don`t get the challenge factor and really, should I worry about them anyway? 

     



       

     

    2

    View comments

  2. Introduction

    I have been programming in C since 1995, and some would say that was leaving it late, as the language had been around since 1973-ish.I was first trained in COBOL in 1979 and learned a number of support languages too. I then switched to assembler and learned some BASIC. As PCs became part of our development kit for 8-bit and later 16-bit, I picked up some more support languages such as DOS scripting, and I was blissfully unaware of C until 1995 when along came the PSX as it was then known, and we got to see some code from the outside world. 


    C Then

    To say I was aghast at C was an understatement. Assembler can get you the most efficient way to do anything on a computer, albeit rather fiddly and unforgiving. CPU developments started to make assembler difficult to write as its parallel instruction decoding meant that consecutive instructions could get executed out of sequence. We needed protection from those dangers and a compiler provides that. "Just leave it to the compiler", said our Simon Clay. Difficult words for a perfectionist control-freak to hear.

    Although we had used many then-modern techniques in our 68000 code, such as pointers and linked lists, it takes a while to join the dots and work out that C pointers are equivalent to address registers and you get free ones that point to the start of arrays in C, and you can pass them around between functions in your C code. Then you start to learn about scope of local variables and how C discourages you from using global variables. Then you have to side-step all that C++ Object-Oriented nonsense that surely started as a university prank. I had already been introduced to that as our Dominic Robinson had implemented a 68000 OS for us to use on the Atari ST and Commodore Amiga, which included methods, inheritance and classes. I didn`t like that at all as the source code was fragmented into lots of class files and it seemed to be quite a chase to find out where the actual code was. Our implementation also had a grid of all the methods against all of the classes, which created an ever-growing 2D jump table in memory that was sure to eventually occupy the totality of the computer`s memory. Anyone else out there brave enough to do object-oriented assembler?

    I huffed and puffed and resisted as long as I could, while at the same time getting some experience writing some utilities. We had 16-bit graphics cutters that optimised graphics into sets and created source files with the sizes. When the PSX came along, we had to arrange all the graphics images into one texture sheet as tightly as possible, so I wrote a utility to do that. I was reminded of my Dad`s job as production engineer at a double-glazing firm. They had a German machine that took in window sizes and then you fed it sheets of glass and it would cut out all the windows as efficiently as it could arrange them. It`s a fun algorithm. I also remember that Steve Turner took delivery of the utility and altered it to show the textures as they were assigned a space, but backwards! It started by placing the smallest graphic in what appeared to be a random spot near the middle and then added other rectangles around it, which appeared to magically fit the space exactly.

    Likely it took me about a year to feel comfortable with C. Considering I had dived into 3 different assembly languages and written a complete game immediately, that felt like an age. I was probably sulking and mourning the passing of assembler. I never wrote any more assembler after that, but it was all useful knowledge as occasionally when your C code blows up, you can be presented with a disassembly of the crash site. It`s also useful to know what little steps the CPU takes as many of them are still present in C, so you know if you`re using any bitwise operators like AND, OR and XOR, you are still "hitting the metal". I still think about what assembler, or machine code, the C I write is creating. Keep this a secret, but I may really be writing assembler, it just look like C!

    Another Job

    3 years later and the good ship Graftgold was slammed into the rocks of administration. I had to get another job and looked around locally for something. Fortunately I found a firm writing insurance and reinsurance software applications, in C and Visual BASIC. At least one programmer there realised that games programmers need to know their stuff, and I was offered the job. I spent the next 18 years writing C, and later quality checking other people`s code, writing some delivery and installation code and then building and installing code at client sites. Before the onset of remote installation, I got to go to Singapore, Kuwait and Ireland.

    History lesson almost over, then, as I retired in 2016 and spent a while figuring out how to start developing games again. I had missed 18 years, games had moved on, hardware had gotten a thousand times faster and become almost incomprehensible, read: shader magic. I bought some DirectX books and downloaded some lovely NVIDIA demos. I was also aware that Direct X was a moving target, and before I knew it, my DX9 development kit was out of date. Got a new book on DX11 and then DX12 came out, announcing that it was quite different from DX11. While I understand the principles of what it is doing, and the maths that has to take place, I found it tough to figure out what it was actually doing and how it was doing it. I still don`t know! 

    A New Old System

    I did create a new version of our old AMP system in C. First written for our Rainbow Islands conversions, this system manages all my game elements, updates them every frame, then gets them plotted on the screen in the designated sequence, or layers. It handles animation, collisions, movement and behaviour. It`s a whole language of commands that can do anything you want, you write the language as you go along. 

    Back in the 16-bit days, the AMP language was defined using assembler macros to create data tables. 2-pass assembler lets you do forward jumps as well as backwards in code, but the language only supported 2 levels of looping and only go-tos, no calls. I put that right by having a mini-stack on each object so now I can have calls and returns. I had to test all this code using just displayed printf() output as I had no graphical interface. By setting up specific test-cases, I could go through a lot of the language primitives. Yes, I left some bugs in there that only showed themselves under higher loads. The collision system went berserk when I had a lot of objects as I had used an incorrect index at one point and objects would take each other out without colliding.

    One has to make do sometimes and solve the problems that you can solve while waiting for other solutions to become available. It`s all about figuring out how to write and test the functions that you need. I don`t like testing too much code at once as if it blows up you have to look under all the new stones. 


    SFML to the rescue

    I decided that I needed a simpler solution. As a lone developer who has to do all the graphics and find the sounds, I have no chance of competing with the big triple-A games. My mindset is still in the old games, and rather conveniently they have become retro games. With modern hardware though, we can have 16 million colours, 16-bit stereo samples at 44KHz, screens 2K, 4K, 5K pixels wide, 2GigaBytes of RAM or more, and multi-core CPUs running 64-bit code at a frantic pace. And there`s the graphics cards with all those lovely GPUs plotting pixels in parallel. 

    In order to get at all that lovely hardware, I needed some middle-ware software libraries to do the heavy lifting. While writing the 16-bit graphics drawing routines was fun, covering all the clipping options was rather tedious, and I once tried to count all the different variations we had over the years: shadow plotters, word-aligned plotters, blue plotters, black plotters, white plotters, 16-pixel wide plotters, 32-pixel-wide plotters, blitter plotters, interleaved blitter plotters, the list goes on. I had some tweeted recommendations, one of them was SFML. They had a sprite routine that talks to its shader, passing in colour tinting optionally, and it does all the clipping possibilities, does any size, can rotation or flip the image, partial transparency, scaling, and someone`s already written it.

    I wrote a sound driver interface for our last game in 1997, it was positioning the sounds across the stereo soundstage, I included some doppler shift, and I like to have a bit of random frequency variation to stop frequent sounds from becoming mechanical and annoying. Well I don`t know about the doppler shift, but SFML might do that, it does everything else.

    I do find that every time I get hold of some black box software, I have to write an interface to talk to their interface. I don`t know if my ideas are different from everyone else`s, but I can seldom get on with the requirements of interfaces. It`s not so bad, if I wanted to swap to different middleware, I could alter my interfaces and not have to touch the game code.

    SFML also looks after creating the game screen or window, since it needs to render all the graphics to that window. There`s a bit of negotiation with Windows to find out what is available, then maybe pick a resolution and let the user alter the window or screen size.

    Then you have to negotiate with the operating system for a spot to write out your user-config data. I`ve mixed up the config with the language translations to keep everything in one file. The high score table is tucked in there too. It`s not ideal but sometimes things come together organically and things get glued together when maybe they shouldn`t be, but it`s easier to load one file than three. I implemented a shield instead, that costs you points. That`s a bit painful when you hardly have any points as it burns them up and then the shield stops working, plus it doesn`t work when you start a new game with no points on the board.

    I didn`t go back to the original, nor any clones, didn`t take any measurements of speeds nor sizes, I created my version by feel. I was creating my own take, a tribute, not an attempt at repeating what has gone before, what`s the point? 


    Project One - Rock Stars

    Doing a conversion of a game is always easier than writing a new game. The design is tested already, all you have to do is copy it. I say "copy", but an arcade game has a different agenda from a home game, it has to make money, so it can have a steeper difficulty curve and wants to deliver shorter games. I never liked the hyperspace button that has a one in six chance of just blowing you up, another two in six chance of you appearing in front of a rock, and another two in six chance of not even spotting where you appeared before you get splatted.

    I decided to write some of the mechanisms to one of my favourite old games: Asteroids. It had straightforward movement algorithms. I looked at some real asteroid graphics off the Interweb. In the end though, I photographed some stones from the garden. Let`s face it, I am not going to visit the asteroid belt with my SLR camera to get some shots. Therein lies a problem. I`m not a good enough artist to draw what I need. In the old days, we only had cartoon resolution and limited colours. Now, the pixels are really small, and with so many colours, the only sources of graphics are photography or creating things in 3D modelling packages. Photography is easier.






    I can`t release a tribute to someone else`s game. I did send an early version off to Asteroids Central, but they weren`t interested in publishing it. I`ve been refining it ever since, but just for the sake of my OCDs.


    Project Two - Monster Molecules

    Still with an issue that sourcing graphics is limited to what I can photograph myself, I have to concentrate on graphical effects rather than actual graphics. I put on my trigonometry hat and started playing with 3D orbits. First I thought of a solar system, and having always been interested in astronomy, I wrote a simulation of our solar system and liked the result so much that I fitted it to run as a background in my Asteroids tribute: Rock Stars.

    I then switched to chemistry and finally to the physics of atoms. I started to create the atoms and molecules of some simple compounds. Chemistry and physics lessons back in school were full of certainty. Everything was: this is how it is. Quite a lot of it turned out only to be latest theory and a lot of that has changed now. I decided then to design everything based on how it was taught to me. I looked up the burning colours of many elements of the periodic table and got a bit more involved in the layout and angles of molecules than I was intending. I managed to come up with some nice flame-type effects and kept stirring the metaphorical melting pot.  

    The ultimate aim is to use the mechanisms that I have to create something that I can release as an original work. So I still have a space-ship that can disappear off one side of the screen and appear on another. It has a big dose of inertia, which is good for making strategic withdrawals at times. I wrote a gravity routine that allows all the game objects to accelerate towards each other in accordance with their mass. I came up with 8 different types of space bus and two levels of mean-ness. Where would we be without meanies riding space buses and being mean? I kept mostly to the first 30 elements in the periodic table as they get quite big by that time. I added a couple more guest elements to give me some alloys that I needed.

    I also figured there was a teaching moment here and added optional information tags to tell us what the atoms, molecules and bits of nucleus fragments are. Might get an education grant out of this, right?

    The game doesn`t scale the picture to fit your screen, or window, it actually plays a bigger game on  bigger screen. It has a maximum number of molecules it will run per level, based on the screen size. It caps at a manageable level, as I found it frustrating at a certain point where there is so much on screen that you`re bound to crash into something. Whilst you do have a shield that you can use at any time, it costs you points off your score. So it gets as busy as it does, but is still quietly turning the thumb-screws in there by winding up the speeds a tiny bit.






    The game keeps turning those thumb-screws all the way to the magical level of 988, which might well take more than a day of play to reach. I was just thinking that the record game of Asteroids took over 56 hours of play, and they had to avoid all the bugs from untested consequences. I`d love to know how they played. If I could play the original for 10 minutes I`d be happy.


    Mostly Finished

    That`s a little taster of where I'm at. I have to check that all the sounds are indeed free to use. I spent a long time finding fonts that are free to use. Many are not licensed for commercial use, which seems kind of short-sighted. Get your work out there. I`ve designed a graphics character set for another project. I don`t have a TTF editor and therefore I don`t know how fiddly it will be to design my own font.


    Conclusion

    I am now working on Project 3, which includes a tiled scrolling background. The core AMP library is common to all my projects and while I try to avoid making breaking changes, development continues to add features for game 3. Any optimisations might well cause me to recompile everything, as happens when the development kit gets an upgrade.   


     

    2

    View comments

  3. Introduction

    Recently I have been writing some routines for a PC game that is 2D and needs some backgrounds on which to play. I had only done some space games on PC before, which cunningly only need a starfield backdrop. I used a photo I took of the Milky Way on a clear night from my garden. 


    PC Monster Molecules

    I knew that the time would come when I would need some nice scrolling backgrounds. I even wrote a routine to draw a background from arbitrary sized squares and cunningly used my starfield photo as the source squares, or tiles. I incorporated code to flip tiles in X or Y, and give them individual palettes, everything I could think of that the retro arcade machines could do. Everything I will need to write retro games.


    Computer Hardware

    Some 8-bit computers had character tile modes. That is where the screen is built from a small number of 8-bit character codes, and the video chip looks up the data to draw the characters. This may be just an alphabet, some punctuation and some rudimentary generic graphics. Forward-thinking computer designers allowed the software to redefine the character set with all user-defined graphics, sometimes in multi-colour mode so that artistic things could be done.  

    C64 games were often done with user-defined graphic "tiles", You get up to 256 of them and can use split-screen techniques to change tile sets for separate score panels. The actual screen can be up to 40 tiles wide by 25 high, which means the screen size can use 1,000 tile characters, which is slightly more than the CPU could update in a 50th of a second, which is why most scrolling games have a smaller play area than the full screen. 


    Game Maps

    Any scrolling game will have to have a larger map of tiles that are used to create the play area. Games that only scroll in one direction can potentially unpack the map as they go along. These type of games, such as Scramble or R-Type, tend to move slowly along to reveal more new scenery. I tended to prefer a free world where you can go where you want, scrolling freely in 2 or 4 directions. That tends to mean that the whole play area map is unpacked and laid out in memory, ready to update the screen. Now these maps tend to be quite large. Since one screen is 1,000 bytes big, then a play area of tiles might easily be 16 times larger, that only gives you 4 screens wide and 4 screens high. 16,000 bytes is a quarter of your C64 used up. A game will also have a number of levels, shall we say 16? Suddenly that is 4 C64s`-worth of space used up if we didn`t somehow compress the data. In our single load games we need to be able to store all the compressed data for those 16 levels as well as having a 16K buffer for for one map, and all the game code, and all the graphics for the tiles, and the sprites.

    We need to be able to efficiently compress all the level data, and additionally we need to be able to design and specify all of the game maps in the first place. Back in the day, I designed all my own graphics for fonts, sprites and backgrounds. As a programmer, I might well have my own ways of designing the level data that a more modern graphics artist would not tolerate. 

    A game designer has to decide how to create the game maps and how to compress them. We used to meet other programmers from time to time and some of them would proudly tell me about their tile mapping utility they had written to create their game maps. Sometimes they might even leave them in the final game. I would always ask them 2 questions. Firstly, how long did it take them to write the mapper, and secondly, will they use the same mapper for another game? They might well answer "6 months" to the first question, noting that their utility was likely customised for their own game and not necessarily generic enough to sell to anyone else, which answers the second question: "No, it might not be used again." Knowing that I could write a whole game, draw all the graphics and create all the map data in 5 months or less, I was reticent to even suggest to the boss that I write a map editor, with no experience.


    Examining Past Efforts 

    As all C64 programmers know, the VIC-II graphics chip with its tile mapping capabilities can perform a number of tricks that can`t be done as easily on a bitmap screen. One can alter a single tile image and every instance of that tile on the screen gets updated for free on the next 50th of a second display frame. With that, we can animate characters, do parallax layers, make things dissolve and reappear, or move objects across the background. Tiles aren`t just for decorating the bathroom, these are alive. Programmers therefore need to reserve some tiles for graphics tricks, and it can also be wise to have control over what tile goes where in the set of tiles so that collision detections and orderly transformations can take place, such as blowing up ships on the runway. Duplicate graphics can also be used as secret markers to control game elements, such as Gribblets reserving their new landing spot as they take off to avoid collisions, and knowing when they are in the home cave. Rules are created that don`t interest graphics artists, and no-one wants to have to rearrange all the tiles after the maps are drawn. It wouldn`t surprise me if all mappers have a function to swap tiles around in the tiile sets when the programmer has another "great idea". 


    Gribbly`s Day Out

    My first game with a tiled background, had me falling into line with other C64 game designers and arcade games of the time. I wanted an organic look to the scenery, which meant trying to hide the square edges of the tiles. I was using sprite to background hardware collision detection for pixel-perfect collisions, which is almost free in terms of CPU time but is also painfully perfect. I had to then use the colours carefully since only two colours of the background caused collisions with sprites. 

    In order to build the background maps, which had to fit into 256 characters wide by 64 high, I did some preparatory work. The edges of the buffer were filled in with side walls and a top ceiling, and then some base characters of the floor. I over-simplified the scrolling and co-ordinate system to lock Gribbly in the middle of the screen rather than locking the scroll near the edges and let him approach them. That wasted some space, sorry! I then applied a block of tiles all over the map that contained the triangular energy barriers and the control buttons. This initialises all of the buffer to the blank sky character. I believe that left a couple of uninitialised character lines at the bottom. Most of the maps would then need a nice piece of land to bounce around on, and maybe some water. 


    To set up the bottom of the map, I then designed some blocks of tiles of arbitrary sizes and assigned them unique numbers and input assembler declarations of bytes for the width of the block and then for each column a height byte followed by the tile characters in the column. The blocks might only be 1 tile high and a a few tiles wide for a stretch of grass, or be a bit of lake with a large rock formation sticking up. I would have an address lookup table to get from the block number needed to the actual block data. There would then be a simple list of block numbers to fill the width of the map from left to right. 


    C64 Gribbly`s Second Day Out


    At this design point, I realised that there would be a lot of blocks for level land to move about on, some blocks to bolt to the sides of the map, and I would need some craggy rock formations that provided the underside of higher platforms. Following the setup of the bottom pieces, the list became busier as I then had to provide positions for all of the remaining scenery blocks. I tried to make the major rock lumps quite big, and fit together at the sides so as to reduce the number of blocks needed. The list was then made up of groups of an X position, a Y position and a block number. All of those fit in a byte each, keeping the data compact and bijou.

    Next I realised that some of the scenery would cut across energy barriers in the sky, and also I would like to switch off or remove some energy barriers to make flying routes, and maybe also remove some buttons so that I could create locked areas, barriers both on and off. There were tile blocks to switch barriers off in the 3 faces of the triangles in one go. I hope I was smart enough to remove barriers and then plot in the rocks afterwards. It was easy to swap the sequence of the blocks by cut-and-pasting the list of blocks. Later blocks tended to get smaller as I might want to patch in small corrections.

    Levels tended to take about a day each to enter, test and adjust. The tougher screens took longer because I had to practise getting through some of the tight gaps. If they were too tough then I might have to move a lot of scenery so I did build them up in stages and test them a bit at a time. I could correct small mistakes and adjust the difficulty at the same time.


    Paradroid

    I reduced the background tile maps to 160 displayed characters wide by 64 high. This game though was set on a space ship and viewed from top down. Straight lines were the order of the day. I figured I could build the decks of the ship from fixed size blocks and therefore they would all fit together. Tile blocks of 4 by 4 characters were created, I ended up with 32 of them. I already had it in mind to have a miniature deck display where the 4x4 tiles would be represented by just one character each.


    C64 Heavy Metal Paradroid


    I was still entering the map data into assembler source files and chose to pack the map data down some more by using run-length encoding. I may have used the remaining 3 bits of the tile codes as a count of 1 to 8, which suited horizontal walls and corridors nicely. So the maps were just a list of bytes containing a tile code and a number of them. I didn`t bother with a second list of alterations as it likely wouldn`t have saved much. It was much easier to set up the maps. I had them all drawn out on graph paper as all the lift shafts needed to fit together properly to feel like a real place. It was then a case of counting the blocks and typing them in. Naturally there were frequent errors and miscounts as the decks would sheer somewhere and I would try to patch the data and thus be able to enter multiple corrections in the source code together in one go.

    I can`t remember exactly, but I would have had the whole ship entered in about a week and didn`t alter it much, if anything, as the ship design provided encounter areas of various sizes and was mostly non-negotiable in shape. I may have added extra energisers later, but that was about it.


    Uridium

    Uridium came along next and with shadows being cast from tall obstacles, the 4x4 block idea was going to be limiting, so I resurrected the Gribbly`s system. I created tile blocks of the height of the screen and filled the smaller map buffer, now about 19 characters high and 256 wide still, from left to right. This created the basic dreadnaught platforms with nothing on them. Then I was able to use the secondary Gribbly`s system of adding arbitrary-shaped tile blocks onto the dreadnaught platforms. I could do two levels in a day as this was a straightforward process and the difficulty of playing the levels ultimately decided their sequence in the game. While i may have had an idea to make a difficult level or an easy one, I could later compare them and swap the data lists about. 


    C64 Uridium


    The new level being worked on was always tested as level one. What`s the point of playing all the way through to level 11 before you get to test the new data? I would let my test team lads have a play and they would let me know if something was too difficult, or too easy!


    Alleykat

    I always like to change things from game to game because, to me, being accused of re-hashing the old stuff would be a horror. Also, one does tend to expend a lot of ideas in a game so it`s best to reset with something new. There might be some ideas that couldn`t go into the previous game. I went for a vertical scrolling game instead here, so I reconfigured the map buffer to be 40 characters wide and 256 high, which would have saved me some valuable bytes for extra graphics sets. There were 8 different sets of tiles in there.

    I suspect that I was getting tired of entering all of the map data because I decided to write the routines to generate the race tracks from a set of parameters and some predefined shapes. I had background blocks that were up to 5 character tiles wide and 3 high, and all the 8 graphics sets were interchangeable. The tracks were then defined by how far apart the rows were and what the density of big pieces was. That also decided how many gaps there might be to drive through, how many energy blocks there might be to pick up, and the collapsibility of the pieces. 

    The map algorithm cleared the buffer to the blank ground piece, then randomly set up the lines of scenery such that as it filled the lines from left to right, it would exactly fill the row with pieces, nothing cut off mid-way. Then it would sprinkle a couple of different ground tiles in the gaps between, along with energy pickups. All I then had to do was select some eye-catching colour schemes. 


    C64 Alleykat


    Maps then were still made from some predefined blocks of tiles, but the random number generator decided on the final layouts.   

    Morpheus next, which had no tile maps.


    Intensity

    This game had 70 different single-screen levels, all made from tile map lists. I chose Uridium for the graphics style and the tile block methods, but added a new type of tile block, the 3-by-3 expandable rectangle. I realised that I could use 9 tiles, with the option of a ring mode with a missing middle. Now I could draw any size rectangle, right down to a 2-by-2, with a string of 5 bytes for the position and size of a rectangle and a last number for the block layout, of which there might not have been many. Likely at this time then I would also have used a simple repeat of a single block either horizontally, vertically or even both. 


    C64 Intensity


    Platforms could be laid out roughly with the rectangles and then decorated with details using the usual X, Y position and block number. I did then generate a height map from the tile codes and then from the height map I set up a desired altitude map around the high points. That process involved effectively plotting a tile block of heights onto the altitude map but assigning the highest value of of the tile and what s already on the map. 


    Rainbow Islands

    In 1989, when we worked on Rainbow Islands, we didn`t know how the level maps were laid out in the arcade machine ROM. The designers of Rainbows Islands at Taito might have had a map editor to create the backgrounds, this seems likely, and they might have been stored raw on big ROM chips on the arcade board. More likely they would at least have been compressed with a generic compressor and stored packed and unseeable. So even if we had the kit to go looking at the ROMs, we might not have found anything useful as compressed data requires the decompressor software. 

    We chose to video the whole game. John Cumming: our programmer and graphics artist, set about writing a map editor using STOS, a BASIC platform with graphics additions, for the Atari ST. This took him a couple of weeks. He then had to sit there with a VCR and our video of David O`Connor, our best arcade player by far, playing through what we thought was the whole game of 7 islands. John would pause the tape and recreate the background. We did get some early level graphics sheets from Taito with the tile sets on. We still had to map them to less colours as the arcade machine could display more colours than the ST. John spent another couple of weeks generating 28 level maps of ever-increasing size.



    Enhanced Rainbow Islands for PC/PSX/Sega Saturn


    We then hit the problem of how we were going to compress 28 levels of data that was up to 256 tiles high by 40 wide, an estimated 450K of data that needed to fit 4 levels at a time in the target machines. These were 8x8 pixel tiles, which now seems a bit unusual given that the arcade machine had a 16-bit CPU in it, but quite possibly the graphics chip was still configured for 8-bits. We followed suit on our 16-bit platforms, knowing that we also had to produce 3 8-bit versions too. It was at this point that David observed that the graphics for the backgrounds were often built from 4 tiles in squares. These would repeat all over the place. We therefore set about writing our mega-compressor that would consider the 4 level maps of each island, looking firstly for adjacent pairs of the same characters, and substituting in a single macro code for the pair. It could then look vertically for pairs of tile codes or indeed new macro codes that occur more than 3 times. It would then substitute another macro, this time a vertical one. Then we might do another horizontal pass looking for patterns 4 characters apart. Then again with vertical. We could do up to 8 passes. 

    The end result would be a table of macro pairs and spartan maps with macro codes and usually big gaps between them. We could then pack the maps down and record the lengths of the gaps as one code instead of all the blanks. On the one hand this produced excellent packing results, on the other hand it did take the Atari ST an hour of huffing and puffing to produce the data. We then had to write the decompressor code as well that sits in the game code to this day. Unpacking is pretty fast as it only has to go through a level once to put the gaps back in (starting at the end of the data), and then 8 times to unpack the macros per pass in either horizontal or vertical mode. Packing had to pass through 4 levels of island data for every macro it created, looking for every instance. 


    Paradroid 90

    I first tinkered about with 32x32 pixel tiles in 4 colours and had a 4-directional scrolling demo on the ST. The sprites were 16-colour but we thought that the backgrounds didn`t look 16-bit enough. There were some nice colourful games coming out and we`d have been shunned with only a 4-colour background. We were wedged firmly between a rock and a hard place, because avoiding horizonal scrolling got us grief from the Amiga crowd.  


    Amiga Paradroid 90


    We decided, since we were supporting the Atari ST and Commodore Amiga, that we would continue with 8x8 graphics in the same manner as Rainbow Islands. Paradroid had used 4x4 tile blocks that would suit our mega-compressor still, but now we had a mapper program and while I designed the ship layouts, we could let the graphics artists do the decorating of the levels without having to stick to the strict 4x4 tile blocks. We expanded the mapper program to support up to 16 maps for compression so that we loaded in one ship at a time. The downside of all this efficiency was that the mega compressor took 90 minutes to pack and save the map data for a 16-deck ship.

    No new tile mapping techniques this time, just a lot more data.


    Fire & Ice and Uridium 2

    We finally went 16-bit proper and optimised our game systems for 16x16 pixel tiles. I believe we had a new tile mapper program, likely on the PC, written in DOS, being pre-Windows, and certainly in 16-bit. 

    The sort of feature that you need in a mapper is to pick up a block of tiles and then plant it in multiple places, which is not so dissimilar from using instructions to plant blocks of tiles. When one uses a mapper program, inevitably a generic compressor gets used, which means you need to know how to decompress the data and have that in your game code. The latter is not necessarily available unless you write both halves yourself, or you know someone who has. We knew Factor 5, thanks lads.


    a
    Amiga  AGA and CD32 Fire & Ice

    The maps for these games were created mostly by the graphics artists and were sufficiently detailed and varied that a generic compressor would have as good a chance as any to pack the data down well. Fire and Ice level maps were typically 8 to 10K before compression with up to 5 per land. Compressed maps were combined with land-specific graphics, sound samples, music and meanie control data and might run to a total of around 70K.


    Amiga Uridium 2

    The Present

    Fast-forward 30 years and I find myself needing a tiled playfield for a game on PC, I`ve done a space game and now I need a top-down background that scrolls. I have the software to plot a background with tiles of any size I choose, and with typical screens being 1920x1080 pixels, I need a lot of background data. I tried my setup on a 4K monitor and my 64x48 tile map of 64x64 pixel tiles barely scrolled at all before running out of data. 

    Wrestling with the enormity of the task of writing a tile mapper program, I still resist and spent a few hours writing all of the code to do what my entire 8-bit catalogue did for tile mapping. Ultimately, it came down to a loop within a loop within a loop within a loop. I can define 4x4 tile blocks and specify blocks in the Paradroid style or arbitrary blocks of tiles to be plotted consecutively or by specified positions, with or without repetition. 


    PC Project 3 (Name undecided)

    I then realised that I can go further with more functionality such as a flood fill, which is great for filling in the gaps in debug mode before the map is complete. Further than that, functionality is there to maybe swap in alternate tiles in areas for graphical variety rather than obliging a graphics artist, or me, to spray alternate graphics randomly. I can also flip tiles in X and/or Y and apply a 90 degree rotate, so a randomiser of solid textures might be able to arbitrarily flip certain blocks for more variety.

    Here`s a teeny bit of C code that generates my test map:

    long Wave1BlockList[] = {

    PBlock1x1_XY(1,1,UB_Small_Tower) // Single blocks are directly specified

    PBlock1x1_RXY(4,5, 34) // Including repeated ones

    PBlock1x1(UB_Small_Tower)                               // Place another tile after the last block 

    PBlock4x4_XY(0, 6, Crossroads_4x4)                      // Paradroid-style crossroads

    PBlockNxN_RXY(12,1, RoadHorizontal_NxN)                 // Add a road to the right

    PBlock4x4(Crossroads_4x4)                               // Add another crossroads

    PBlockNxN_RXY(12,1, RoadHorizontal_NxN)                 // Then some more road

    PBlockNxNVertical_XY_RXY(0,10, 1,12, RoadVertical_NxN)  // Add a vertical road frpm the 1st cross  

    PBlockNxN_XY(14,7, ZebraVertical_1x2)                   // Put in a zebra crossing

    PBlockNxN_XY(1,14, ZebraHorizontal_2x1)                 // and another zebra

    PBlockNxN_XY_RXY(0,17, 1,2, LevelCrossingHorizontal_4x1)  // Add a train track across the road

    PBlockNxN_XY(23,6, LevelCrossingVertical_1x4)           // Add another train track over a road

    PBlock1x1_XY_RXY(12,0, 20, 6, UB_Earth)                 // Create a muddy patch of forest

    PBlock4x4_XY(4,10, HedgedBuildingTL_4x4)                // Create a small hedged plot

    PBlockNxN_XY_RXY(0,36, 64,1, EastWestRiver_1x5)         // Make a long river

    PBlockNxN_XY_RXY(0,41, 64,1, SouthCoast_1x7)            // Make a beach and some sea

    PBlockNxN_XY(27,23, HedgedBigBuilding_NxN)              // Put a big building near the river

    PBlockFill_XY(49,5,35)                                  // Fill all the spare space with railway track

    PBlockEnd


    Torture, or what?


    July Addendum

    I added a postprocessor so that after the tiles I listed have been plotted in the map, I can identify block edge-pieces that have an auto-fill bit set. That way, I can, for example, have a lake or sea with an auto-fill edge that then bleeds out to any adjoining empty space. That means I don`t need to explicitly fill areas. Makes the job shorter and therefore quicker. 

    As I alluded to, above, I also used another couple of ordinarily mutually-exclusive bit-settings to indicate that a block could be randomly flipped in X, Y, both and/or rotated to give variety. Tiles of one texture can generally be flipped any which way and/or rotated. I have tiles that split into horizontal bands of two or three textures so they can be randomly flipped in X, though they can be flipped in Y and/or rotated under strict control too.  These can be specified explicitly in the tile blocks, or the flood-fill requests or the final auto-fill pass, since the random flipping etc can be identified by the low-level tile plot routine. That`s where structuring the code is vital to use single functions and not repeat code.

    I`m now considering how to get some initial tile data into the maps automagically as I increased my current map to 128x96 tiles, which is plenty big enough for a slower-moving game, even on a 4K screen. If I use strings of  characters to represent 4x4 Paradroid--style tile blocks, I would only need 32x24 characters. With that, I could put in the roads, railways, and basic areas on the map and then add my specific arbitrary-sized tile blocks to put in the details. I wonder whether I can get an algorithm to do that?

    I am going to use the tile map to generate objects. Each tile number  can generate one type of object. I have some algorithms such as a random chance of making a tree, I can put street lamps at specific intervals and hedges round the houses or fields. That may require similar-looking blocks, some that generate one thing, some another and some not at all. It all adds to the variety of a natural environment. I may be able to introduce a height-map and tune the block tints a little to get some shape to the landscape, though I am also using the overall tint for daylight effects. We`ll see.

     

    Conclusion

    Whether you have a map editor or generate maps with commands is all about art versus technology. Both work and if you`re an artist who can program then you`ll likely write a map editor, whereas if you`re a programmer with some graphics savvy you`ll probably want to get on with creating some maps with data. Your data starts off compressed and you already have the unpacker. If you do want to write a map editor, then be prepared to have a lot of functionality, since we programmers do like to change our minds often.


    0

    Add a comment

  4. Introduction

    Over the years I went from writing simple games on a mainframe to 8-bit home computers, then 16-bit computers, took a break to work on business applications, and now returned to PCs writing 32 and then 64-bit games (spoiler alert: 64-bits made little different to me). Over that time I developed a way of writing a game engine that meant getting a new game written should be quicker, prototyping new game elements is faster, and even whole small games could be created in a matter of weeks.


    The Start

    My first games were written in COBOL The construction of those was that the game built up a display screen and then sent all that information to the terminal I was sitting at, where I could study the screen for as long as I wanted, effectively in a paused state. I would then fill in my next moves or actions and hit ENTER; at which point the game would enact my instructions, update the game by moving and actioning all the game elements and build the next screen display. 

    Every game I wrote, I started from scratch writing completely custom code. I was learning how to do different games. The first was Space Chase, which bore some resemblance to the Star Trek game they had on the multi-million pound mainframe. Then I wrote a top-down maze game, possibly inspired by Pac-Man. Then I did another Space Game, but with planets and resources, followed by a 10-level multi-player dungeon-style last-man-standing combat game. I even managed a real-time Space Invaders clone when we got the technology.

    COBOL Dalek Hunt (1980)

    Jackson Structured Programming

    I was not immediately looking for patterns within the games I had done. The game code was quite small, quick to write, and did exactly what I needed at the time. It was good experience. When I moved to 8-bit, I was converting Steve`s ZX Spectrum games to the Dragon 32. He was writing in hex machine-code, so I saw no assembler. He drew me a Jackson structured diagram of his game code and I worked from that. That`s a more ordered way of coding than a flow-chart. There are just 3 constructs: an iteration, which is a loop. That might be a for loop, a while, or a do, in C. Then you have a sequence, which is just two or more processes done one after another, and finally a decision box, which is an if statement. The diagram goes downwards into as much detail as you need. It`s actually pretty straightforward but has some quirks in implementation of certain processes, along with some strict rules. Firstly: GOTOs are evil, and secondly, flags are bad. COBOL and C do support GOTOs and they generally cause chaos as you can go anywhere in the function, anytime, which can be abused. Flags can cause chaos as you can set or clear them anywhere, anytime. Jackson encourages us to just look at the actual data to make decisions as flags are arbitrary and we can forget to reset them. I do have a small collection of them for configuration settings but I don`t generally write code with flags any more, it was beaten out of me. That can lead to some hefty nested if statements, mind! The philosophy is to make a decision based on your data and arrange the code so that you don`t need to ask the question ever again. Condensing it into a flag is cheating, just arrange the code so you don`t need to ask again. 

    One of the quirks of Jackson was the idea that if you`re validating a load of data, say a screen full of values on a website, they way they told us to do it was to duplicate all the tests and you stay on the positive side if the tests pass, but if one test fails, you cross over to the (Dark) side, with a GOTO, no less, so that at the end of the tests the positive side can continue with saving the data, whereas the negative side can get on with dispatching the error messages. That was a nightmare on one system I wrote as the screen was packed with tens of fields and you don`t want to have two lists of al that validation. I would just count the errors as I go along and if it`s zero at the end; you`re good to go. I digressed, again!, sorry. Ben waiting about 42 years to vent that. Don`t get me started on program inversion! Actually  may need to do a piece on that since my game engine actually does inversion, allowing me to write meanie algorithms from the meanies` point of view. 

    I was writing 6809 code from scratch then. I just had the game design, a diagram, and the original author at the next desk, the latter being the most useful; of course. The design just calls for 1 player, 1 shot at a time, 24 identical enemy ships and a few enemy shots. There is a refuelling ship too. It`s simple enough that every element type has its own calls to deal with them. 

    Dragon 32 3D Space Wars (1983)

    At the end of 3D Space Wars, I backed up the final version of the code, printed it off, and added comments in pencil. The code was all edited and assembled in small chunks as 32K of RAM isn`t enough to hold even a quarter of the source code. I had to write it in separate modules, each with entry points at the top to give a stable interface rather like a DLL (Dynamic Link Library). Mostly they would be high level calls, only once per game frame, so not too terrible. I then stripped out all of the game-specific code to leave me with a shell of common code to begin the next game with a head start. In this case it really was very hollowed out as I might have some maths routines, the graphics plot routine, there was just one, and the sound routine, all very low-level stuff.  I do remember there was a Shell-Metzner Z sort routine to ensure that the distant space ships were plotted before the closer ones. That`s one of the items we carried forwards for every game, though we used a different sort strategy on 16-bit. 

    After 3 Dragon 32 games, I switched to the Commodore 64. That meant throwing away my entire 6809 CPU code-base and starting again from scratch. Since my first game was going to be a re-conversion of Lunattack! and this time I had some source code to work from, I could work initially at the function level and rewrite the functions that I might need again, and discard the ones I wouldn`t need again. Being a game with a hi-res screen rather than character mode, I needed the trusty plot routine. The sound routine from the Dragon 32 was thrown away as the C64 has a proper sound generator chip.  

    Commodore 64 Lunattack! (1984)


    I started to formulate a way to best use the C64`s CPU. It`s a good plan to put your variables in the first 256 bytes, the zero page. That`s where the OS puts its variables, so make sure you take over everything before using that space, and don`t expect any ROM calls to work after that. There are CPU assembler instructions that are shorter and faster to access bytes in zero page. It makes sense to put all your game variables there, but likely you won`t have enough space for arrays of all the game elements. Typically a game element such as an enemy ship might have X & Y positions, speeds, animation variables, type, mode, maybe 20 bytes. What I used to do was keep the array of element data elsewhere and copy each one`s variables to zero page, do the update routines and then copy the variables from zero page back to storage. I figured the space-saving for every access was worth it, and the speed benefit was a bonus. Can`t remember if I unravelled that loop, but I should have.

    Swapping editor files was quite a burden back then as you could only edit one file at a time. There was no quick peek at the variable names and locations file. They were all written down on a paper list. Since there wasn`t really even RAM enough for code comments, the paper list had a lot more details. In assembler, like COBOL, there was no real concept of local variables, everything is global, just to heighten the peril. 

    At the point of starting Gribbly`s Day Out, I switched into use-the-C64-hardware mode and plotting sequence is decided by sprite numbers. Sprite 0 is always in front of sprite 1, etc. Usually you assign the player to the top sprite, player bullets next and then the meanies. It`s decided in advance, anyway, and not needed to be sorted at run-time. I now had four element types: player, player bullets, Gribblets (frendly elements) and meanies. RAM space was still a bit of a premium so I wasn`t yet thinking about a generic element format that covered everything, there would be too much wasted space, and of course copying more to and from zero page might make it less worthwhile.

    Commodore 64 Gribbly`s Day Out (1984)

    Paradroid did reuse a bit more code. I was trying to reuse more each time. The games had similar mechanisms of running 16 meanies on a level but allowing only 5 or 6 on screen at once. I did then try to optimise games to focus on what is on the screen and don`t run things that can`t be seen and won`t be seen. Uridium had to run at 50 frames per second. The attack formations kept the objects together.  

    When the sprite multi-plexor got developed to get more that 8 hardware sprites on screen at once then the number of game objects went up to 32, which was more of a CPU burden, and whilst many games proved they could scroll slowly, building up the next screen over multiple frames, I tried some different ways to move about within the game world.


    16-Bit Times

    Right from the start of the 16-bit adventure, we went from 64K RAM maximum to 512K RAM minimum. Dominic Robinson was already looking at the best use of the CPU registers as we went from 4 of them to 16. The 8 address registers were clearly more than we needed for any single routine. It made sense to start assigning some to common causes and some as scratch registers. We decided that a0 and a1 would be scratch registers free to use and pass parameters. All the other address registers would have specific uses and any deviation would require them to be pushed onto the stack first. We decided to use a6 for pointing at the hardware chips, no other use, which allowed main and interrupt code to know that they can always use short addressing modes based off of a6: smaller and faster. I designated a2 as the register that always points to the element that I am working on in my game engine, the Alien Manoeuvre Program (AMP) system. I keep calling them elements rather than objects, since I don`t want anyone to think I`m using object-oriented programming, which I definitely am not. I could also call them AMPs, or Alien Manoeuvre Programs.

    The idea of AMPs came from John Cumming`s Soldier of Fortune C64 game, or at least I thought it did. He could program sequences of instructions with parameters that could tell each game element what to do. These things have to be broken down to a single frame (or 1/50 of a second worth of processing, so one move of the object. Traditionally this might mean that you have one or more mode bytes that tell you what the element is doing more broadly so they can pick up where they left off. AMPs resume their processing straight away. When I spoke to John about this revolutionary idea, he said he got the idea based on what Uridium was doing to fly the formations of space ships! Without him taking my idea much further, we would not have invented the 16-bit AMP system at all.  

    Having decided that all of my "elements" have quite a large number of common fields, and I can afford the space to have a few specific fields and no-one is going to mind, I then began to design an AMP data structure and a game engine. Previously, that job was copying each element`s data to zero page, updating it and then copying the data back out. 68000 doesn`t have a zero page as such, but it does have short addressing modes that allow you to point an address register at the beginning of the structure and then refer to individual data items in the structure by offset from the start. 

    Rainbow Islands was the first game to use this system. I already knew that the sort of things we are doing all the time are that we move the elements in the game world space, decide what activity we're doing and continue, move, do some checks on the background, calculate the screen co-ordinates based on the world co-ordinates and the scroll position, then plot the object on the screen. There`s a bit of collision detection going on too, of course.

    Amiga Rainbow Islands (1989)


    There are also some wider issues such as that we need to usually move the player first, then set the scroll position, as a camera on the player needs t update before we can then calculate the screen positions of all of the other elements correctly. I haven`t quantified in my mind whether it would be noticeable if the sequence is not followed. Maybe at 50 or  60 frames per second it wouldn`t make a lot of difference. It`s best if you understand the dependencies in the game so you can work things out in the correct order though. That`s exactly the type of thing that keeps me awake at night.

    It made sense to invent a set of plot layers so that as you process the elements, you put each one in one of the layer lists so that when all they are all updated and moved, they can be plotted onto the screen in the sequence you want them, one list at a time, from back to front. That ensures that spiders go on top of webs for example. This process eliminates the need for a sort process on ever-more elements, once again. Sorts are to be avoided whenever possible. 

    I implemented Michael Sentinella`s clever collision method as it is very generic and fast. I haven`t heard from him yet so am not comfortable saying too much about it. Suffice to say that collision checking gets more time-expensive the more different collisions you want to check for. Every player bullet against every meanie ship for example. Then every meanie and meanie bullet against every player. The collision functions are built into the AMP game engine as it has to do a couple of passes through the objects, which is lucky because the AMP game engine already has to do a couple of passes through all the objects, one as it does all the movement and other updates, the second when it goes through all the plot layers to plot all the "sprites". If you have a loop already going through all the objects then hook in as much processing as you can rather than writing another loop and going through them all again.    


    Paradroid 90

    When I got onto Paradroid 90, and we had no shared library of game engine code back then, I was able to make some improvements to the game system. Some common functions were always done in pairs so it made sense to combine them into one. Later I built them into the AMP game engine to make it faster still. Back to the plot sequence of items then, I had two-part robots where the head was a separate graphic, and I had shadows being "cast", faked, obviously. The drawing sequence of the objects becomes vital, and you can`t always make sure that objects are in the game elements list in the same sequence you want them drawn. Indeed, some objects can move between layers and we don`t want to keep rearranging the master list. We use linked lists and it was usual that if, say, a robot is created and it wants a head then it typically creates a head element right after itself, which is convenient for it getting the latest updated position of the body, same with the shadow, do it after the robot, not before, but it will be plotted before the robot. So inheriting position, direction, or colour is best if you get the latest information. But when it comes to plotting, you want control over which get done first. You don`t even get any flickering of "sprites" on the same layer because they will be in the master list always in the same sequence and get added to the PlotList in a consistent order, and therefore drawn in sequence.

    There are two released versions Paradroid 90 for the Amiga. The original release sat on my development kit for a while before Fire & Ice started and I was doing a few tweaks as the Amiga was armed with a blitter to speed up some plot routines. I was experimenting with some pixel effects in the explosions and wondered if I could make them into a weapon. I changed the weapons of a couple of the robots, certainly the 821, and that later version made it into the A1200 bundle pack. Whilst we didn`t make an AGA version, a lot of games had trouble working on the A1200. We were fairly disciplined in obeying rules of the hardware and didn`t slip up on the new AGA Amiga.  

    Amiga Paradroid 90 (1990)


    Not as much changed in the AMP game engine for Fire & Ice. I did have to invent a new bunch of functions to run a side-on game instead of top-down. Negotiating the sloping characters involved more physics than I was hoping for. The player control mode also had acceleration and deceleration values for each land, plus gravity. That allowed me to make the ice devilishly slippery (sorry!) and the undersea stages all floaty. Renegade weren`t happy with the coyote movement and I took the game to London to show to Julian Rignall. He thought the controls were a bit slow too, so I agreed to speed everything up. That, in turn required more gravity to make jumping faster and that messed up every other meanie in the game so far and I had to re-tune them all. The runaway mine-cart set-piece was all set to work and make jumps and suddenly it couldn`t. Pro-tip: get the control mode right first!

    AGA Amiga Fire & Ice (1994)


    The game engine got a re-working in C when we got the job of putting Rainbow Islands onto the PlayStation, Sega Saturn and PC. By 1995 we were writing in C. We had a game engine in the tank game we were writing but we needed specifically the Rainbow Islands game engine so that we could run all of the Amiga island data files. I adjusted all of the game constants and timing loops to run at 60 frames per second instead of 25 and tweaked a few objects as we restored a couple of compromised areas such as the screen height. We had to get the game engine exactly the same to be able run all of the game elements. That was all Kevin Holloway re-writing the engine. I did not envy him that job. We also had to convert the data from big-endian to little-endian at load time. I had the Amiga A1200 adjusting the data files and then handed them to Kevin to run. I think he was impressed that the idea worked, and I was impressed that he got it all debugged.

    When you`re writing in C, all the assembler foibles are gone. The compiler is creating the machine code. It will try to use the CPU registers, and now onboard cache RAM, for the best performance or space, depending on your compiler options. Naturally we are going for best performance. That might mean it unravels some loops all on its own. Does anyone know how clever the compiler really is? I read that it might even reformat an array of structures into arrays of individual fields. Reserving CPU registers for particular purposes is shot to pieces, and in any case you are no longer talking to the hardware, but to middle-ware or drivers, and that gives you flexibility of working on different hardware at the expense of knowing how efficient it really is.


    Taking a Break

    Fast-forwards 20 years and I re-implemented the AMP game engine in C, I never saw Kevin`s code for PC Rainbow Islands. It wouldn`t be much help now, being a 16-bit system. I created a new 32-bit variant, applying as many improvements as I could think of, and avoiding as many awkward bits as I could. The biggest change I made was to put a mini 16 entry stack on each object so I can do proper call and returns, and store loop tops and counts, which can all nest nicely. I wrote a ClearStack function that is good for interrupting processes that are not going to continue and you want to know that all the stack is available again.

    I also had to do things differently as a C compiler doesn`t let you do one of the fundamental things that assemblers do, and that is address a point in a data array downwards. That`s because the C macro pre-compiler is single-pass and can`t see ahead in the code. I considered using assembler macros to generate a lot of my data and got hold of a generic assembler. The 2-language mechanism all looked a bit too complicated so I decided to go with C and bend my code so it always looked upwards. That got rid of my GotoDependingOn macro anyway! 5 years later and I`m still thinking whether I can fiddle that back in. The benefits of building all the code in one single compile totally outweigh any benefits of using assembler macros. I just can` t have relative references, which does mean I can`t load data at run-time as all the addresses are absolute. My game code runs to less than 1 Megabyte currently, I don`t think a multi-load will be needed!

    I have tried to come up with ways to use multiple CPU cores and processes, but in a world where the processing order is both relevant and important then it becomes difficult to isolate things that can run asynchronously. I did want to get the big background plot started as soon as possible so I had to give a few objects higher priority to get put in the master element list at the front. The backdrop draw is top priority, get it updated first. It might be just a colour fill or a picture copy, other times it might be a full scrolling background. If it is the latter, it will be done after the player update. Then I can look at getting the background draw started while the other elements are being moved, which nicely overlaps updates and drawing. I did also manage to weave my gravity processing between elements into a separate thread. That does need a bit of thread control to make sure the gravity thread has finished its work before the next frame starts, otherwise the linked lists might start being altered while they`re still being read. This sort of optimisation would make a mess of a single Jackson structure diagram, but if you draw a diagram for each process then it`s all sensible. How you knit them together is more architecture-related. Everything was written and tested on one thread first.

    PC Monster Molecules (2023)


    Conclusion

    Whilst you don`t need a game engine at all, you will probably find yourself coding similar pieces on each game. By having a game engine, you know that you can do collisions, animations, linked objects, movement and know that you have a working set of code so you can concentrate on the new bits and the little details. My game engine supports new functions that can be more game-specific, though I try to keep them generic enough that they support top-down games, or side-on games. That might just be something simple like which direction you want gravity to operate. The game engine can just be the bit that calls all your new functions. Be flexible with the design, allow for expansion.


    5

    View comments

  5. Introduction

    Over the years of my IT career, and I include games development in that, I have seen computers go from 2 colours to photo-quality. The technology behind that is mysterious, but I have had to work with the limitations of the video chips, and push what I can do with them.

    I am going to concentrate just on the colours here, not the great advances we have made in shrinking those coloured pixels so we can get more of them on the screen. 

    Colour-Blind

    Firstly, I am colour-blind, mainly in the green-brown area, but light greens and yellows aren`t great either. That`s caused by having a lower number of red cones in my eyes. This means that greens overwhelm reds and I fnd it difficult to distinguish the red content of a colour, leading me to think that oranges are browns are greens. I also have trouble with reds and greys. Trouble only occurs where the brightness is similar and I am unfamiliar with what I am looking at. Traffic lights, for example, are easy because I know where the colours are, and the green light is a lot brighter than the red light. Back in the day, the red light had the word "STOP" written across it in black, which was another clue, but rather language-dependent. "Stop" might ought to be an early word to learn if you get in a car. While I`m here, I think that in the new world of LEDs we could save space with our traffic lights and make them easier to read by having one circular traffic light that can change colour and shape. This would reduce manufacturing costs. We could have a red circle with a black cross over it, then a slightly larger amber circle with a smaller cross, and finally a green circle. We could even have a sponsored Nike tick on it. I digress.

    Don`t let anyone tell you that colour-blind people can`t do graphics. Sure, I have to look at the hex values to check what I`m doing, but given that there will be colour-blind people playing the games, I can ensure that I pick colours that can be distinguished. If it doesn`t work for you, it won`t work for me. Get someone who isn`t colour-blind to check the sanity of your work though! Know your limitations. I once repaired a joystick and it didn`t work still because I had soldered a green wire to a brown wire. I even had someone check my soldering, but later he told me he is colour-blind too! 

    1977

    Our school managed to get a Philips computer that consisted of a desk-sized box with a card reader and a printer. There was no screen, so we can count this as a monochrome computer as it was printing in black on white paper.

    1978

    The school got itself a Commodore PET that lived in the electronics club. I believe I got a glimpse of it one time. That had a green screen, I believe. I did actually get to work a bit on a PET in about 1984 when Steve Turner and I did a little job for my Dad`s firm that made double-glazing. We wrote a program in BASIC on our C64, with Steve adding some assembler to draw diagrams, to show windows arranged with Georgian Bars. We had to set the positions of the bars and then give cutting information so that the bars could be cut from fixed length bars at the factory. We were able to write the BASIC on a C64 and take it on disk to the office and load it into the PET. That`s good compatibility, well done Commodore.   

    1979

    I started on my programming path on an IBM VM370 mainframe and that had green screen monitors. I believe they had a bright character attribute, so we were treated to 3 colours: black, green and light green. The green was supposed to be better on the eye than white. About 1981 we got one colour monitor. That had about 8 different colours in normal and bright variants. I did get to adapt one of my COBOL games called Space Chase, to run on it with some colours on screen. The monitors only supported character modes but were quite smart. We used to write user messages in shouty capitals. I championed using mixed case, but there was even a switch on the side of the monitors to convert all the lowercase letters to uppercase, Well, that`s a user`s choice, I suppose. We were coding COBOL in capital letters too, by the way.

    Next came Dalek Hunt. Of course you weren`t hunting Daleks, they were hunting you, in two shades of green.



    1980

    We got a ZX80, and we were back to black and white, and the screen display was mutually exclusive with the CPU running. No big decisions to be made regarding colours, just whether you want the pixel on or off.
    The next year, we got a ZX81, which could think and display a picture at the same time, though still just black and white.

    1982

    We got a Dragon 32 this year, as the fragility of the ZX81 was somewhat disappointing with a 16K RAM pack hanging out the back. The colour choices on the Dragon were, shall we say, interesting. There were two hi-res colour schemes, either black and white or black and green. Then it had 2 multi-colour modes, one with red, green, yellow and blue, the other with cyan, magenta, black and white, I believe. I don`t know how they chose those colours, they`re just pure hues with no real attempt to make them usable. The colour modes on a black and white TV would be difficult to distinguish too. I did see some games come out with different versions on the tape for different modes and colours.  I wrote some BASIC demo games in these colour modes, but found them quite tasteless. The Dragon BASIC did support user-graphics "sprites". I wrote a Lunar Lander where you could put the landing locations where you wanted while it drew the mountain range. I used the red, green yellow, blue colour fruit-salad scheme.

    Dragon 32 Seiddab Attack

    1983

    This was the year I started seeing the Commodore C64s in the shops. While we still couldn`t pick the palette of colours, which of course comes with its own dangers, there were 16 colours to choose from, that maybe had a bit more thought behind them. I regarded them as 2 banks of 8 colours as there were some graphics modes where only the first 8 colours were available to choose for the foreground colours. The first 8 colours were the more obvious primary and secondary ones, but the second set of 8 had some more useful colours, including 3 grey shades. The C64 palette was thought about; rather than just having colour ON, colour OFF mentality.

    1984

    While side-stepping the ZX Spectrum, which had its 7 different colours not unlike the the C64 first 8, and in two different brightnesses, I moved to the C64.




    I liked the C64 multi-colour modes and used any number of wacky combinations for Gribbly`s Day Out. The game used a single constant foreground colour as I couldn`t afford to scroll the colour map.




    1987

    We bought a pair of Opus PCs for code development and cross-assembling C64 (and Spectrum) code and we`re back in two colour mode. We chose amber screens over green just to be different. All 8-bit graphics were still being developed on their respective hardware, as were my Dragon 32 graphics. It was important to see what the graphics would actually look like on the real hardware. We didn`t even develop C64 graphics on the Spectrum or vice-versa. That was partly down to only saving them out on native formats still. I was using C64 5.25" floppy diskettes with CharFont and SpriteMagic to develop my graphics .My multi-character fonts were usually laid out so that they could be read on the editor screen. Having look-up tables to get from character codes to font letters meant there was no easy way to see text in a memory dump, I don`t believe I used ASCII codes. I got used to writing text in hex codes.

    By this time I had also obtained a Commodore Amiga 1000 and, along with Deluxe Paint, I was first able to work with 32 colours of my choice. I tended to arrange the colours in groups by shades, in different brightnesses. I did some mock-ups of Uridium and then Morpheus mainly using lots of grey shades. Colours are not my strong suit, I believe we already established that. 




    It was about this time that I bought my Atari 800XL, which notably has a 256 colour mode, the hard-wired palette of which appears to be based on HSV colours which look lovely and metallic. I know the pixels were enormously wide in that mode, but I did have a couple of nice demos that show that off. Explanation of HSV colours follows later, when I understood what was going on.

    1989

    Amiga colours used 3 4-bit values for the colour palette. Each colour had red, green and blue values from 0 to 15. The Atari ST only had 3-bit colours, so values 0 - 7. That was OK for a brightly-coloured arcade game. The Atari STE came along later with 4-bits of red, green and blue, but rearranged as they had put the 3-bits on the ST in the lower 3 bits, so the STE had the new low bit in the high position. We had to rearrange the bits at run-time. We probably used an algorithm to swap all the bits at once. A 12-bit translation table would be too large, and an 8-bit translation table would need to be accessed twice, so likely we just copied the colour shift and mask and then re-OR them together.


    For Rainbow Islands, John Cumming was producing the level maps, the 8x8 pixel character tiles and the "sprite" graphics. We used 16xN and 32xN pixel plotters. Anything wider was done by multiple calls to consecutive 32-pixel-wide images. John had to deal with the fact that the arcade machine had backgrounds done in 16 colours, each island had its own palette. Then each sprite could have its own palette, of which there would be one for the island sprites and one for the common sprites such as rainbows and fruit. We needed that compressed into one 16-colour palette. John had a common palette of about 13 colours and the rest varied by island. That was a tough job. After that, he had to remap the Taito graphics into our new palettes.



    The colour resolution on computers then is still very much of cartoon quality. The Amiga did have Hold-and-Modify mode to display more colours on screen at once, and that was good enough to get a semblance of photo quality. I don`t know if anyone ever used that mode in a game. Hardware sprites could be used over the top of a HAM picture, but plotting graphics onto such a bitmap picture would maybe have rather moody edges, given that red, green and blue data might need to be changed pixel-by-pixel. Each pixel of the picture can either be one of the palette colours or it can modify one of the Red, Green, or Blue values of the previous pixel colour. Choosing the best palette for the picture is vital. At this point, getting a digital photo would have required scanning a printed photo or negative. 

    1990

    For Paradroid 90, I took charge of the colour palette. I did that because as I was writing more plot routines, I realised from the Amiga`s 64-colour mode that displays half-bright pixels specifically for the purpose of creating shadows, that I could emulate that in 16-colour mode so the Atari ST could do it too. The Amiga would be quite heavily performance compromised in Half-Bright mode as a lot of address bus cycles could be lost to bit-plane data fetches, and all the object plotting would be writing to 6 bit-planes instead of 4. 

    I organised the colours into pairs, darker and lighter. I had 2 colours reserved for the alert lights flashing. A palette often needs 3 or 4 brightnesses of each colour to get some nice 3D shapes and shadows. I had 3 groups of 4 colours and a black in position 0 as that was also the border colour. I would have a dark colour in position 1, then the two alert light colours that would be animated to flash green, yellow, amber or red. Then I had 3 blocks of 4 colours, the brightest of which would have white as its top colour so I could use that for specular reflections. I let the graphics artists select the exact colours they wanted, and varied some of them from ship to ship. Since every game sprite uses all the same palette then subtle changes tended to be used.




    One of my plot routines just removed the sprite mask from one bit-plane. This had the effect of changing any odd-numbered colour to the previous colour below. The principle floor colours would be made of odd-numbered colours so that they darken and the shadow plotter takes the mask of the sprite image to be shadowed and draws it onto the background. Even-numbered colours would not be darkened. The plot routine is applying a logical AND operation on the screen data, not operating on the pixels by palette number, that would take too long.

    1991

    We started Fire and Ice. With a central character sprite that has to go through all of the different lands, its colours shouldn`t change, which locked down a lot of the colours. I also wanted to freeze the meanies. I split the palette into groups of 4 and wrote another plot routine that performed a logical operation on two bit-planes that plots the sprite in only the top 4 colours, which were the blue cold colours. I used the Turrican II idea of plotting a different background colour on every raster line to get a nice sky fade. That got me a lot of colours on the screen. I wanted to do a sunset fade to signal the passage of game time. I worked out how a sunset gets all the lovely reds and oranges and calculated the colour fade in real time, only to realise that 4 bits of red, green and blue, 16 different values, is simply not enough. The colours went very lumpy during a sunset or sunrise. On one hand it looked quite clever, but on the other, the quality of the colours was sub-par. That`s when I formulated the day and night fades and the curtain fall and rise swap. That way I had better quality fades. I threw all that sunset code away, which likely represented a couple of days` work. When it came time to do the AGA version, I really wished I had kept the code as 8 bits of red, green and blue would have been plenty.

    Some other Fire and Ice tricks were that the underwater section has all of the red removed from the palette colours, which looked OK to me, but what do I know? I hardly detect red anyway. I used the sky fade to do the river colours on the jungle setting. That required the sky fade to be fixed and not scroll up and down, which meant that the foreground couldn`t scroll vertically either. The sky/river fade can`t be seen in the waterfalls area where I needed vertical scrolling, so we went mad on "character animations" for the waterfalls. Phillip Williams managed to get 3 layers of water going at different speeds. He had a couple of goes at that to get it right as it is a complex animation.  






    1993

    Uridium II began. We started with a dual playfield variant, which only gives us 7 colours on the foreground layer. We might have restricted the back layer to 3 colours since mostly it would be behind the main layer. While the parallax scrolling was nice, the  7colour restriction for the backgrounds was limiting. Walls and other collidable background objects needed to be clear. We needed shadows too. Phillip did do a good job, but we decided more colours was more important than a parallax layer. Maybe if we had considered using 16-colour  hardware sprites for most of the moving objects then we would have had just enough colours. I had not written my hardware sprite routine yet and didn`t necessarily get how powerful it would be even with only 4 of them as the sprites can be reused down the screen, but there would be no contingency of using a background plot as the colours would not match. I did have that capability with the finished 32-colour version.

    Dual-playfield prototype


    I switched to single-playfield 32 colour mode and that gave the graphics artists twice as many colours. I had the colours arranged in pairs: darker then brighter, plus I needed the hardware sprite colours in the first 16, or is that the second 16 to be used for many of the flying meanies. Likely I was using the at least 2 hardware sprites for the backdrop moving stars. The rest of the hardware sprites were 16 colours, and due to there being only 3 remaining hardware sprites, I needed a contingency plan to plot more on a line into the background seamlessly with a contingency plotter. Once again there was a shadow plotter to drop the odd-numbered colours to the even-numbered colour before it. 

    We had developed an AGA version of Uridium II already. This used 6 bit-planes instead of 5. Hardware sprites were still 16-colour mode, so we needed some input graphics to still support that. Given that hardware sprites go over the bitmap background, and I wanted the Manta to fly under some of the scenery, it was not a hardware sprite. I used the hardware sprites for high-flying craft that would ride over everything. Hardware sprites are great for cheap plotting into the sprite buffers instead of having to plot onto the bitmap, even with the blitter to help as you also have to then unplot the image by restoring the background afterwards. Well worth taking the hit of only having 16 colour graphics.
     
    I wrote some 68000 code to convert RGB colours to HSV. Anyone using art packages will likely be familiar with the HSV wheel, cube or cylinder. Instead of using colours by Red, Green and Blue, noting that this is the only thing 16-bit and beyond computers understand, one can also represent colours by Hue, Saturation, and Voluminousnosity, or the meaningless: Value. Hue is the colour of the rainbow, Saturation is how much white is mixed in, and that big V-word is how bright it is There is some straightforward maths to convert between the two notations. HSV colours do slightly suffer from gimbal-lock as pure white and pure black have no Hue as such, so you lose your hue if you go to those colours and you won`t find your way back. Other than that though; fading the screen through HSV colours gives a more natural flow than just scaling by RGB values. I did use some HSV colour fades and found them a bit smoother as the colours change.

    1994

    We were given a CD32 to test versions of Fire & Ice and Uridium II, at least. We had no development kit for the CD32, so we developed on the AGA A1200 Amiga. We added joypad control options, CD music-playing options and for Fire & ice, which was a 16-colour game, we added a second 16-colour playfield. The graphics artists gleefully created large pictures with 16 colours of their choosing that would run behind the main game screen. As mentioned above, the AGA machines had 8-bits for each of red, green and blue in the palette colours, so 256 different values and a total of 256 cubed colours, that`s getting on for 17 million different colours. That`s as many colours as most PC games still have today, unless you`re getting into the world of HDR colour. But it`s still palette-based on the Amiga, so we`re using now two 16-colour palettes and higher definition sky fades as I increased the resolution of the colours to 24-bit and daily regretted deleting the sunset code.  



    For Uridium II, we already had an AGA version that used 64-colours. We only had to hook in the joypad controls and get some CD music done. Jason Page seems pretty sure he did create the CD music, though we have not found any CD music for it. It would be on a DAT tape. Then it all stopped as sales were not that good and there was no financial benefit to be had by continuing the work as we weren`t likely to exceed the guaranteed minimum sales royalties. That does annoy me now because it would be the version most likely to live on today on a CD rather than a floppy disk.


    1995

    We had been using PC Deluxe Paint for a lot of our graphics work. This supported up to 256 colour mode, still from a palette of colours defined by the user. The screen layout would be byte-per-pixel. All that was about to change. Hardware accelerated graphics cards were starting to arrive. Graphics data was about to change from byte-per-pixel 256 colour mode to long-per-pixel nearly 17 million colour mode, with no indirect palette. While that gives us all those extra colours all over the screen, the one thing we lost is that indirection of the byte colour number going to an actual colour looked up in a table. That stopped us from getting big on-screen changes just by changing some colour values. No more fading colours or the whole palette by just hitting the palette array, and no more animated waterfalls just by moving some colours around, not that we did that.

    Consoles also went through that upgrade from palettes to long-per-pixel. I`m not going to parade my ignorance by trying to guess when consoles changed from palettes to colour-per-pixel, it was likely just after my time. I know we were chasing the hardware and software with our tank game where we started as a DOS game with byte per pixel and then we had to convert to Windows, tried to support both software and hardware acceleration graphics cards and we were working on PC and PlayStation. I don`t recall us getting into long-per-pixel colours.

    2018

    Fast-forward to the near present. I felt a hankering for writing more games and began my PC experimenting. I dallied with Direct X 11 for a short while, figuring out that it was lovely but way too complicated. Then Direct X 12 landed and changed the landscape. It didn`t get any simpler. I decided to let someone else do the heavy lifting of display and chose SFML to do the graphics for me. I still don`t understand how shaders fit together. I know what they do, but not how they get to the graphics card and how to get them doing stuff. Bit like I know in principle how a car works, but I couldn`t build you one.  

    I realised pretty quickly that pixels have gotten way smaller so it`s pretty tedious trying to draw them in an editor. Just selecting colours from a palette of nearly 17 million means you`d be picking every pixel by hand from a giant palette swatch. I came up with some solutions. Firstly, I could draw something simple in about 16 shades of grey and then let SFML tint the image with an actual colour at run-time. For example, I draw one player ship and then tint it to get 4 different colour players. Secondly, I turned to my SLR camera and could use a photo I took of the Milky Way as a backdrop. I also took some photos of pebbles and stones in the garden  and scaled them down to make rock graphics. I also thirdly looked for some free images of planets on the Interweb. One has to be careful there as images might be copyrighted. I found some lovely game space-ship sprites too, but they turned out to belong to a clip-art site that expected payment. Having found that out, I did have a look at what else they had. They showed promise and I considered buying some. First I tried a couple of them, but I needed such small versions and I needed them to rotate, that actually they were too complex to work. I ended up just drawing something simple myself. That game is not going to get released anyway.

    2022

    I persevered with solution 1 for game 2: Monster Molecules. I am primarily using colour effects rather than draw graphics, overlaying multiple small images and using a lot of semi-transparent effects to get fragments and flames. I did try solution 2 by using some of my photos of fireworks and associated fires, but that was a disappointing failure as once again it was just too complicated. Simple is often a better solution.

    I revisited the old HSV colour wheel and recoded the RGB to HSV colour conversion algorithms in C. It`s a fun one to test because you can convert a table of RGB values to HSV values then back again and check you end up with what you started with. This works as I mentioned above, for everything except black and white as they have no specific Hue. 

    Colours today still have the same number of bits as the AGA Amiga, though now we might use them for pixels or tinting sprites instead of changing palette colours. I couldn`t think of an immediate use for the HSV functions though.

    I did go through an exercise of pre-processing my input graphics sheets for a number of purposes, including applying partial transparency to edge pixels, effectively anti-aliasing, and blending adjacent colours a bit to make them less bland, give the surfaces some idea of texture. There was some HSV blending potential there, but I didn`t feel the need as there is some random element anyway. Doing that on photo-quality graphics will only blur them too, which already happened once with the size reduction.

    2023

    Fast-forward to the present and Monster Molecules is almost complete. 




    Yesterday I decided on another way to fade my colours, particularly flames dying out. Since I can`t get at the SFML shaders, and they do a good job of most things, I wanted to fade down the colours in a generic manner. The problem with my current generic solution is that you`re going to just subtract (or add) a fixed amount to each of Red, Green and Blue. The lower values will quickly reduce to zero, so they need to be checked to stop wrapping. Similarly at the top if you are adding colour. Suppose you want to fade lights in and out. You might generically subtract 10 from each colour part, and one of them might reduce to zero. When you start adding the values again, the lowest value raises with all the others and will ultimately end up higher than it was, so you`ve changed the colour permanently. Yes, you could have adjustment values that take the original  colour into account, you'll end up with at least 7 lists of adjustments and if you change the master colour then you may need a new list. If the colour is randomly chosen then you have to spend some run-time working out the best list of adjustments. One could also take off a percentage of each of the 3 colours, but the values will then only tend to zero, unless you also record the original colour. . 

    Now if you want to keep the colour and just change the brightness, the V, then it really is a good idea to record the original value of the colour. Then you can darken, or lighten it. Darken by calculating percentages, in this case hexadecimal per-hex-ages. 100% (or 256) gives you your start colour, and 110% (or 281) gives you a brighter version of it provided you don`t "burst" a colour by needing more than the maximum 255 value of any of the Red, Green or Blue values. It`s OK for a quick splash of extra colour that may tend towards white before fading down towards black. Having some variations of these overlapping semi-transparent objects gives a better natural blend than them all being the same colour. So I randomly vary the start colour, and then expand that before bringing it down. Since I also affect the transparency, bringing it down randomly, I get some some nice blended effects. As with real flames, a static picture doesn`t do it justice.

    Conclusion

    Computer colours achieved near life-like quality when we ditched palettes and decided that 8 bits of Red, Green and Blue for every pixel is not-half-bad. I do remember my old school friend Gary Sewell telling me that his radar system video chip supported something like 24 bits of Red, Green and Blue, and 22 bits-worth was about where you couldn`t see the join. I do also note that my SLR camera spectacularly fails to pick up reflected sunlight on a dewy morning, so 8-bits isn`t perfect. TVs are nowadays supporting HDR 10-bit colour, which might be fiddling with the ranges of your basic 8-bits, I don`t know. 

    I do miss some of the tricks we could do with palettes: fading the screen, flashing colours, sky fades and colour transformations with plot routines. Ah, the good ole days...









    2

    View comments

  6. Introduction

    How does one go about producing a computer game conversion of an arcade game or another computer game? Back in the day. arcade games started off with 8-bit hardware the same as home computers. They always had a headstart on home computers though. By the time we got our nice 8-bit machines like the ZX Spectrum and Commodore 64, the arcades were starting to use 16-bit CPUs and some handy sprite chips and all the memory they wanted. By the time we had our 16-bit home computers, the arcades were experimenting with scaling sprites, and even mechanical cabinets.


    Expectations

    Comparing the hardware capabilities of the "from" and "to" platforms, we should get an idea of what is possible. If you are going from a 16-bit arcade platform to an 8-bit computer, then it is a case of working out what is the best you can do. Sometimes, you might be going from a smaller memory footprint to larger, and you might therefore be able to add something. You wouldn`t do that to an arcade conversion, of course, but you would not have better hardware anyway. It was simple financials: the arcade machines would cost whatever the game software needed, whereas home computers were built down to a price-tag. Despite needing to be more flexible, running a word processor or a spreadsheet doesn`t need a lot of expensive video chips.


    Starting point

    How easy the conversion is going to be, depends on what your starting point is. What information will you be given? You might be given all the source code, which will only help if understand the language and possibly you get the full hardware manual. Comments aplenty in the code will help. You might get the arcade machine, in which case you're going to ahve to play the game all the way through, possibly multiple times, and you`re going to need to figure out the behaviour of everything you see. You might get the original author on the phone, or even in the room, that'll help because you can ask questions. You could, if you`re very lucky, get the original design documentation. You`re not out of the woods though, as the design may have changed and expanded during implementation. Indeed, it probably will change and no-one is going to go back and edit the design documentation.


    Horror Story 1

    My first conversion job would have been when I was given a job to take another department`s online program and make some tweaks. The bad news was that the COBOL program was written in Italian. All the COBOL keywords were English, but the variables, constants and routine names were in Italian. UNO-BINARIO-NEGATIVO is still imprinted on my mind, binary minus one. I was given about 6 weeks to make some alterations to this code. Half-way through, I was struggling to find my way around the code. My boss wanted to have a word and I had to explain that a translator might be handy. Another week went by and I wasn`t really getting anywhere. I couldn`t even figure out how well-written the software was. I could see w the software running, so I decided a rewrite was in order. About two-and-a-half weeks later I had the rewrite operational. The moral of that story then is that sometimes working with 'difficult' source code is a lost cause. Go back to the design level and start again.  


    First Game Conversion

    By 1983, I had experience of writing my own COBOL games, and I`d even written my own COBOL version of Space Invaders. I`d played a lot of that game in the pubs and seaside arcades. I was also, by job title, an analyst programmer, so had shown some aptitude in working out what`s going on. I don`t know what happens after about level 4 of Space Invaders though. Do they keep coming down another step? I guess no-one can get past level 4 of my version either! I can`t remember what I did, actually, but I`d probably stop the forward progress of the invader array at a certain point so the player had an outside chance.

    COBOL Space Invaders listing


    I had accepted Steve Turner`s invitation to join him writing games, so during my notice period I figured it would be a good plan to try and write some assembler on the Dragon 32. I had only written a couple of BASIC programs up to that point. Firstly we had a ZX80, then a ZX81, and my Dad decided he wanted a proper keyboard next, so he bought a Dragon 32 rather than another Sinclair. I didn`t have an assembler at the time either. I bought a 6809 assembler reference book and followed the design of magazine listings to poke values into memory and then call the machine code. To that end; I had to write out my first assembler routine, a sprite plotter, on paper, then translate it into hex, and finally then into decimal to poke into memory. I had to visit the Colchester Tandy shop to get a hardware manual as the Dragon 32 was a close copy of the Tandy Co-Co. Thus I learned how the bytes for the hi-res graphics screen were arranged. I confidently scribbled my routine in pen, thinking I would get it right first time. That was a new lesson; no matter how simple you think something is to code, you will not get right first, nor second, nor third time. 

    Not knowing anything about how fast these computers were, although I knew assembler was way faster than compiled COBOL, I thought I'd design a space-ship graphic and map each pixel separately. I figured I could plot the object normally or apply a position adjustment to blow the object up, pixel-by-pixel. I knew from talking to Steve about his first game: 3D Space Wars, that I would need an un-plot routine too, to clean up the screen. We didn`t have much concept of buffering screens, if even the hardware would allow us to select a different area of memory to display. Would there even be enough RAM to make it viable in games?

    It took me quite a few days to get my plot routine to work. The next lesson is that if you get something wrong in assembler, it might well go off anywhere, or loop forever. Save your work before every test. I had to keep my master listing scribbles up to date as well. I also had to map out the offsets from the centre to every pixel. I didn`t realise that for efficiency, one really has to work with sets of 8 pixels in bytes, not individual pixels. Working with pixels, you have to calculate the position from scratch, check it`s on screen and plot. Working with a proper graphic image, you can take a lot of short-cuts, clipping the object checking once for left right, top and bottom, and plotting 8 pixels at a time. I had yet to learn all this. 8-bit computers in bitmap mode had 8 pixels per byte, or 1 bit per pixel, they were on or off. Nowadays we have 32 bits per pixel, and 30+ times more of them on a 1920x1080 screen.

    My plot routine worked, I had proved to myself that I could write a small piece of 6809 assembler and run it. It wasn`t really all that fast, and since I wasn`t yet even trying to screen-sync, it was un-plotting the old image and plotting the new one straight after, so it flickered somewhat as that whole process might have taken longer than a 50th of a second. I was quite please with myself though. As soon as I started working with Steve, we went to the local computer shop and mercifully they had a Dragon assembler program on the shelf, Dream, I believe it was called. We also got a Dragon 32, of course, and a dual disc drive of the 5.25" variety. It also had an early Microsoft DragonDOS OS. Listing the contents of a floppy diskette used to fly past at great speed, since I didn`t know about piping to the More command.

    Graftgold did not yet exist, we started out as ST Software. Always get the company logo sorted out first.

    My attempt at a company logo

    3D Space Wars

    My first conversion job was then to take Steve`s 16K Spectrum 3D Space Wars and make a Dragon 32 version of it. Firstly, Steve wasn`t using an assembler. He hadn`t been able to find one so he was writing in Z80 hex machine code, with labels. He had written an auto-loader, which I believe he even advertised commercially, that read the hex in BASIC REM statements and tied up all the labels and resolved the JMP statements to those labels. Even that would have help me with my first assembler plot routine, if I had thought of it. 

    Working with the original source code was never an option. Steve did then explain to me at a higher level what the game code was doing. We could communicate in Jackson Structured Programming diagrams that we had independently learned in our previous jobs. The best starting point, then, is to have the author of what you`re converting sitting at an adjacent desk. Mostly I could just get on with the job of coding from the high-level design. At one point I decided I needed a sort routine, and found a method in one of the reference books we had. The whole job took me about 6 weeks. Converting a 16K Z80 game into a 32K 6809 game gave me some spare space to fill. I designed a launch sequence and some more spaceship graphics for more variety and a refuelling ship. I wasn`t going to go re-designing too much of the boss`s game.

    Launch Sequence Graphics


    The sound capabilities of the Dragon 32 were not much different from the Spectrum`s. Just a single bit that we could waggle about over periods of time, at great CPU expense. I asked Steve to write a sound routine and set up sound effects based on the Spectrum`s. While we`re talking of great CPU expense, the analogue joystick system of the Dragon 32 was not well documented at the hardware level. I ended up finding the call in the ROM to pll the joystick and we went with that. However, it was based on timing and the position of the stick in each axis was determined by how long it took to read. They weren`t using interrupts, so a down and right joystick may have taken a quarter of a frame to poll, whereas up and left would return almost immediately. Guess which one affected the performance the most.

    Approaching Enemy Squadron


    Seiddab Attack

    While I was busy writing 3D Space Wars on the Dragon 32, Steve had finished his second game on the ZX Spectrum, which he wanted to call "War of the Worlds", based on the H.G. Wells book and the 1953 movie. That was until a couple of days before final delivery and we got a call to change the name, so it became Seiddab Attack. 

    Seiddab Attack


    Steve would have created this second game from the common bones of the first. Some routines can be common to many games. So, I was able to do that too. Now, I had two inputs: Steve`s new game and my first program. This game involved driving a tank round a a city at night. We had graphics for the city. I had to have that mechanism explained to me as you drive down the roads and can rotate at the crossroads. With more graphics for this game, and no way to extract them from Steve`s game, I decided to write a graphics editor in BASIC to make it easier to put in the graphics. We used to draw the graphics on squared paper and then work out the hex vallues per group of 8. Drawing graphics in black and white isn`t too bad, you only have one choice to make per pixel: on or off. Using the graphics editor allowed me to save the data to disc, but by the sounds of it, we still got the program to print out the data and we had to type that back into the assembler, so we wouldn`t want to be editing the graphics and re-entering them too much.

    Under Attack


    The conversion took around six weeks again. It shouldn`t take as long as the original game because all the tuning has been done, I`m just copying a working design. Again, I was copying a 16K game, so I had space to spare and could create a few more graphics for the game.


    3D Lunattack! 

    The third Conversion was Steve`s third Spectrum game. It was complete so I could get started on that straight away. Still no source code to work from, but all went quite smoothly.


    Dragon 32 sales seemed to be tailing off, so we decided to switch to another platform. I had already been playing some nice C64 games and decided I wanted to write something for that. Thtis meant getting a C64, a disk drive, a 6502 book and learning the new assembly language. Fortunately there was a good macro assembler available, though a full compile of a game was liable to take 30 minutes. In order to famliarise myself with the new platform, we decided I should write 3D Lunattack! for the C64. This removes the issue of creating a new game. So this time, the inputs were two different versions of the game, pus a clean sheet of 6502 code on a new platform. 



    This time then, I had a 16K original and a 32K conversion going into a 64K computer. I had the advantage that I had working 6809 source code and could more or less do a routine-for-routine conversion. When you have less CPU registers then your code is going to be markedly different. Many of the routine names can be the same as used in the previous version. In order to test things effectively, I built the outside layers first, i.e. can we get control of the machine, switch off the Operating System and set up the hardware how we want it. 



    This game was using a bitmap screen, slightly larger than the Dragon 32 and Spectrum screens. I can't remember the exact byte-for-byte layouts of the screens now, but the plot routines would be broadly similar. The input data would be the same. I used the hardware sprites for some features for almost free plotting, which speeds things up, and gets more colour on the screen. I could have used character mode for the lower part of the screen, if I had thought of it. Probably I was just thinking about using the same graphics routines and not thinking enough about time taken, or maybe it didn`t make a lot of difference. I was still learning and not yet thinking about the finer points of optimisation. 



    I thought of using a map of part of the moon to give the player something extra to look at and choose where to go, rather than have a strict sequence of scenery. That then changes the mechanism to choose what the scenery and enemies will be at each phase from purely sequential to decided by the map. The player can then also steer on the map to choose what to go for.

    New map screen

    After completing C64 Lunattack, I had caught Steve up, he had decided to make the next game 48K on the Spectrum and that was necessarily going to take longer to fill. I did do a bit of playtesting on Avalon to help out, while also thinking about my next project, which was going to be an original title. In order to use the strengths of the C64, it was decided that our developments would diverge so that we would both be developing original titles.

    We then had Dominic Robinson produce Uridium on the Spectrum. He had devised a system of storing pre-rotated pairs of characters and managed to do the near-impossible. 

    Dominic and John joined Graftgold and we picked up a Spectrum project to convert Flying Shark to the Spectrum, That arcade machine had at least two playfields and a lot of sprites on the move. Add to that the schedule that they wanted it in about 6 weeks. We just had the arcade board to work from. Fortunately, the game design doesn`t have too much complexity, though it does have a lot of graphics and runs a lot of sprites at times. Dominic and John did a great job under pressure, basing their version on what we could see on the arcade machine. I believe there are 5 big main levels before it repeats.


    Horror Story 2

    Steve Turner did a couple of conversions. Magnetron went from Spectrum to C64, and then he converted my Intensity from C64 to Spectrum. The former was his own game, and he created macros to convert Z80 to 6502, which was probably quite tough given that you're going from a good set of registers to just the 3. it wasn`t his project, so less familiar, but he did have the source code and me to ask questions of. The original game used most of the C64`s 64K of RAM, using a character screen, and 29K of code and squeezed it into a 48K Spectrum with a 6K bitmap screen.  

    He went through the whole of the source code. After some weeks of coding, he was ready to try it out. He seemed disappointed that it didn`t work first time. It probably took him another fortnight to get it working. On the one side; I`m hugely impressed that it was even possible, but on the other side; I would never want to put myself through that torture.

    Converting the whole lot at once means any mistakes will cause new and interesting ways for it not to work, and you never know whether you've tested everything, there could be unused code in there, or at least code that hasn`t been called yet. This required a lot of patience and dedication, kudos. While it might have been the quickest way to get the job done, I believe there`s a high chance it could drive one mad. The whole thing still gives me the shivers.

    I generally like to work from the outside, get the game shell just running and then add features one at a time, test them in isolation and build things up in a working state. One of the fundamental mantras is that if something unexpected goes wrong in the program, look at the last thing you changed. No matter how apparently disconnected it might appear, it`s the most likely candidate. 


    16-bit Era

    My next conversion was not then for about 5 years. We got the job of converting Taito`s arcade game Rainbow Islands, onto 5 platforms. We were provided with an arcade machine of the game, which we delicately had to manoeuvre up the rickety fire escape to our first floor office above the green-grocers. We also got a ring-binder full of the game`s design document. I am still impressed at how much was designed up-front. We also got sheets of graphics for the sprites, and some backgrounds. 


    Graftgold by then had 7 staff, and it took all of us working together to design our solution to getting this game working on all 5 platforms. The good news was that the game only scrolled up and down, not left and right, otherwise 3 out of 5 platforms would have had a hard time. 1 up for horizontal smooth scroll registers! 



    The arcade spec, as far as I could tell, was two character playfields in 16 colours each. It then could display a LOT of sprites, and again each could have a palette of 16 colours. We had to compress all of those colours down to one palette per island, or set of 4 levels. We fixed about 13 colours because the main player colours, ,rainbows and gems would appear on all levels. We could then have 3 floating colours to help with the individuality of the backgrounds. For example, the monster level had a pinky sort of colour. The combat island needed some camouflage colours.

    We filmed our best player, David O`Connor, playing the game through on Steve`s camcorder. That gave John Cumming all the backgrounds. He wrote a mapper tool in STOS so that he could design the backgrounds from 8x8 tiles. We got some of the early character sets, though we still had to remap them to our palette. I suspect that the graphics sets we received were all that was available at a certain point early in development. 

    The design document detailed the rainbows pretty well, telling us all the things they could do, not necessarily how they were actually programmed though, since that would have come later. We had some speed information for the meanies and how that climbed per island, and additionally with whether players had been in secret rooms for permanent power-ups.

    Combat Island


    I set about designing a new Alien Manoeuvre Program (AMP) system for running all of the game objects. A lot of the meanies were pretty straightforward walk along the platforms and turn at the end, and the flying ones had just bounce off the side movement, but each island had a couple of more interesting items, such as the spiders. All meanies had a normal mode and then an angry mode of operation. 

    The AMP system allowed us to write little "programs" for each meanie, handling movement, animation, and collisions. Breaking everything down into common elements, these programs were effectively just data of routine numbers to call, but we could act on conditions detected, and write general simple routines or complex specific ones. The programs are from the viewpoint of the meanie, or player, or rainbow, so it's quite simple to see what`s going on without modes and sub-modes.

    Graphics were separated into those that were non-directional, and those that needed left and right-facing. We would load in the left-facing ones and generate the right-facing ones from the left. There's no lighting on the graphics. Of course the arcade machine would just set a FlipX bit on the sprites. We had 16 -ixel wide graphics and 32-pixel wide graphics. The reflection routines would be slightly different and they would be kept in separate lists.The plot routines would also be slightly different. To get larger sprites, we would use consecutive 32-pixel wide graphics.



    Dominic Robinson came up with (at least) 2 clever mechanisms for the scrolling background. Back in the day, the 8-bit and 16-bit computers were not really up to rebuilding the whole bitmap screen image from scratch every game cycle, we had to clean up the old preceding images by wiping over the graphics plotted into the backgrounds. Hardware sprites were great because the video chip did the hard work of display, with no cleanup necessary. To that end, the scrolling mechanism keeps a clean image of the background map where the screen is, we called it a barrel, which it rolls upwards, or downwards, as the player moves up. Any sprites plotted on one of the two double-buffered screens is removed by copying the relevant part of the barrel to the back hidden screen. This is done for all sprites before new pltting begins. The barrel will necessarily have a join in it, so some graphics might require a split restoration of two blocks. Later we had a more sophisticated Amiga dsplay system where the two double-buffered screens were barrels as well. 

    Second clever system was that all of the Rainbows and pickups were sprites on the arcade machine. There could be 12 rainbows (64 pixels wide) and potentially a hundred pickups, too many to plot every time. The good thing is that most didn`t move. Dominic came up with an 8x8 character stacking system that overlaid the background characters with little stacks of other characters on top, either fully solid or not solid squares. We could then plot non-moving rainbows and pickups into the barrel and they would then get copied to the working screens. If the rainbow then dropped or the pickups were picked up, we could remove them from the stacks and they would disappear or become plottable graphics. Pickups and rainbows could easily overlap, so there had to be stacks of characters rather than just one overlay. 

    We did also develop a map compressor rather than store all the backgrounds raw, since they could be 40 characters wide by 256 high, that`s 10K of 8-bit data per level, 4 per island. Dominic devised the packing strategy and I coded it. Since a lot of the backgrounds were made of 2x2 character blocks, we created horizontal and vertical macro pairs of common occurrences in multiple passes. This repeated procedure could leave large areas unused where a macro code replaces 2 characters with 1 macro code and a blank. After up to 8 passes we could then further pack the map with run-length compression. The compressor and decompressor were written in assembler, btu the packing operation looking for duplicates might take John`s Atari ST over half an hour to pack 4 levels.

    We had Gary Foreman writing the C64 version, David O'Connor writing the Spectrum version and Steve doing the Amstrad conversion from the Spectrum version, all dealing with the colour limitations on those particular platforms. Dominic had been writing his 68000 O.O.P.S. Kernel to give us a mutli-tasking pre-emptive interrupting O.S. to run the game for us on the Atari ST, and then he got that working on the Amiga so that mostly my development was done on the Atari ST knowing that when it was finished, I could swap over a few files to Amiga versions, write the plot routines for the Amiga format screen, which was different from the ST and we would be done. It was difficult to use the Amiga hardware sprites because the game sprites had to go between two playfield layers when the water rises, and we'd already solved that in code. We did use the blitter for object plotting. Jason Page then got all of the sounds and music on all platforms. 

    The job of converting Rainbow Islands really was a complete team effort, everyone working on their own particular area and pulling all the pieces together, sharing code and methods where required. 

    Here's the caterpillar AMP from the Insect island. The caterpillars patrol left or right and turn if they hit a wall or the edge of the level. They get angry after a certain amount of time, or if they get trapped by a rainbow, or if the "Hurry!" message appears. They can stop moving if the Clock bonus is activated. Caterpillars can walk over a rainbow too. 

    InitCaterpillar PrimeList
    Prime.w  _SpriteID,'CA' ; Series of specific variable initialisations for the object. 
    Prime.w _SpriteLife,AngryTime
    Prime.w _SpritePriority,12 ; Display layer, 0=back, then in 4`s (for table of pointer access
    Prime.w _SpritePlot,_FullPlot16 ;Plot routine - full colour,16 pixels wide
    Prime.w _SpriteDepth,1 ; More width, used for multiple sprite plotting
    Prime.l _SpriteSpeedY,0 ; Unnecessary Y speed zeroised. X speed is set by initialiser, just to left or right indicator
    Prime.l _SpriteTarget1 ; NULL pointer
    EndPrimeList _SpriteEnd ;  End of list marker
    AMPCaterpillar
    SetFlag ClockHold ;  Caterpillar can be stopped by the clock bonus
    Animate SlowTwo ;  2-frame slow animation 
    Collision ReadOnly, CSizeCaterpillar ; We are reading for collisions at the size of a caterpillar
    CVector ThumpClasses,.Hit ; If we get hit by anything of interest, such as a rainbow or a star, we spin away
    LVector .Angry
    .Happy
    MeanieSpeedX 8 ; Set horizontal speed to 8, multiplied by game progress accelerator
    MeanieSmallFace BaseCaterpiller ; Sets animation frame to small 16-bit frame left or right, based on direction
    MVector 4,.Fall ; RainbowRide could detect rainbow gone and signal code 4 to fall
    Loop Forever ;  Start of loop, unlimited
      HitPlayer ; Check for hitting player and stopping the level clock (Player doesn`t detect hit by meanies) 
      MoveUpdate ; Apply movement left or right
      BlockCheck 0,15,8 ; Check f left or right blocked characters
      DropTurnCheck 0,15,16 ; Checks for end of platform and turns object back rather than fall
      RainbowRide 0,15,16 ; Checks if walking round a rainbow. Rainbow can disappear, so error 4 generated to go to .Fall
      SlopeFrame ; If riding a rainbow, there are angled animation frames
      MapRelative ; Calculate screen relative co-ordinates from map and scroll positions
      PurgeCheck  AMPPurge, .Explode ; If we are outside screen area by certain Y amount, go to AMPPurge common routine
      MeanieSmallFace BaseCaterpillar ; Select correct direction frames for movement left or right
    EndLoop Delay ; End of loop and Delay option means end of processing for this game cycle
    .Fall
    LVector .AngryFall ; If the life timer expires while falling, we fall angry
    QuickSetSpeed 0,1 ; Zeroise X speed, set Y speed to down
    MeanieSpeedY 10 ; Expand Y speed based on game level and permanent bonuses achieved
    MVector 4,.Land ; FallCheck calls can issue code 4 to signal hit ground
    Loop Forever ; Start of loop, unlimited
      HitPlayer ; Check for hitting player and stopping the level clock (Player doesn`t detect hit by meanies) 
      MoveUpdate ; Apply downwards move
      FallCheck 0,16 ; Check ground at X offset 0, Y offset 16 pixels from top left of object
      FallCheck 15,16 ; Check for ground at X offset 15, Y offset 16 pixels (can fall through a gap of 16 pixels, not 8)
      MapRelative ; Calculate screen relative co-ordinates from map and scroll positions
      PurgeCheck AMPPurge, .Explode ; If Player gets to Goal line, we go to .Explode label
    EndLoop Delay
    .Land ; Finished falling, hit land
    LVector  .Angry ; Ready to make caterpillar angry left/right moving if lifetime counter expires  
    LocatePlayerX 8 ; Is player left or right of caterpillar? Move towards player
    SnapPositions $ffff,$fff8 ; Align X on pixel boundary, Y on character 8 pixel boundary.
    SetField.l _SpriteSpeedY,0 ; Set speed Y t o0 to stop falling.
    MapRelative Delay ; Calculate screen position for plotting, then Delay option means end of this game cycle
    Goto .Happy ; Back to patrolling left or right, carries on in same direction as patrolling and falling, no player detect
    .Angry
    MeanieSpeedX 10 ; Set speed
    .Move
    MVector 4,.AngryFall ; Now always angry, so if caterpillar falls, remains angry
    Loop Forever ; Start of loop, unlimited
      HitPlayer ; Check for hitting player and stopping the level clock (Player doesn`t detect hit by meanies) 
      MoveUpdate ; Apply movement, which is left or right
      BlockCheck 0,15,8 ; Check for blockage, left position pixel 0, right position pixel 15, Y position 16 down. 
      RainbowRide 0,15,16 ; Check for edge of rainbow to start riding over it
      SlopeFrame ; If riding a rainbow, there are angled animation frames
      DropCheck 0,15,16 ; Angry meanies check for end of platform to fall down, not turn
      MapRelative ; Calculate screen position from map position
      PurgeCheck AMPPurge, .Explode ; Meanie is removed if too far off screen, ready to be re-created 
      MeanieSmallFace BaseAngryCaterpillar ;Left or Right based on SpeedX
    EndLoop Delay ; End of loop, cycle done, pause for next cycle
    .AngryFall
    QuickSetSpeed 0,1 ; Set X speed 0, Y speed downwards
    MeanieSpeedY 12 ; Set falling speed to 12, multiplied by game acceleration based on level and bonuses
    MVector 4,.AngryLand ; FallCheck sets return code 4 if hit solid character 
    Loop Forever ; Start of loop, unlimited
      HitPlayer ; Check for hitting player and stopping the level clock (Player doesn`t detect hit by meanies) 
      MoveUpdate ; Apply movement, which is downwards
      FallCheck 0,16 ; Falling, check for ground at position left, 16 pixels down
      FallCheck 15,16 ; Falling, check for ground position right, 16 pixels down
      MapRelative ; Calculate screen position from map position minus scroll position
      PurgeCheck ; Off screen and Goal-In! checks, object is purged or explodes. Also checks for Hurry! message, makes caterpillar angry
    EndLoop Delay ; End of loop, cycle done, pause for next cycle
    .AngryLand
    LocatePlayerX 8                ; Is player to left or right from our X position + 8 pixels.
    SnapPositions $ffff,$fff8 ; Snap position to pixel boundary X, character boundary Y 
    SetField.l _SpriteSpeedY,0 ; Hit the ground, zeroise downwards speed
    MapRelative Delay ; Calculate new screen position, pause for next cycle
    Goto .Angry ; Resume walking
    .Hit
    SpinFrames BaseSpinCaterpillar, ZenChan ; Meanie spins away with caterpillar graphics or ZenChan, if the crystal ball has been collected.
    Goto AMPHit ; Common routine to spin away until landed, not shown
    .Explode
    Goto AMPExplode

    SlowTwo AFrame         0,3 ; Frame 0 for 3+1 cycles
    AFrame         1,3         ; Frame 1 for 3+1 cycles
    AEndList         ; End of list, restart at the top    


    All the above can be condensed more or less to:

    Caterpillar begins in walking happy mode (green), and walks happy until:

      it hits a wall or is about to step off a platform, in which case it turns round,
      or does not have ground below, in which case it falls, 
      or it touches the outside end of a rainbow, in which case it walks over the rainbow,
      or it touches the inside end of a rainbow, in which case it stops and becomes angry,
      or the Hurry! message appears, in which case it becomes angry 
      or its life timer counts down to zero, in which case it becomes angry
      or it leaves the screen by 56 pixels, in which case it is purged and can be recreated if it is closer to the edge of the screen
      or it is hit by a rainbow, a star, the player or any special weapon, in which case it spins off
      or the player reaches the goal line, in which case it explodes.


    Caterpillar falls happy until it hits the ground, in which case it resumes patrolling in the direction it was going left/right

      or the plethora of exceptional coditions above.


    Caterpillar becomes angry and goes red while moving left right and now can walk off the end of platforms to fall, rather than turn,

      or the plethora of exceptional conditions except ones to make it angry,


    Caterpillar falling angry until it hits the ground, when it decides to go left or right depending on which side the player is,

      or the plethora of exceptional conditions except ones to make it angry,


    So, all patrolling meanies have to act similarly and follow this kind of pattern ,though later ones can stop and fire as well. We also have flying meanies that move towards the player, wait a bit then fire, bouncing meanies like the spiders, which can spin a web to go upwards, and spinning meanies that just fly diagonally through the scenery, amongst others.

    The movements were all simple, there's no acceleration, no gravity, just constant speeds, which makes things easier.

     The beauty of these AMPS is that they can be adapted for other platforms pretty quickly. You just need the primitives code to be written. I was therefore able to provide the AMPs to the rest of the team to use. They might have to do some adaptation from 32-bit values to 16-bit, or even 8-bit.


    Anecdotes

    We spoke to other people about arcade conversions and one team told us they only received a video of a game being played through, so they coded to that video. They were then surprised later when playing the arcade game themselves that the behaviour of a big boss was completely different from what they had seen and coded as it reacted differently to the way they played. A video on its own doesn`t necessarily show you all of the possibilities in the game, especially as they get more complex.

    We hadn`t immediately realised that Rainbow Islands had 10 islands in it, not 7. Neither did the publisher and we had quoted for 7. The hidden 3 islands are even larger than the 7th, one of them has a completely different palette of colours and would have been very difficult to get into our scheme. Very few, if any graphics we were supplied were from those later islands so we were blissfully unaware until something magical happened when playing the game, he says, avoiding too many spoilers. I had some graphics for a cut scene in the 16-bit version almost until the end when we really did get short of space in the 512K base machine, so they reluctantly got removed and that ended any possibility of adding any more bigger islands. There was also another ending sequence that would have been required.  


    Paradroid 90

    After Rainbow Islands, I then took to converting Paradroid to 16-bit. The scrolling routine was suitable, given that the Atari ST was not going to do smooth scrolling sideways. I did try, I had a 2 bit-plane all-directional scrolling system, but 4 colours was just not enough for the backgrounds, it just looked 8-bit. We expanded our art department since graphics were getting more colourful and larger, needing better skills than I had. I then had some graphics artists assigned to getting the Paradroid graphics done while I was doing the programming. As a conversion, there was less design, though I wanted to visualise the robots on screen rather than use the C64 icon idea. 



    I carried on with the AMP system, so there was little use in reading the original assembler. Everything had to be written from scratch. The transfer game got some better AI strategy, the original was random. The 16-bit version had the AI player scan for the best places to fire. Maybe for the first time then, I suddenly found myself having to put my thoughts into words to get the graphics I needed from other people. I had become a project manager without realising. Fortunately I didn`t have to write reports and beg for budgets, so a rather privileged project manager who was just left to get on with the job. I was able to let the graphics artists gte on with producing the maps as well as the graphics for them, with a bit of architecture thrown in, since the lifts and decks still had to fit together cohesively.


    Uridium 2

    Uridium 2 was then another conversion, with even more graphics needed, which turned out to be a monster task. We first tried a dual playfield version, which left us with only 7 colours for each playfield and all the non-hardware sprites. 


    With other people going for 32-colour graphics, 7 wasn`t going to be enough, so we decided to go single playfield. 



    We let each artist prepare their own graphics and ship layouts, with just a degree of difficulty as a starting point. Additionally they could produce enemy spaceships and the like, and come up with ideas of what features were on the big ships. I did go back to the original C64 source code to make sure I got the control mode for the player exactly the same as the C64. Having done that, I believe I changed it a bit! Once again, I was just converting my own design, which makes life a lot easier.




    Rainbow Islands Again

    After Uridium 2, we did produce Rainbow Islands, written in C, on the PC, PlayStation and Sega Saturn. We had a programmer for each version, mainly of course to tie up the differences of the hardware, though they used the same game shell. After a fair amount of thought, we decided to use all of the Amiga game AMPs, since, as mentioned earlier, they are platform-independent. We only had to write an AMP interpreter in C and then all the AMP primitives, of which there were 208, and not many lines of code in most of them. There's a fair amount of source data and that converts nice and easily, it`s just numbers. The code conversion had all the Amiga source code available so not too bad. My part then, was to get the Amiga version running at 50 frames per second and then make a few tweaks to improve some of the effects ,mainly the one I remember is that we put the gem display off the bottom of the scrolling screen whereas they should have been overlaid over the game screen. Doubling the frame rate of the objects mainly involved slowing down the animations. Fortunately, the Amiga A1200 was now available to give the Amiga more power and actually run fast enough. I do still have a floppy disk marked "50Hz Rainbow Islands Amga", but whether it works...

    Enhanced Rainbow Islands 


    It was also part of the plan to do enhanced upgrade versions of the game, all of which had to be submitted to Taito for approval. The guys doing Bubble Bobble as the first part of the double-pack, I never knew who that was, didn`t end up doing an enhanced version. We heard Taito said "No". We had Colin Seaman create new parallax backdrops which we inserted as an extra back layer, and new versions of all of the graphics with some more colours, plus we had semi-transparent rainbows on Playstation and Sega Saturn (my idea!) and it all got approved. I thought it was a good modernisation.  


    Conclusions

    Game conversions can be from superior hardware to less, or onto superior hardware with more memory and more CPU speed. If you`ve got superior kit from the original, you can get a good version and maybe even add a few bells and whistles, whereas down-converting is going to mean compromises, maybe less colours, less objects, less levels, something may have to give. 

    There are lots of ways to do a game conversion. You can convert the code line-for-line, routine-for-routine. You can mimic the game with your own code base never having seen a single line of the original's code. Testing is a lot easier when you are familiar with the whole construction of the code so you can find the bugs. Documentation is really helpful, maybe more-so than that the source code, but the most helpful is still to have the original author tell you what`s supposed to happen.

    Having a good team around you helps to solve problems and get the job done. 



    0

    Add a comment

  7. Introduction

    I began to read up on PC development for a new game, written in C, in 2016. I bought a book on DirectX 11 and downloaded the DX 11 SDK and some demos. Knowing also that Direct X 12 was out and being incorporated into the OS, I tried to get my head round that too. I initially thought it was quite helpful that I didn`t know too much about Direct X 11, so the big differences new in DX 12 seemed largely irrelevant, I would start with DX 12. 

    I downloaded some more demos and the DX12 SDK. 

    There was then a short pause. It got longer.


    The Entertainment

    I mulled over my desktop positioning in the living room. It`s positioned against a side wall, behind the sofa. It is not in an ideal location for listening to the stereo. Way back when, in the days of Amiga development in the Graftgold offices, we built a stereo from spare components so we could listen to music in the office. I don`t know whether it helped us, it certainly didn`t hinder me. I can concentrate on my coding to the point where a CD would finish playing and I had no recollection of it even being played. Nevertheless, I think in those moments where I wasn`t concentrating, the music helped. The only time where the music was not a help was when the sound effects were being tested. This was usually late on in the project when we could identify all the events we needed effects for and get them all done at once. A play-through would then be needed to balance the volumes and identify any weak points that needed a sound, or annoying places where a sound effect might be too loud or monotonous.

    I digress. I like the option of listening to music while programming, and my computer desk was not in the optimum position, it's not really living room material. The solution, as it turned out, was to buy a reasonable laptop and then I could sit on the sofa and program in the Dobly Sweet Spot, as it were. I still used the desktop for graphics work as I had my old art package installed and it refused to install on the laptop: wanting to install IE6 when IE11 was already in residence, which bailed the whole installation process. I also did some software development on the desktop, I mean, why get up, right? That did lead me to a couple of tricky days where I updated two versions of the code with some improvements and then had to merge them back together. Always nominate one machine to hold the master copy and keep the others up to date. Merging by hand is not fun and takes ages. I also steadfastly refuse to use a source repository since I am not a repository administrator so wouldn`t be able to dig myself out of a mess.


    Directly Stuck

    So, here we are in 2018, it's been nearly two years of thinking about stuff and I now have a laptop with Visual Studio 2017 installed and am ready to begin. I was/am baffled by Direct X (all of them) as, although I understand the principles of how shaders work, I don`t really understand the nuts and bolts of how it all holds together. All I know still is that DX11 and DX12 do it differently. I believe that getting one line of code wrong in any part of one's shader implementation will result in not seeing anything on the screen and be a complete nightmare to work through. I was advised to have a look at some ready-made libraries of code that would allow me to link to their already written and working code at a higher level so I can just concentrate on writing a game. Being a lone developer means being less ambitious and not trying to compete with the A-List games written by teams of hundreds of people.


    The Start of Something New

    Top of the list of libraries to look at was SFML. They have written graphics and sound routines, amongst others and it seems quite easy to link to in dynamic mode at least. I never did get it to link in static mode and since I had a working dll implementation, I decided not to fight the static mode. I did want to split my code into a generic library of useful stuff for more than one game, which I named ABLib. I then wanted some agnostic code for the main game that was platform-independent, and finally an outer shell that gets things started up on the chosen platform, currently PC, though a plus-point of SFML is their mobile implementation, and also Visual Studio supports mobile development, somehow, I have not investigated this deeply yet. I will need to find out about developing for Android and/or iOS. 


    ABLib

    I had already begun to write new material for my ABLib project, which needs to not know anything about the OS, not know anything about SFML, just in case I need to swap to another library. Way back in the Amiga days, we had a fairly organised system for maintaining game elements, or objects, which performed such duties as movement, graphic animation, plotting on screen and collisions. That was called our Alien Manoeuvre Program system, or AMPs. We used that for Rainbow Islands, Paradroid 90, Fire & Ice, Uridium 2, and Virocop, so it seemed to work, plus it made converting to other platforms quite straightforward. We even managed to use the Amiga Rainbow Islands data to create the Rainbow Islands updates for PC, Sega Saturn, and PlayStation, upgraded to 50 frames per second. I also have an Amiga 1200 50 frames per second Rainbow Islands that I used to test the data, at least that's what it says on the floppy disk. I haven`t dared try it as it's the only one and if it doesn`t work I'll be terribly disappointed. I digressed again, sorry.


    The AMP System

    The new AMP system I wrote needed to do everything the old system did, plus more, plus improvements to areas where I would have liked to improve the original, and minus the things that annoyed me about the old system. Since I'm writing in C, the whole codebase is brand new. The AMP system is data-driven, instructions are effectively just numbers, indexes into a list of routines, with arbitrary parameters, all created with C macros. We used to use assembler macros, which are much nicer, allowing temporary labels within lists, and data can be added to different tables in one macro, so there were some types of instruction I could no longer use, like forward references! So I decided I wanted the new system to be better and had to think how I was going to get C to jump through some extra high hoops. I put a little stack onto each object's structure so I could have call and return instructions in the AMPs, plus there were a number of different sub-systems that might interrupt the flow, and they might all kick in at once, so there is the option to stack them and deal with them all, plus some naughtiness where I know we're not going back, ever, so clear the stack. The stack also controls loops within loops, keeping the counts on the stack as well as the top of the loop location. I'll show some later. The benefit of the AMP system is that when you want to write the control routine for an object, you write it from the point of view of the object, there's no Mode variable that needs to note what you were doing last time, you just carry on the program from where you finished last time.


    AMP Language

    I was testing the AMP system blind for a while, since I didn`t have SFML yet, and that encouraged me to write quite a comprehensive logging system for the AMPs. I could get any specific AMP to switch on logging and it would note down what it was doing every cycle. We didn`t have anything like that on the Amiga. Naturally too many objects spitting out log information all the time slows it down a lot! However, there are times when you might need to know what's going on in there, and while I didn`t have any graphics to show then it was fine. This allowed me to implement some simple language commands such as loops, calls, returns, and interruptions. I do have simple Gotos as well, where you might want to initialise 3 different types of object that all behave the same way, so they then Goto the common code. Yes, I could call it, but we're not coming back and the stack has only 16 entries. Due to the extra pain of C needing prototypes for forward references, I don`t do forwards Gotos, and try to keep them to a minimum. Calls only go upwards too, for the same reason. I got used to writing "upside-down" code a long time ago with C, so if I write a function that wants to call a second function, I'll put that second one above the first. No function prototype needed. Also due to C not allowing temporary labels in the arrays of AMP instructions, it makes things rather more disjointed. I have not come up with a satisfactory way of doing conditional chunks of code yet. I would need to have that code scan ahead, looking for matching elses and endifs, which can be embedded. Sounds inefficient and messy. C pops in some jumps in the machine code, but doesn`t allow me to!


    SFML

    SFML was pretty easy to set up and is quite well-documented online. I looked at all of the sprite display features they supported, and added variables to my objects that would drive those features. SFML is a 2D sprite-based system, we will be needing to dig into OpenGL if we want 3D, but it allows me to plot sprites, with rotation, scaling, colour tinting, and transparency, and it can do thousands of them at 60 frames per second, so I'm happy with that. Of course the exact number depends on the individual PC and the designated screen size. CPU time seems to be the limiting factor rather than the graphics card. I can also display True Type fonts, and have then scaled, rotated, coloured, outlined and transparent. By also creating variables that allow me to control the colours, the scale, rotation and transparency, I can do all of the bread-and-butter effects I need. I do miss the old colour palettes that we used to have as that gave us some indirection, we could change one video chip register and everything on screen of a particular colour would change, great for flashing lights or fading the screen. Now we have to build the whole game picture from scratch every frame, or sixtieth of a second. 


    Early version with a C64 Manta 


    Pyxel Edit

    Graphics are easy enough to get into the game, once I found Pyxel Edit. This allows me to draw graphics down to pixel level, including fully transparent pixels so we can draw shapes. My, those pixels are small. My first graphics attempts were still done at Amiga kind of sizes, but screens are now 8 times the pixels wider, so they were really small. One also has access to nearly 17 million colours, but who has the time? I could cut out and reduce elements from my digital photos, which is the only way to get the quality. The graphics get collected together on sheets, console-style texture sheets, and loaded in. I can`t draw to photo quality. I also wrote a function to soften the edges of all the graphics as I load them in, which gives them anti-aliased edges. Takes longer to process but it smooths the edges. I also wrote another routine to smooth out the colours a bit, effectively anti-aliasing internally, or even blurring slightly. The pixels are so small it's tough to see any difference, but I know it's there!


    Temporary new player legends in nice font not licensed for commercial use


    Sounds


    Sound effects are simple enough too, now everything is a sound sample. I wrote a routine to load all the sound samples in and then designed a data table of sound effects. Thinking back to my PC game programming time, I wrote a 3D sound routine that would position the sound on the soundstage and adjust the volume for distance. SFML does all that, but I had some other features such as varying the pitch a bit to make the sounds slightly different each time. My old PC routine also accounted for distance delay and I was doppler-shifting the pitch by movement so that planes would sound more realistic. I haven`t done that here for a 2D environment, though I note that I do have a 3D co-ordinate system and I could drive surround sounds. I will need more speakers on the PC for that... 


    Star Trek font, but no punctuation. Debug targeting legends


    What to Write

    Having got the basis of a game engine, which is what the AMP system is, I needed a game to write. All of the original games I wrote from Gribbly's Day Out on the C64 to Uridium2 on the Amiga, weren`t just written, they were designed, programmed, debugged, tested and tuned, plus I did do a few graphics myself too, along with the occasional Paradroid sound effect, not forgetting that we did a certain amount of promotional work too. Of course the later designs had inputs from the larger team we had. What I needed now was something simple to do that wouldn`t be too taxing, and wouldn`t need a lot of graphics. I had no publishing ambitions, I was just doing this for my own amusement. Some 3 or 4 years earlier, I had encouraged Steve Turner to write a game after his retirement from the software company where we both worked. This project is nearing completion as of the time of writing (Sept 2022), currently called "Deepest Blue". That amount of effort he's put in made me think I should try and take it a bit easier!


    Game Shell

    The outer shell is the start of the game and is platform-specific. If you can keep all the OS interactions in the one outer file then that's the only one you need to change if you want to port the game to another platform, maybe iOS or Android. The inner workings of the game then do not talk to the OS other than to write out files where the outer shell directs them to.

    I first had to create the ubiquitous game loop that runs at 60 frames per second in a double-buffered manner, which means that the graphics for the new frame are plotted into a hidden screen copy and then displayed as a complete picture while the next frame is worked out. This avoids any unpleasant screen-tearing effects or flickering. We might have done that in the C64 days by carefully arranging the scrolling and plot routines so that the work is done while the raster display is in front of the plotting, or behind. The AMP runner routine is then called once per frame, which moves all the objects, does any animation or other effects. 

    To get the objects plotted on screen in the correct sequence, I assign the objects to one of 32 layers and all the objects for each layer are threaded through one of 32 plot linked lists. The objects are then plotted layer by layer, so we have the backdrop on layer zero, then the game layer is on about layer 5. The layer number can be set from the Z co-ordinate, with X being left to right, Y being up to down, and Z being front to back. No idea if that is correct for maths matrices, but since I'm not using any, it doesn`t matter. My collision system is forward-thinking and coded in 3D, so game elements have to set things correctly or they'll miss, but I can have cosmetic items going back into the screen or forwards out of the screen. This is most effective for text messages that I can bring in by fading them in and then enlarging them and moving out of the screen and up through the plot layers. I do have a prototype for another game which is top-down view and features both walking and flying objects and we have ground to air weapons, air to ground weapons, etc etc. that use the Z depth for collisions. Plus I can do shadows under the objects. Incidentally, I still haven`t thought of a way of doing a detailed space background with objects casting shadows onto other objects, but not onto space. The C64 did allow me to do that by arranging the colours carefully. So that's progress.


    Legend font changed again. Big explosion test.


    Lights and Sprites

    A sprite is a 2D graphic. Back in the 8-bit days, the C64 was able to plot 8 such sprites with its VIC-II graphics chip, all done largely for free. The images are read from memory and displayed over, or maybe between the background colours without actually being incorporated in the background data. The Dragon 32 and the ZX Spectrum did not have this advanced feature, so we had to combine the graphics data with the background data using code, and then clean up the mess as the sprites moved around, which all took a lot of additional time. .

    In the 16-bit days, we tried to plot the background on the screen as little as possible, cleaning up after the sprites have been shown on screen. Rebuilding the whole screen image from scratch every frame was rather labour-intensive and certainly not going to be possible at 50 frames per second except by reducing the working game screen size or the number of colours. 3D games did rebuild the game picture every frame, but at the expense of running quite a lot slower than 50 frames per second. Modern games do tend to do a full screen rebuild every frame as shaders provide fast ways to plot pixels on screen in parallel with lots of processors. This also helps with windowed products where multiple applications might be overlaid on one screen.

    The SFML sprites are quite straightforward to use. I can set position, graphic, colour tint, transparency, rotation and size and by holding and manipulating these features on my AMPObject structure, I can transfer them to the SFML sprite variables and it does all the hard work. I did an experiment to run each object with its own SFML sprite, and then again through one single SFML sprite. The call to plot must transfer the info to the graphics card in blocking mode and then let the GPU get on with the work as both methods work. Now I load the graphics info onto each object's sprite only when it changes image, which saves a bit of time. Just about everything else might change and the cost of checking if it has changed is more than the cost of copying the variable across. That's a Nike moment: just do it. The first plot of the whole background layer from one giant picture will take the longest, but it doesn`t appear to take very long and all of the plots do appear ro occur in the sequence I intend so there appears to be an organised plot queueing system on the graphics card rather than a parallel free-for-all.


    High score table overlay with many font sizes


    Sound and SFX

    The SFML sound effects are also pretty simple, once you get the soundstage scale of things sorted out. Sound effects are assigned a 3D position and use the X co-ordinate for panning and the Z co-ordinate for distance. The soundstage needs to be defined to give it some scale, which depends on what you're trying to portray. One can even drive the sound in 3D space once it has started, thanks to Jason Page for pointing that out to me! I can define a number of virtual voice channels and assign any effect to any channel. That is nice for isolating continuous sounds where they can be looped, or I can keep channels for specific purposes, such as each player's firing, and route all the explosions through one or two channels so they don`t build up too much. There is even a feature to load big background sounds, or even whole tunes, from disc on the fly. Sounds can be stereo or mono, so it can move the mono ones about in 3D, but the stereo ones can be for announcements like "Game Over". 


    Backdrop gets a respray. More smoke effects


    Character sets

    I've tried a number of different fonts in the game over time. I need a simple fixed-width font for the player legends and scores. When displaying number that change, we don`t want a narrow 1 making the number wobble about while it's counting. That's one of my pet hates. I had a couple of attempts to find a simple font that had all the required punctuation and a free-to-use licence. Player names can be input up-front on the config screen or in the loaded language file, if you're careful. Putting the player names in up-front means that the score legends can display your names, the game can refer to you by name during play, and you don`t have to enter your name every time you hit the high score table. I allow 11 letters for the name, which covers most forenames I can think of. It would have been 10, but Christopher Smith came along. I figured it would be nice to allow hyphens and apostrophes as well, though these are mostly in surnames. Then I needed a bigger, prettier proportional-width and equally free-to-use font for the other game messages. This too needed apostrophes, hyphens and later I found I needed the awkward underscore character for use as a cursor when entering names. A lot of TTF fonts don`t have that character in the set. A lot of fonts are licensed only for use in documents too, not in commercial games. 


    Testing without the backdrop refresh

    Game Types

    I decided to support multi-player modes in two ways since SFML supports multiple game controllers. It can let up to 4 players play consecutively, preferably with a controller each, otherwise there would need to be controllers swapping hands. It can also allow up to 4 players to play at once, co-operatively, definitely with a controller each. I haven`t rounded up 3 other people to really try that out. Steve Turner and his son tried out the 2 player simultaneous mode. Since the players can`t damage each other, which has to be, as there is so much ordinance flying about, and shields can protect the players from rocks, physics can start to bounce the player ships all over the place. This did cause some merriment. I don`t think that`s a bad thing. There needs to be co-ordinated cooperation. Spoiler alert: the demo ship can sometimes join in the fray and help out, which can also cause trouble. The demo ship has limited shields so can be destroyed by the players with a few shots if it`s being a nuisance. I have even seen two demo ships helping out at once. They can survive for a fair while if there is not too much to crash into.


    Tuning

    I can run the game in a window or as full screen on a PC. The game reads what the desktop resolution is and uses that for full screen mode, or a little bit smaller for a window. I covered all resolutions from about 800x600 all the way up to 1920x1200. The latter is the top resolution that my desktop could do, and a little more than my laptop. And I would have gotten away with it until a certain Mr Page had to have a 4K laptop. 

    I set about tuning the game so that it never got too overwhelming. When playing an early version with higher limits, I could feel when the game got too hard and the end was inevitable. I also noted that that point was different on different sized windows, so I introduced a variable that was the percentage of the biggest size screen so I could scale the maximum number of objects limit. I also then had to increase my maximum window size to 4000x2400. I don`t scale the game or the sprites, I just let the game run in a bigger space. It does change the whole game feel because one has to chase stuff a lot more on the bigger screen, whereas one can just wait for objects to come to the player on a smaller screen. I will have another issue when 8K monitors become common. We have now resolved the issue of changing graphics modes on the desktop. There's a pesky option in Visual Studio that is defaulted to disallow any screen graphics mode changes. Why would they even have that, let alone set it to disallowed by default in the RELEASE build? Or at least draw my attention to the fact that they've blocked my request. It's in VS2022 and VS2019, never seen it before. So finally my efforts to find out all the supported video modes and pick the most suitable actually can really change the full-screen graphics mode. That's a weight off my mind.


    Moving Starfield looked quite interesting 


    Photographing Pebbles

    It was a couple of months getting the graphics together. I sought out some images of real asteroids on the Interweb and shrunk them down to the sizes I thought I needed. These early attempts turned out to be miniscule on the screen. Pixels have gotten so small since my C64 and Amiga days where a screen was 320 pixels wide. I had two further enlargement redraws to get the scale right. Photos of real asteroids are pretty blurry to start wth, so I decided to go out into the garden and find my own asteroids. I found some suitable pebbles and photographed them on a black card background, transferred the photos to the PC, then greatly reduced them to the resolution I needed. This gave me better quality graphics. I can still tint the colours for a bit for variety. Getting multiple pebbles the same colour is tricky anyway. One can photograph the same pebble from different angles to get different graphics. I'm sure it would be lovely to make a movie and get all the images of them rotating in 3D, but I don`t have the mechanisms to do that, which would be a 3D package and creating rocks, then generating a heap of images They look fine rotating in 2D.

    I also needed some space ships. I haven`t got any of those in the garden to photograph. I decided to just pixel bash some space-ships. I had the same initial problems that they were too small at the first attempt, and the second. We just love to do the same thing over! 

    I remember from my movie books that for space ship models they did 2 or 3 film passes for different elements. I therefore did a main graphic image, a lighting overlay, and in some cases some contrasting colour overlays. I found a graphic of a car wheel that I repurposed as the top of a space ship and I found a picture of a rocket engine that showed me how to draw a nice shiny rocket cone. Sprites are suddenly cheap, using 3 for one object is not an issue. I also started building up fire and smoke effects with multiple partially transparent images so I can have engine flames and glowing shields. Mercifully, the quality of graphics has been quite small as they are all collected together on one graphics sheet, other than the backdrop, which is a photo I took of the Milky Way one starry night. One can get animations by rotating objects, fading them, combining them or just using lots of them. Simple maths and experimentation are used instead of graphical drawing talent, which itself might not be enough given that the photographed images look much better.   


    All-Nighter

    I then had a couple of days where I was determined to get the bones of the game together. I worked all day to put in the necessary game code and then ploughed on through the night. Big changes sometimes require the big effort. It was dawn when I got all the required objects moving around. I looked up the equations for spherical billiard balls colliding in a vacuum and got the rocks all bouncing off each other. I could assign them a mass figure so it's better than billiard balls as we can have different sizes with different masses that cause some realistic looking effects. I rigged up the animation system, which was originally solely to drive graphics changes, to do scaling effects so I could introduce a compression effect to raise the X size and reduce the Y size a bit, then swap over a couple of times before restoring the shape. It could be taken to classic cartoon levels if required. The animation system was also expanded to do colour tint changes. I draw a lot of images in greyscale and then tint the image to get colours. This makes the objects look a bit too flat, so an un-tinted overlay with a bit of different colour on it can help a lot. The animation processor runs concurrently with the main AMP processor. The AMP processor can select animations to run, and the animation can cause interrupts to the AMP processing when it is done.


    The Waterline

    I also wanted some nice big multi-part explosions that spray stuff everywhere. We can have bits of shrapnel, fire, smoke and fireworks. Wary of the fact that different PCs have differing capabilities, I rigged up explosions to be some mandatory pieces and then, if the machine has enough capability, more optional pieces are let loose. I monitor the number of objects being plotted every cycle and I note the finish time under a 60th of a second. If we finish in good time and I have plotted a few more sprites than my starting performance guess, what I am calling the waterline, then I can push the waterline up a bit. If I get dangerously close or even over the 60th of second, the players will notice a judder, so I bring the waterline figure down a bit so we don`t start as many objects in future. The waterline usually settles down after a good game and is saved out for future reference at the end of the session. There's a minimum waterline that I define below which the game couldn`t function, so if the waterline hits rock bottom, it may start to dig and judder if the PC isn`t up to it. There's an upper limit of however many sprites I have chosen to allow for. CPU time is what runs out before GPU time, it would appear. I am getting quite different figures for different games, My desktop can run about 5,000 sprites, at which point the machine CPU shows 25% busy, so one core is flat out, 3 are idle, and the 970 GTX graphics card is not panting. If I can get a second CPU helping out then it'll be very nice indeed. In any case, computers are getting faster at a quicker pace than I am writing code! The games work fine on 10-year old hardware.

    Debug info shows waterline of 1580 AMPs


    Sounds

    I have a set of 5 CDs that I bought ages ago with sound effects on them. I managed to isolate some individual effects from the CD tracks that tended to contain multiple effects. There are also plenty of websites with free sound effects. It just takes a while to listen to them and pick out what yu want. It's important when mixing all of these different sound effects from different sources that you can set the individual volumes of all of the effects, as well as then having your choices modified by the 3D sound player. Way back when, before we had samples, we just had to choose one of the available waveforms and then control the frequency and volume changes in real time. At the time, I wasn`t able to think about how a sound I wanted would be formed with those algorithms. I still wouldn`t want to try it! The sound effects routines were quite involved, making changes to the effects on up to 3 voices every 50th of a second, though I believe some people ran more than 1 call per 50th of a second for better control. Now, we just need to call a function to say: "Play this sound sample we prepared earlier. Position it here on the 3D soundstage. Go!" Of course there were some clever people who managed to fabricate extra voices out of the 3 or 4 available on the hardware by mixing their sounds or samples together." There's always a way for some clever person to push the envelope.


    Monitor Sizes

    Asteroids is one of those games that just carries on behind the titles after the player has lost his or her last life. I was concerned as to how much time it might use displaying a lot of text in front of the still-running game. It doesn`t seem to bother it too much. I'm not sure how SFML does it, possibly it pre-renders the font in the desired scale to a spare sheet. If so, I must be driving it mad by scaling the fonts in real time! I did note that every time I chose a new font, the sizing and spacing changed. Text that used to fit suddenly didn`t. The old issue of variable screen sizes cropped up. Rather than resize the fonts based on the screen size, I decided to just set the screen positions with percentages of the screen size. This bunches the text up on a small screen and lets it spread out on the 4K screen. This approach at least means I don`t have to check everything on two dozen different screen sizes, I can just set a minimum size window and roll out the 4K monitor for the biggest. Yes, I had to buy a 4K monitor to investigate why my game, when the biggest screen I could conceive of was 1920x1200, decided to carve itself a 1920x1200 space in the top left of the 3840x2160 laptop screen. Since I was kindly informed of the filthy DPI Awareness setting in the Visual Studio project that by default forbids me changing the graphics mode, I have set it to "Per Monitor High DPI Aware" and I can change the graphics resolution in full screen mode. Basil Fawlty might add: "Thank you so much." 


    Titles Sequence

    I constructed a fairly lengthy titles sequence with a titles page, a credits page, a high score table, an intro to the space buses and a demo screen. The game objects all continue. There is an orchestrator object that can generate game elements and text. Incidentally, I now hate the word "object" in the computer sense in case anyone thinks I am writing object-oriented code. I'm not, I don't understand all the extra punctuation used in C++. I can just about do stuff with the SFML C++ objects as long as I have an example to work from. My objects are just an AMPObject structure that lives in an unused object list until activated, when it moves to the working objects list and has over one hundred variables for what the object is doing in the AMP language, and then variables for rendering as a sprite, or text, just a controller of some kind. Every variable is 4 bytes long, whether it be a pointer, an integer, a floating point number or a group of 4 bytes, e.g. for colour. I looked over some of our old code and was amazed at the number of different data definitions we had, which resulted in lots of casting all over the place (which I liken to switching off the safety circuits). I have tried to simplify things as much as possible, though I still occasionally need to interact with other systems that have their own data types.         


    Titles sequence with moving starfield


    DEBUG and RELEASE compilations

    By the end of 2018, I had the project pretty much completed. Mercifully the code was pretty stable at all times, no surprise crashes, at all. I try to write defensively so that there is error checking in the DEBUG version. Just to clarify for non-programmers: the modern way to develop is that the program is compiled (C programs becomes machine code) in a DEBUG configuration first, which allows us to place breakpoints in the code to stop it where we want, trace the code one instruction at a time and examine variables and memory to check that everything is working. We never had any of that back in the 8-bit days. When the code is working nicely, we can compile it in RELEASE mode, which allows the compiler to optimise the produced machine code to work fastest, at the expense that the code can be re-arranged and is therefore not debuggable with the tools, potentially we might not even recognise it! We can also put in pieces of code that only work in DEBUG mode, such as producing log files and messages that we don`t want the final end-user to see. Generally, it's just extra tests and checks we would have in the DEBUG mode to make testing easier. This might also include cheat modes or start-on-later-levels menu selections.


    IBM Error codes

    Occasionally a log file or 20 might be produced that tells me an AMP object is unhappy about something. It can generate a warning situation, the lowest severity, which I might class as: you might want to know about this. If I have set up a routine to call to deal with it, then it's all quiet. If there is no routine then it squeals, as something has occurred that is not being acknowledge nor dealt with, which might be more serious. For a warning, should it really do that? I ended up hooking up an empty call so that I acknowledge the warning and carry on, rather than nobble the AMP routine and say that warnings are OK from now on. In this particular case, the space buses generate a warning when they go off screen, which signals that they have completed a pass across the screen and should be removed. Sometimes though, they come on and turn the wrong way and go off again very quickly, which isn`t satisfactory, so I let them go off one side and come on the other, at which point they do generate a warning to let the AMP object know that it has occurred.

    Incidentally, I still base my AMP error codes on the old IBM mainframe standard I learned in 1979:

    0 = OK

    4 = Warning

    8 = Caution

    12 = Error

    16 = Disaster

    They likely implemented a jump table and used the error levels in 4s to get the byte offset into the jump table to get the error handler jump address. I have expanded the error codes that I use as the above list is all the main errors that I can have handlers for,. I can also have interruptions from the animation sub-system, the collision sub-system, and parent life signals to deconstruct multi-part objects. There are extra negative codes I use for complete language failures such as the AMP interpreter going rogue with bad data. These are clear failures in the AMP that are unexpected and can`t be sensibly handled at run-time. The AMP system logs it and kills the object. Naturally, you might get a few hundred of those if there is a programming mistake. Usually the first sign is that the game stalls badly as logging all that info in error files takes time.

    Here's a low-def snippet of the game running, filmed with my mobile phone. I can`t play and film, but it gives a flavour of what's happening on screen.



    Back to Work

    Writing games can be a job, but is it work? I always said it was the best job ever. Didn`t feel like work, It was also best when I was doing all the graphics in the 8-bit days as I got to do more variety of things and didn`t have to rely on anyone else, except for the sound and music, of course. Not to say that handing over graphics duties wasn`t interesting too, as I got some quality work that far exceeded my abilities. The quantity of work that goes with co-ordinating all of that is also a lot of work. Suddenly one becomes a project manager as well. That's a tale for another time.

    I went back to work in 2019 and game development slowed down a lot. In 2020, everyone was sent home to work because of you-know-what, and I continued working part-time until early 2022. I was still tinkering with the Asteroids project while also working on another game project. They both share the ABLib library of code. Every now and again I feel I have to alter some shared code, which then necessitates at least a re-test of both projects, and sometimes the project code will need a correction, maybe an extra parameter is passed around. A shared library is a double-edged sword. On one hand, you only need to fix errors in one place, but on the other hand, compatibility can be broken by a change. It's a fine line to tread. I fixed a few bugs I had not identified earlier. No matter how careful one is, there can be little issues show up which mean that something isn`t quite working as intended, even though you thought it was. Shared code in another project can uncover these when maybe I use something in a different way, or make an "improvement" that has unforeseen consequences. 

    A set of unit tests is only as good as the tests you write, and would become an ever-larger additional lump of code to maintain. I like to think that I test new code thoroughly at the time of writing and once I trust it then I can leave it alone. Code doesn`t deteriorate over time, one's understanding of it might! I don`t want to expand the capabilities of a function and then have to rewrite the tests that I will have forgotten about. Good tests don`t just check for getting the right answers with the right inputs, but should also fire in wrong inputs to make sure nothing collapses.  


    No Release

    It doesn`t sit comfortably with me to release a game that is a tribute to another company's game. It is not exactly a copy, but could be construed as passing-off, which is not my intention. It is therefore currently just a learning exercise for me. It is a good kicking off point for other projects as I now have a working codebase. It is also a reasonable example of the quality I can achieve, for promotional purposes only. I have some ideas to create a bigger game, based on an old COBOL game I designed in 1981 for the mainframe ,called "Navigate". I also implemented a tile-based playfield system so that I can do scrolling background games, so I have plenty of directions to go in (pun intended!). The issue with scrolling backgrounds is that there needs to be a lot of graphics, probably with a mapping tool as well. Plenty of work generated there, then!


    Next Time...

    Part 2 - Creating a new game. Also, a more in-depth look at some Alien Manoeuvre Programs (AMPs).


         

    3

    View comments

  8. Introduction

    It is early summer, 1985. Paradroid was the one game that I mostly designed up-front, thinking about the game on the walk home from the "office", Steve`s dining room. Arriving at home, I wrote everything I had thought about down on a single sheet of a blue note-pad I found next to the `phone.

    Over the next 4 or 5 months, I set about putting a game together based on the words hand-written on the blue sheet. The game was going to be built from the embers of my previous game, Gribbly`s Day Out, stripped down to its bare essentials, so much of the technical work was already in place: a multi-way scrolling screen, space for 16 levels of background data, 2 character sets, 1 for the font, 1 for the backgrounds, and 176 sprite images maximum.


    The Blue Sheet

    So, without further ado, here is the blue sheet, transcribed as written:


    Cute and hi-tech don`t go together.

    Instead of robots, just use the digital

    specification numbers as per fighters in

    Lunattack!


    Player has access to detailed data

    specifications of robot.

    Player controls an "influence", which may

    be transferred from robot to robot at a

    cost to the source robot`s energy of a 

    "takeover" or "dominate" cost of the robot

    to be taken over. The reverse process will

    be possible, provided sufficient robot

    energy is available.

    The new robot`s energy value will not be

    known, of course, until transfer is 

    complete.

    The weak robots cannot, say, take over

    the strongest, but have to climb a flexible 

    ladder in stages.

    Build a picture of robot with data from

    bolt-together pieces.


    Each robot has:

    Internal energy for all functions,

    Dominate value, based on robot`s

    intelligence and power,

    Security class (Privilege) - allows access

    to computer data, security areas, etc.,

    Armaments, or none,

    Mobility, maximum, but degraded by damage,

    Armour, protection from shots, not 

    usually able to withstand 1 direct hit,

    Other miscellaneous background data,

    e.g. year of manufacture, model no.


    Types of robot:

    Menial droids, Personal servants,

    Protocol, ship maintenance, security 

    robots, battle droids, command robots.



    The original blue sheet got a dose of water damage in storage when the roof leaked. It is mostly unreadable. It was lucky that it was fully transcribed in Zzap!64 issue 6, October 1985. 

    The specification

    The day hadn`t gone well as I had been tasked with coming up with a scenario  that might use a cute robot. I had grappled with that idea and decided I couldn`t do it. Steve Turner later did produce both Quazatron and Magnetron with exactly a cute robot, so what do I know?

    Lunattack! originally programmed by Steve Turner for the ZX Spectrum in 1983/4, and then converted to the Dragon 32 and C64 by myself, featured enemy fighters that were identified by radar and plotted initially as just a distant dot with a range, which acted as a countdown before the fighter came properly into view. The player ship had long range missiles that could be launched at the fighter plots, or they could be shot at with guns at close range. Pre-dates Top Gun, the movie, but not the quote: "Too close for missiles, I`m switching to guns!"




    In Lunattack!, I had used the hardware sprites for the fighter plots, so was already copying digits into sprite data, rather than drawing all the different sprites with all the numbers in advance. Therefore drawing robot numbers into sprites was also going to save me sprite image space, and I could show the robot energy levels with moving graphics too, though the blue sheet suggested not to do that. 

    Having learned my first computer language, COBOL, on a business mainframe, I wanted to show robot data to the player by means of console terminals within the game. I wanted to compress the data by having a dictionary look-up for words so that I just had to list the word numbers for each robot`s description. I figured this would save me space, but never did the maths to see how big the word decode routine was. Similarly, I wanted to build the robot images out of up to 8 source sprites. I chose to use some of the same parts for different robots, which is good to show consistency, and sometimes I could get away with using reflected sprite data for the right side of a robot. Again, I had to write a reflect-a-sprite routine, so did I save much space? Right now I'm also wondering if I had all the sprite data for the robots in the 16K video bank, since I really only needed 8 sprites there and could copy in what I needed.

    Influence.

    The blue sheet just talks about an influence, which must have been turned into an Influence Device later on, presumably so it gives it more of a reality. It needed to be something tangible so that it could be vulnerable and therefore destroyed. The player is never far from Game Over as you only have a maximum of the robot you`re controlling plus a dog`s chance if it gets destroyed.

    Domination.

    I decided to make the strength of a robot very clearly visible as the first digit of the 3-digit model number. 1 are the unarmed service droids, 4 are the maintenance droids, 6 are the sentry guards, 8 are the battle droids and 9 is the command cyborg. The higher the number, the harder it is to take over. The transfer game was not in the original design, but when it came into being as a shoot-out, the strength of the robot decides how many shots you get, for both sides, so if you are a big robot taking over a smaller one, you will have more shots, and therefore a better chance. A menial robot taking over the cyborg will have many less shots, but still an outside chance if the player is clever and patient.

    I chose not to bring the robots` energy into the calculation of how many bullets you get. It might have been a clever way to damage and then take over a bigger robot, provided you can then get it to an energiser to restore its power. All that might be time-consuming and tedious. I did though, need a way to at least show how much power the player had, with the speed of the rotation of the top and bottom of the robot icons, and it seemed useful to show whether the other robots are being damaged, since some of the highly armoured robots are not in the slightest damaged by smaller weapons. Robots, and the player, are damaged by explosions, though not so much if the robot is well-armoured. Many players do appear to walk straight into an explosion and are puzzled that they take damage and blow up sometimes. Don`t be puzzled by this.

    Security Clearance.

    I also used the robots` first digit as their security class, keeping it simple, so that when they access the console terminals, the player can see the details of all the robots up to and including their own by just comparing the robot numbers. If you want to know about the 999 cyborg, you have to BE the 999 cyborg, and you can see all the other robots too. That gives new players a learning curve that they can gather all the information as they get better at the game.

    Mobility.

    I did want to degrade the mobility of the robots a bit when they receive damage, but that isn`t very helpful when you`re on your dog`s chance and want to just scuttle away and re-energise. Moving slowly might prevent you from getting to an energiser. Maybe now I might at least visualise such a situation with sparks coming from the robot to show it is damaged.

    Later Additions In-Flight

    The blue sheet was a document of the new ideas I wanted to try out. Here are some of the features that were thought of before or after.

    Something that came along after then, was the transfer game for fighting for control of the robots. The player gets to pick which side they favour of the randomly generated circuits, requiring a bit of quick analysis. This gives the player a bit of an edge when random chance does not operate in your favour and gives you a bad circuit. Give it to the other guy! Having decided I wanted to try that, it took me 2 weeks to design the admittedly simplistic graphics and code the transfer game. It worked pretty well from that point, I just tuned up how many shots you get slightly, it`s something like 3 plus your robot`s first digit of its serial number.




    The feature that only shows robots that you can see by direct line of sight was from my earlier COBOL mainframe game Survive aka Assassin. The multi-deck claustrophobic enclosed scenario was also from that design.

    I had always wanted the ship to be a cohesive design, with lifts that matched up so that the player doesn`t get disoriented. The computer consoles can be accessed to show the deck and ship layouts so you can check where you are.

    The idea of the player's maximum energy level going down with time was just a simple mechanism to hurry the player along. It fitted the scenario nicely that a robot would attempt to fight for its control back, ultimately to its own self-destruction. That keeps the game moving.

    I added the alert status feature to give the player more incentive still. If you can destroy a number of robots quickly, the bigger the better, it can raise the alert level from green to yellow, to amber to red, awarding extra points every few seconds according to the alert level. The alert level goes down over time.

    The game awards negative scores for being rejected at transfer. This led to recording the lowest score of the day as well as the highest! 

    Paradroid 90 on Atari ST and Amiga.

    The game design was revisited in the 16-bit age. I didn`t change much of the design. We reduced the number of different robots a little as we were visualising them all properly and graphics space was getting tight given that we needed some to animate in all 8 directions and that gets expensive on bytes. I did try more varied algorithms for the robots, being able to assign sentries to guard certain places.




    I also gave some robots hearing or radar as well as line-of-sight of their own. With separately animated heads, they could "see" in front of them and open fire on the player they could see. You could sneak up behind the sentries and they would only turn to the player if they heard them fire first. 

    I then invented pirate raiders, who would arrive after a certain amount of time and were not robots, so could not be transferred to. Best to get the ship cleared before they arrive!

    There are 6 different ship layouts for the 16-bit versions, getting ever larger.

    Conclusion.

    It certainly took a lot of the design pressure off by having a clear idea of the overall game plan up-front. There was still trouble with the firing mechanisms that I tried to use, the famous gunsights. I wanted instant firing at specific points on the screen. It might just work with an additional mouse pointer to show where to fire as you also have control of the speed of the pointer.  
    You can`t force that working design out, this one just arrived like a flash of inspiration. It doesn`t often happen that way. I like to get something working, see how it plays, and improve it until it is the best it can be. That can be a painful task, but getting a good, playable result is what it is all about,. 

    3

    View comments

  9.  Introduction

    I was trying to write a piece on Kernel code in general and it was getting large and fragmented, so it`s better if I start at the beginning with Graftgold`s first concept of Kernel code, the 68000 Object-Oriented Programming System kernel that Dominic Robinson wrote in 1987. 


    Screenshots from the Interweb, not made by me. Since I did write some of the game code that made those pictures possible; I feel empowered to show them. Hope that`s OK with everyone.


    Up until that point we had been writing 8-bit code in Z80, 6809 and 6502. Each programmer had his own library of code. Assembly language is so low-level that there is little scope for a kernel. We had no idea of memory allocation, nor linked-lists, for example.


    Kernel

    Dominic was the first of us to start 16-bit coding, on the Atari ST. Knowing that the 68000 was a much more complicated beast than we had used before, he set about taming the Atari. We knew that these machines were going to be fitted and upgraded with different amounts of RAM and we wanted to be able to use that. The HiSoft DevPac development system had a proper debugger in it so we would have to play ball with that. We also felt that we would be nobbling the OS to be able to use all of the available resources without sharing.



    The 68000 CPU can run in two different modes: User and Supervisor. The Supervisor mode is mainly for the Operating System so that it can switch tasks, and the tasks it switches between are our applications, generally running in User mode. User mode is a subset of Supervisor mode. User mode disallows a few of the more sensitive operations, generally not needed by applications. If you want to play ball with the O.S., you would be expected to run in User mode. Even getting into Supervisor mode from User mode is tricky.

    The first job of the Kernel was therefore to get into Supervisor mode and take over! The debugger is also using Supervisor mode so you want to ultimately be in User mode for safety and to allow debugging. We did have macros to switch into Supervisor mode and back out again if we ever needed to. I can't immediately recall how we did that.


    The next thing we wanted to do was give the spare RAM to the memory allocator. Then we could ask for RAM to set up screen buffers knowing that the screen was going to be placed in spare RAM and not where our program code was. We could also then avoid anywhere where hardware registers might be set up. 


    Register assignment

    The 68000 CPU has 8 32-bit data registers and 7 32-bit address registers, excluding the stack pointer(s). The Kernel would re-site the stack for us, pointed to by a7. It seemed smart to use some of the address pointers to point to specific locations, mainly the hardware chips. Dominic decided that some registers would be working registers, namely d0, d1, a0, a1 and maybe d7. We would use those registers to pass parameters to functions. The other registers would be protected in that any function using any other register would have to push its value on the stack before messing with it, and restore it after. We always had a6 pointing at the chips so we could use the address+offset addressing mode to write to the chips. That made good sense in that our interrupt routines could also rely on a6 and not have to keep pushing it, loading it, using it and popping it, just get on and use it. Woe betide any programmer that messes with a6. Crucifixion, first offence. Teaches respect.


    The AMP system got a2 assigned to pointing to the current object, again so that structure elements coud get read with the address register+offset mode. The plot routines also needed a2 and then you tended to need address registers for the plot image, the plot mask and the screen address. 


    So when we were writing routines we tried to stick to using a0, a1, d0, d1 and d7 as scratch registers. Mostly we were so happy to have more than 3 8-bit registers we didn`t mind at all. We could use any reserved address register for its designated purpose, just not alter it.


    Sometimes we needed to get something done without being interrupted, in which case we had to go "Atomic", i.e. cannot be split. I can`t immediately think of where that would come in, the only thing I can think of is if we did want to alter a6 at any time, which I can`t imagine we would need to do. We would execute a macro to block interrupts, do whatever we needed to do, then call another macro to allow interrupts again.


    O.O.P.S.

    Dominic had been reading some programming design books, possibly from his university days, and decided to go with an Object-Oriented design, despite the assembler language having no specific support for it. He therefore designed his own constructs with macros. He supported classes, inheritance, privacy and methods. At this point I got rather lost because it seemed to be slowing me down. If I wanted to see a variable that the class held then I had to ask Dominic to write me a "get" method for it. It was too early days for a network or a server or a source code control system, we barely had a hard-drive. When that got delivered, the delivery guy crept in with the box like it was an unexploded bomb, as hard-drives didn`t have a "park" mode at the time. Only Dominic worked on the Kernel.


    The Kernel was able to do pre-emptive multi-tasking. That is, the system could take the CPU away from the current task every game frame and work out what the top priority task was and pass control to that. In this way you can have your game code running every 60th of a second and once it has done its job it relinquishes control to wait for the next frame`s game cycle. Your next priority task then gets the processor to do a bit more stuff, knowing that if it doesn`t finish by the end of the frame it will be paused. In that way you could, for example, keep some objects running in the foreground while you are loading a file. I used it to do a bit of fractalising of a landscape in the background while showing some info on the screen.  


    Dominic had written some demos to show what the Atari ST could do. One Friday evening we left him in office and came back later to find him sitting in the dark totally mesmerised by a demo he had written. I believe it ended up as part of Simulcra. He`s a maths wizard so was keen to get some 3D going in there. He ended up writing a whole 3D engine. Before that, he was busy writing routines to control all the aspects of the Atari ST. He developed classes for the screen and to split it into raster ports and support colour change interrupts by raster line. He also had disk reading and writing code, since we had dumped out the OS so we had no help from what was already there. We never interacted with the Atari OS so I don`t know how easy that would have been. It would also have been a moving target so we would be vulnerable to unexpected changes. We found it pretty tough on the Commodore Amiga, and we had all the documentation for that.




    One small slip-up that the O.O.P.S. kernel design had was that it tried to support every method for every object. This was to allow run-time inheritance. It was only when he was writing a tank algorithm that wanted to "seek" the player out that he realised that "seek" was also in the disk drive sector read routine and the two had no connection really. At this time he had an enormous and ever-growing 2-dimensional table in RAM of jump addresses for every class against every method, many of which were blank. This 2D table was just going to eventually take over the entire machine! It started to get interesting once we were getting towards finishing Simulcra.




    Extensive use of pointers was all new to me, so as I learned 68000 and the ST screen layout; I was also learning pointers and linked lists, all the things that Dominic had already got sorted out. It`s always useful to have the author of the code you`re using in the room. Black box libraries fill me with dread, especially if the documentation is lacking. You don`t know totally what`s going on, what it is doing, how long it is going to take. 


    Memory Allocator

    A memory allocator typically is given a single big block of memory. When your game needs a block of memory, we carve off a piece of the required size from one end of the unallocated space. This process is repeated as required. We don`t know whether memory is required for a short time or a long time. Imagine then that we allocate a large lump for a short time, then allocate a small lump that we decide is a cache and we want to keep it. We then return

    the big lump to the pool by freeing it and now we have two unallocated blocks of memory with a little bit still allocated between them. This is called memory fragmentation. The largest block we can now allocate is the biggest of the two lumps, we can`t move allocated memory as the game has an absolute pointer to it, and potentially copies of it. If we need a lump of memory larger than either of the two remaining parts we will not get it. 



    Now there are ways round this, we did discuss passing pointers to pointers to memory, noting that each time you want to access the allocated memory you have to reload your pointer to it as the memory allocator could have done a tidy up. You then have to code the memory allocator to have a tidy up if the total amount of free memory would not accommodate the request if it were all in one lump. You then have to shuffle the blocks of memory to one end of the memory pool. I then raised the issue that if such a re-organisation occurred at a time-critical moment then you might get a horrific glitch. We don`t like calling functions that can arbitrarily take a long time. The other burden of using pointers to pointers also struck us as quite a limitation that would catch us out a lot, plus it`s not thread-safe, i.e. if you have one thread using memory while another is busy shuffling it then you`re in a world of pain. We did expect to use multi-threading too.


    Random Numbers

    No self-respecting operating system would need a random number, but we do need them in games. Sometimes it is handy to have seeded numbers that draws the same numbers every time, as used by arcade games that play levels exactly the same every time, and sometimes it`s nice to have more random numbers, which you can seed to be different every time. Firing in a real-time clock stamp as the seed is one way, of course we didn`t have a real-time clock as standard back then.


    Since we kicked out the OS; we did need to write a random number routine. We used a fairly intensive routine, with  a later conversion of the Spectrum random number routine for faster numbers. Steve Turner had a book with the entire Spectrum ROM listed. The Z80 routine is quite a classic, giving completely flat distribution of 1 of every number between 0 and 65535 before beginning again. I remember testing it to prove it! No idea how it works, but it was a good proof. It doesn`t have a table of values it has used before, I know that. I appreciate that a cheating way to do that would be to just add 1 to the previous random number and pass it back out, but this issued seemingly random numbers each time. Most of the time we scale the returned numbers differently depending on what they`re for so the distribution doesn`t matter too much. 


    In order to get super-fast random numbers in my games I grab a block of, say, 256 random numbers at the beginning of a game level, set up two indexes into that table, then I grab the numbers at the two index points, Exclusive OR them together for the result, and move one index up and one down by different prime numbers. If your array is sized to be a power of two then you can AND the indexes with, in this case 255, or hex FF, to keep them pointing at your table. If you seed the main random number generator before you set uo your table then you can get consistent results. You might need that if you were running the same game on two linked machines, just sync up your seed first. 


    Ups and Downs

    On the up-side, we had a system that allowed us to write code for 2 platforms at once. Since Dominic wrote the Amiga drivers for the kernel only just before we needed it for Rainbow Islands then we weren`t testing on the Amiga at all until the end. It made the Amiga version easy to do. Dominic had tested the I/O routines and we had Chip memory and Fast memory all organised. It made all the I/O interfaces identical to the game code. The screen layouts were slightly different so the plot routines were not identical. The kernel code was thoroughly tested, we didn`t find many issues with it, maybe none at all.


    On the down-side, only Dominic worked on the code. No-one else would have been able to find anything, there were so many little files. The Inheritance feature meant that the code you thought you wanted might suddenly be somewhere else. Modern development systems have editors that help you flit around the code. We didn`t. I got stuck one time as I needed to know what was in a class` variable. I had no access to the code and wouldn`t have been able to find anything. What if Dominic is too busy to write the "get" function for me? 


    Source Code

    I did just find the source code for the O.O.P.S. Kernel. It was on an old sloppy disk, maybe not so sloppy as it was saved out over 30 years ago! Actually the source is all zipped up but I did read the random number file to check how it was working.


    Conclusion



    The O.O.P.S. Kernel then gave us a solid foundation for our code on two platforms and kept us disciplined enough to use the machines sensibly in their different configurations. It also taught us how to put pieces together and later comply with the O.S. in more co-operative ways. My efforts to write my own core code later were based on this design, with a view to reducing the footprint, partially by having to be cooperative with the OS, and partially by reducing what I need. Next blog part... coming soon.


    10

    View comments

  10.  Introduction

    Seeing Paul Hughes` (@PaulieHughes) recent tweet with some 68000 code for a graphics plot routine reminded me of my frst 16-bit project: Rainbow Islands. Whilst we were overjoyed at moving from the 8-bit platforms with all the limitations that came with, we had to be quite careful.


    Processor

    Firstly, we get a 16-bit processor instead of an 8-bit one. The ST and Amiga 68000 chips were running at 8 and 7 MHz respectively, which instantly looks 7 or 8 times faster than the 1 MHz C64 6510 processor. It's not quite that simple, as individual machine code instructions take different counts of cycles. Generally though the 16-bit CPUs had a larger number of instructions to do more complicated things, had 16 32-bit registers so you can keep track of more things at once, and the registers have 32 bits each, so you can work with larger numbers, and pointers. On the face of it then you`re probably better than 8 times better off.


    Screen Size & Layout

    The C64 had a very handy character mode or two that allowed us to draw 8x8 pixel graphics in 2 colours (for example for the font), and 4x8 graphics in 4 colours. The screen then a 1K block of byte-sized character codes that can reference up to 256 different character graphics. We might also use raster split-screen techniques to change character sets at one or more points down the screen to change the character set, colours, or graphics modes to get more on the screen. To scroll the screen then we might be working with 900 bytes or so, whereas the 16-bit beasts had no character modes, which is more flexible graphically, but burdens the CPU with having to shift or redraw the best part of 32K bytes (or more likely interpreted as 16K 16-bit words. Suddenly our CPU speed advantage is used up!   


    Memory

    On our base ST and Amiga machines we regarded 512K as the minimum spec. The manufacturers realised they would need that amount to do anything with their Operating Systems. I believe my Amiga 1000 had 256K at birth, but most, if not all, were upgraded with a 256K RAM pack slotted in the front. So we start with an 8-fold memory advantage over the 8-bit machines. More memory could be added as these 16-bit machines actually had a 24-bit address bus, so were able to "see" across a sea of 16 million addresses ( a million being a thousand squared, and a thousand being exactly 1024, as we all know, right?


    The 16-bit machine code instructions are all multiples of 16-bits wide, so our 16-bit programs are potentially going to be larger, but not necessarily double their 8-bit counterpart due to the fact that we can do  more complex operations in less instructions. Of course we tried not to do anything complicated. If we try to emulate an 8-bit program then we`d only be reading and writing 8 bits at a time, which wouldn`t be very efficient. All our variables will likely be bigger, just because we can. Over time we have found that our programs always got much bigger. I could look at any two consecutive program images that I have written and know that the later one was always bigger than the previous. Partially that`s because we keep all the bits we can reuse for the next game, and partly because we get more ambitious over time. Debuggers were starting to become available so that also allowed us to write more complex code. The 68000 chip had its supervisor mode that was designed for the purposes of tracing and debugging. Up to that point we only really had changing the border colour as a technique to know how much time routines were taking, and where it crashed. 


    Arcade machines

    So what were the arcade machines up to at this time? They appeared to be using the same chips, 68000, Z80 and the like, but they could use multiples if they needed it. The later Sega Saturn had a conglomeration of chips inside, including a 68000 just to do the sound. I thought that was a bit OTT. The other thing the arcade machines had was more able sprite chips, with independent colour palettes, quite often 16 colours would be enough per object. They had a lot more sprites than the home computers. They could use alternate palettes to change colours of objects when they got hit or were very damaged. Just guessing now, but they look to be using blocks of 16x16 pixels and building up larger images with multiples. They also appeared to have X and Y flipping capabilities. Hardware sprites means you don`t have to clean up the background bitmap because the display chip is building the image at run-time, it`s a big time-saver. Nowadays we just use brute force and rebuild the whole screen image every frame from scratch.


    Rainbow Islands




    We received some bitmap sheets of graphics from Taito for Rainbow Islands. We noted that the game objects that could move left or right only showed a left-facing image. From that we deduced that they had a way of flipping the sprite images at run-time. The way the ST and Amiga screens are arranged into bit-planes and pixels; groups 16 pixels together so that flipping images is not easy to do at run-time, I doubt if anyone managed to do that. In order to save floppy disk space and to not torture the graphics artists into creating the right-facing graphics, I split the graphics into 2 sections, front-facing and left-facing. After loading the graphics, I then generated the right-facing images at leisure. We also split our objects into 16-bits wide and 32-bits wide. That was so that smaller objects didn`t waste any space or run-time plotting a lot of empty pixels. We didn`t have any particular restriction on graphics height as that was just a repitition loop. Initially we were short of graphics tools. The guys were using NEOCHROME on the Atari ST. We then exported the images for each object as a set of binary data, that`s just what it did. We had a better bulk method sorted out later. The data is then included, file by file, so object by object, into another assembly file per island of data. I also had to create a bit of additional header information to define the height of the object so it would know when to stop! There was also a positional pixel offset so that we could define the origin of the object as the centre or the top left or somewhere in between. The collion detection is done off the same co-ordinates as the plotting, it can be useful to separate them a bit. Because the plotting starts at top left we tended to use the top left and then have offsets to the feet positions for movement. Only in 2018 did I switch to a central origin for my objects, which is not to say I hadn`t considered it earlier. For most objects we could leave the X & Y offsets at zero.


    We were mostly using software objects plotted into the bitmap, which obliged us to use one palette for the background and the moving objects. The arcade machine was clearly not restricted in that way. Indeed Darius Island 9 had a completely different palette for its indigenous objects that didn`t match at all with the main game objects. They may have had more than one palette just for the backgrounds.   


    We used different software plotting routines to effectively change the colour of objects as they were being rendered. Mostly at this time we only used two variants to force all pixels to colour 0 or colour 15, which we had intelligently arranged in the palette to be black and white respectively. They`re easy, as to get colour zero you just apply the mask and don`t add the data, or for the top colour you just reverse the mask and use it for all the bit-planes` colour data. There was one other variant which had interleaved masks, one per bit-plane, which we used for the Amiga blitter so that it could blit all the bit-planes in one blit. This meant the data size was larger but we could leave the blitter to get on with the job while we got on with setting up the next plot, doing the calculations from the required X & Y positions into an address and X pixel offset, and just checking that the blitter is finished before we hit it with a new plot request. Anyone not checking first might get away with it using the standard CPU as it was always ready, but put a faster CPU in and the CPU might be ready before the blitter. The blitter did all the data logical shifting faster than we could do it in code, so even having to wait between bit-planes for the blitter to complete each bit-plane was still slightly faster than just using the CPU. At least it was waiting quickly!


    We did have one other plotter that didn`t use any graphics data as it was for extending spider web vertical lines. The line length was an input parameter and since the line was vertical, the mask and data were in the same position per line. We wrote the ST software version first and we didn`t bother to convert that to blitter. The lines disappear some pixels at a time so since there was no input data it was easier left to software.


    Palettes

    The arcade machine used 1 16-colour palette for most of the objects that persisted from island to island, such as the gems, though it`s not out of the question that they could have used 7 palettes for the 7 gem colours. Since all the bonuses were on the same sheet as the hidden fruit, and the rainbows themselves, they were able to get them all into 1 palette. They were able to use a palette switch for Bub and Bob to wear different colour outfits, we had to have two versions of all the Bub/Bob images. Each island background and meanie sets then had their own 16-colour palette or palettes, one would probably do. Again, we didn`t have that luxury. We adjusted all the common graphics down to 13 or 14 colours, and then we had 3 or 2 colours that we could assign per island. Monster Island I specifically remember needing some extra magenta and grey. Our common graphics sets then could be loaded into memory once and used on any island, otherwise we`d have had a big remapping operation going on between islands. John Cumming had the unenviable task of coming up with the palettes and then doing the remapping of the colours from the original supplied palettes to our palettes. 


    Islands



    So for every one of the seven islands we had an assembled data file. This would contain the 16-bit and 32-bit wide graphics, the palette colours, the compressed background map which was packed on the ST using our mega-compressor. Thich was specifically designed to pack background maps that were typically constructed from 8x8 pixel characters that were made into 2x2 blocks. It effectively looked for repeating pattern pairs (using horizontal and vertical passes) that could be substituted into a single macro. The compressions had to be lossless. Up to 8 passes could be run and we tweaked the sequence of horizontal and vertical on an island-by-island basis to get the best results. Once macro-ised, we could then run-length encode the resultant maps to squeeze them down, being left with 4 squeezed maps and a list of macros that each contained a pair of other macros or characters. John wrote a mapper program on the ST in STOS. He sat there with a VHS video of David O`Connor playing the game the whole way through (or so we thought as we were blissfully unaware of Islands 8, 9 and 10 at this time). John would pause the video and map what he saw, trying to get it done before the VCR unpaused itself. He had the background tiles in the supplied graphics sets. We noted a few mistakes where there might be a missing shadow, or a missing bit of cloud. I insisted the corrections be made, correct being better than authentic, and I didn`t want anyone blaming the errors on us. The background 8x8 tile graphics were also in the loaded file.


    We could also load in object control "routines". I called them AMPs, or Alien Manoeuvre Programs. They are data created with 68000 assember macros, effectively just a bunch of 16-bit words that are interpreted by our AMP controller. An object such as the Clown below is created and has its structure initialised and then customised with the values you see at InitClown. The program then calls various AMP functions with whatever parameters it expects. There are common functions that receive control if the Clown is hit, so this isn`t the full life-cycle, but it`s mostly this: 


    InitClown PrimeList
    Prime.w _SpriteID,'CL'
    Prime.w _SpriteLife,AngryTime
    Prime.w _SpritePriority,20
    Prime.w _SpriteDepth,1
    Prime.w _SpritePlot,_FullPlot32
    EndPrimeList _SpriteEnd
    AMPClown
    SetFlag ClockHold
    SelectPolarX $a0,$e0
    MeanieSpeedPolar 16
    Animate ClownSpin
    Collision ReadOnly,CSizeClown
    CVector ThumpClasses,.Hit
    LVector .Angry
    MeanieLargeBase BaseClown
          
    .Move Loop Forever
    HitPlayer
    MoveUpdate
    RainbowReverse 4,27,0,23 ; Collision with a rainbow?
    BeeMove 4,27,0,23
    PolarVeer $40
    MapRelative
    PurgeCheck ; On or close to the screen?
    Delay ; Wait for the next game cycle.
    EndLoop
    .Angry
    MeanieLargeBase BaseAngryClown
    MeanieSpeedPolar 18
    Goto .Move
    .Hit
    QuickModifyPosition 8,4
    SpinFrames BaseSpinClown,Monster
    Goto AMPHit
    .Explode
    QuickModifyPosition 8,4
    Goto AMPExplode
    ClownSpin AFrame 0,5
    AFrame 1,5
    AFrame 2,5
    AFrame 3,5
    AEndList 


    The beauty of the assembler macro is you can have local label positions (preceded by a .) and it's 2-pass so the referenced offsets can be worked out upwards or downwards. The whole data is interpreted in relative offsets, no real locations so they can be loaded and used anywhere. I haven`t achieved that in C because I have to use single-pass C pre-compiler macros. That`s a giant leap backwards for AB, I have been using absolute locations, upwards only, so I can`t load them.


    The AMP system provides quite a fast way of prototyping a meanie or any other game object. In Jackson Structured programming terms, it allows us to write the lifecycle of the object from its own point of view. The Delay instruction (later becoming an option on some other instructions) is where it says "right, that`s it for this game cycle, resume operations from the next instruction on the next game cycle. It avoids having to have modes and spend a lot of time working out what you were doing last time. The individual building blocks of the actual functions can be as simple or as complex as you want. The trick is to keep things as simple and consistent as you can. 


    The final item in the loaded data is the list of all the objects to create on the map. Once you get into flying meanies the start positions are only estimates. So John also had to note down all the places were we saw both fixed and hidden fruit positions. David might not have been playing in a way to get all the hidden fruit to reveal itself. There was plenty of rainbow firing to defend himself so I reckon we got most of it. We spent a little too long trying to figure out what all of the items were, many being based on Japanese snacks that we had no knowledge of. I had to give them all names in the assembler headers! I think there was a list in the documentation that we got, but just what are Daikon and Taiyaki? 


    Surprise!


    Imagine our delight as David got better at the game and we realised that the "ending" screens that we saw after island 7 suggested that we had been playing the game all wrong and we should have to do better. We had by this time discovered that sometimes a secret door appeared in the boss-room. We even found out why and could get it every time, at least on the earlier levels. It gets tougher as you go along, as does getting through the secret door. One of the secret rooms even lets you skip islands. Eventually we figured it out and getting to the end of island 7 goes through a different sequence. We had seen some mysterious graphics that we didn`t know where they were used, but by this time we had seen other graphics that we were pretty sure weren`t in the final game either, such as Bub sliding round the rainbow rather than walking, or changing into his Superman costume(presumably for invincibility). The islands screen appeared and then 3 more islands rose up out of the water! They`re quite big graphics too, plenty of frames. Those extra 3 islands are also huge, much taller than any of the previous ones, plus island 9 is Darious Island, with a whole new palette. I did code the rising islands but as we got closer to the deadline we knew we wouldn`t have time to even map the new islands, and we hadn`t budgeted on doing them, the publisher didn`t know about them. I also had to remove the rising islands graphics towards the end as space was getting tight. We`d have needed another floppy disk and a minumum spec of a 1MB machine to run it on. I checked the M.A.M.E ROM image for Rainbow Islands and it comes to about 2MB altogether. We agreed that we would complete the 7 islands and that would be it. Shortly after, everything fell to pieces as the publisher got sold. That put the whole release on ice for quite a while, through no fault of our own. It was a good first project though as I got to learn about 68000, the ST, the Amiga and platform games.


    Epilogue



    There was some wrangling about a year later and Ocean and Taito agreed that the game could be released. We got a new loading screen and made ready with a new master diskette. Thanks to Garys Penn and Liddon to just get some heads together as they had seen a preview of the game and thought it would be a shame if it didn`t get released.  

      


    4

    View comments

Loading