C64 Character Modes

Introduction.

The Commodore 64 existed in that magical time between screens with only text and screens with only bitmaps. The C64 had both, plus it had the ability to display character maps with completely user-defined graphics. These graphics did not have to look like letters of the alphabet, they could be 8x8 pixels in 2 colours, or 4x8 wider pixels in 4 colours.

What`s the Hardware Doing?

In the character modes, the graphics chip is taking 1K of character codes, 40 per row down the screen, and using those character codes to index into a 2K character set of graphics. Since each character has 8 bytes, or slices, of graphics from top to bottom, it just scales up the character number by 8 to get the addresses of the graphics. It`s all very mechanical, but the fact that the chip is doing it every 50th of a second means you can have some fun with it.

Character animation.

The most obvious use of the character mode is to move the graphics data in a set of consecutive characters. By drawing a group of animations you can then move the character data, preferably during the vertical blank period when no characters are being displayed, so that the characters change from one image to the next. You probably don`t need the speed of doing this every frame as the animation ends up being too fast. You can rig up a system to just do that every 3rd or 4th frame. That means you could have 3 or 4 sets of animations and do one of them every frame. Typically each animation involves moving 32 bytes, possibly plus a buffer of 8 bytes. If you just want to move the data then you will have to be careful not to overwrite something you haven`t copied.

Programming is often about speed versus memory. If you have data for characters 0, 1, 2, 3, 0, 1, 2 then by picking your start position you can copy 4 characters' worth of data without having to check for the end and wrapping around. Logical ANDs can alleviate that as well to some extent when working with data lengths in powers of 2. With only a few registers you should use your variables wisely. For example, your character animation needs to go from frames 0 through 1 and 2 to 3. Your copy operation though needs a data index from 0 through 8, and 16 to 24. So rather than shift you animation number up by 3 bits every time, just add 8 to your variable and use it as an index. You can then AND the value with hex 0x18 (decimal 24) to keep it in range and it`ll go 0, 8, 16, 24 and back to 0.

Gribbly`s Day Out.




My first use of animated characters was for the barriers and buttons. To get the correct angle for the barriers I had to use 3 sets of animated characters as I needed barrier characters offset to the right by half a character. The buttons look like 2 character animations as the graphic is quite small and had to be done in one colour, so was much simpler. Animated characters were also used for the Gribblets. They are two characters wide. There are also three versions of them to keep processing simple. One of the versions can be picked up or turned over, one can`t, and one is the upturned version waving their legs in the air. Other character animations are the PSI grubs, the waterfalls and the surface of the water. More on how the whole game works in a later blog.

I had seen how other people were using animated characters. The beauty of them is that no matter how many animated characters you have on the screen, the cost is the same, they are all re-plotted by the graphics chip every 50th of a second... for free! The fact that you can also set smooth scroll registers to get 0 to 7 pixels of offset, also for free, means that we can have smooth scrolling.

I was also using sprite to background hardware collision detection. Gribbly gets an indication from a register in the graphics chip if any pixel in his sprite touches any pixel of the two higher colours on the background. Background colour and multi-colour 1 don't trip in the collision detection so he flies straight through the buttons and the waterfalls, but by careful use of the colours to make sure most of the edges are done in foreground colour or multi-colour 2, there is total pixel accuracy on the collision detection, and once again, it`s free.

Paradroid. 



I cut down on the animated characters in Paradroid just because I needed more characters to draw some of the graphics facing four different ways, such as the consoles. I never believed in putting in effects for the sake of it, they had to have a purpose. The energisers are animated characters, so it doesn't matter how many there are on screen, just the graphics data for 4 characters are being moved around. By making all the pass-through characters at the front of the character set, and the blocked ones at the end then a simple comparison of the cut-off point tells me whether a character under the player or bullet sprite is passable. 

The door animations then are just swapping blocked door characters on the background map for clear ones, a pair at a time. Once swapped to clear characters the player, other robots and bullets can pass through. Bullets don`t open doors though, only robots. If the player detects an energiser character under them then they gain power. Similarly, lift characters access the lift if the fire button is pressed while standing on one, and the characters in front of consoles do the same for computer access. That's the magic of a character set background rather than a graphically drawn one, whereby every active object in the game has to have a way of doing collision detection. That's for another blog too, though I am reluctant to reveal the system we learned from a fellow programmer, it`s his secret really.

Uridium.


In order to get a game running at the full 50 frames per second speed that all of the arcade machines were managing, I needed to reduce the time spent per frame, so there`s no character animation here. There are a couple of little tricks though. I was able to track the mine ports on the dreadnoughts and update the colour map so that they glowed. Right now I'm wondering whether bullets glowed if they went over a mine port. There's an experiment to try! For this game though I wanted plenty of player bullets so I used the character set in another new way, for me anyway. I reserved some 24 characters in the character set for bullets. When the player fires a bullet, I look at what the character is where the bullet should go, I copy its graphic to a spare bullet character, and then add the graphic of the bullet onto the character. Now I substitute my bullet character onto the map where I need it, noting what character I am substituting and where it was.  

For each of the 24 bullets I just need to know if it's alive or spare, and which direction it`s going in. One byte does all that: if it's a zero the bullet is spare, ready to be used, -2 it`s going left, 2 it's going right. I can add this to the X position each move and check for being off the visible screen with another quick check. The bullets are 1 character wide and character aligned, they're all going at 2 characters a move. All the enemy spaceships are looking at a 3x2 character block beneath them, checking for a character in my 24 bullet range, and if so they blow up and award their points to the player. They'll always spot a bullet moving under them. Every frame then I have to repair the background of bullets in reverse order (in case 2 bullets are in the same character, they have to unwind in the correct sequence. Yes I could have just restored true characters with a bit more simplicity. Hindsight is wonderful!

Collision detection with the walls for the player is as straightforward as Paradroid, all the blocked characters are at the end, the destructible ones are in the middle, with whole and destroyed versions arranged so I just add a constant (preferably a one) to the character code to get the destroyed version. The only thing you need is a little map of what character is part of a larger object so that you take out all the relevant blocks together. Likely I had offsets X and Y to the top left of the object, and then the X and Y size for each of the destructible characters.

Alleykat.


For the race-tracks, most of the scenery is destructible, with up to a 5x3 block of background. I had to split the character set into blocks that you could fly over, under, or into, as I was trying to represent a 3-dimensional view with a 2-dimensional map. The height that you're flying over background adjusts your position and, rather helpfully, the nose of your ship always points to the character you`re going to fly into, you just allow the player to pass if the height of the character is lower or higher than you're flying. I can't remember exactly but I'd have lined up all the graphics so that they could be drawn in position for whatever the editor needed. I didn't get involved in re-arranging the characters for different heights, but some simple comparison tests would tell you if you were over a top height character as they were all lined up together. You only need to do the required tests for the height you`re flying at.

I had animated characters for the energy blocks on the background. I did 4 different frames to make the graphics bob up and down, and the animation was sequenced up, above, middle, down, below middle. I didn't want to lazily copy the middle frame to positions 2 and 4. The 4 character graphics just cycle around in the 4 graphics positions, and you can put any of them on the background and they all move, but at different stages in the animation to look more random.

I couldn`t quite scroll the whole screen, 1,000 was too many characters to do in a 50th of a second, let alone a 60th for the U.S. release. The trackside left and right characters were posts spaced at 2 characters apart. I had to swap the graphics over depending on whether I was displaying starting on an even or odd vertical map character. The outside characters were just continuous vertical lines, so was not scrolled at all. Total genius, I thought! I did reserve a lot of characters for the giant trench that the player makes should the ship run out of energy. Some reviewers seemed to enjoy just making as large a trench as possible.

I also used animated characters as black animated silhouettes for the fire effect on the titles screen. I had a system of driving character-high colour fades through the background and I was moving a bunch of those up the screen randomly, and they could overlay each other so they would cycle round and with animated black characters over the front produced some wacky fire effects. Mostly the colour technique was just to be able to do colour fades across text. By Intensity I also had the X smooth scroll position under control every raster line so I could make the text italic or wave about.  

Morpheus.



For this game, I sat there with my hex calculator and converted 256 angles in a circle into hex sine values. There are 256 degrees in a circle, not 360, as school would have you think. It makes sense really, as you can add two angles together, ignoring any carry over, and get the answer without having to do any range checking. A multiply algorithm was needed to multiply an angle's sine and cosine by the polar speed to get a Cartesian X & Y speed out to add to the X & Y positions. I needed these so that I could drive individual pixels around the screen for explosion and star-field effects. In multi-colour mode you get 3 different colours to plot over the background, so by arranging the multi-colours carefully you can get fading effects too. 

As with Uridium then I reserved as many characters in the character set as I wanted pixel particles, something like 32, I do like round numbers. By the way, character zero is usually blank so it is easily recognised as empty. You always need a blank character, right? Anyway, when you want to plot a pixel particle you work out the character on the screen where you want to plot it first. Make a note of that character code, and where you got it by address, not X & Y, as you'll need to restore it later. Copy the graphic in that character to your particle character, add the particle graphic to the correct place in the character. The low 2 bits of the X and the low 3 bits of the Y give you the pixel position X and Y. You have to AND in a gap where you want to plot before ORing in your colour pixel so that it doesn't wipe another particle on the same line. Then you substitute your new character onto the screen where you worked out earlier. All this needs to be done in the vertical blank period to avoid flicker, but before you get started each frame you need to clear up the previous frame's activity by restoring the previous move's alterations. The fastest way is to mow through all the particles, skipping any unused ones or any where the substituted character is itself a particle character, as it will get cleared by the first particle in at some point. That way you don`t even need to clear the particles in reverse sequence. For speed, never work anything out more that once, save the answer for later, and every CPU cycle is precious, plan to write it efficiently first time. Don`t just figure out how to do something lazily and then have to optimise it later. Why write it twice?

The "tooth-paste" gun at the front of the ship is also done with characters. All the characters are in a set range to allow the meanies to just check under them for a character code in that range and they die. The firing sequence then just defines which characters to plot in front of the gun to make the shot grow, hold and then decay. 

I wanted shots to be fairly immediate from the gun add-ons, you just see a muzzle flash and the bullet is not shown, it's too fast. I'm now wondering whether I just fired out a bunch of blank characters in the direction of the shot, making sure that the alternate blank character was in the reserved bullet range too. No animation needed. I hope I did it like that. When testing, you can put a graphic in the character to see that you`re plotting the character and then removing it correctly.

The giant character font is made of blocks so I had a map of which characters to make each letter. There are 3 different widths of letters 1, 2 or 3 characters, and they are 3 high. You always want your score digits to be the same width, you don`t want scores and timers hopping about just because a 1 appears in the top digit. It`s one of my pet hates, it looks so horrible. So you have to widen the 1 artistically. The two big text bars are actually changing the character set with raster interrupts. This meant I wasn't limiting the game`s character set by having to use half of it for text.

The radar at the top of the screen had a reserved block of characters for it and the particle algorithm was used to plot pixels into an array of characters. The clearance routine was slightly different as I wasn't replacing whole characters on screen, I just had to blank out the lines I had altered, so I just had an array of addresses of lines in the character array altered, and all I had to do was blank them out. This worked better for a smaller chunk of screen where you can afford 1 character for each character in the radar, rather than 1 character per pixel particle.

The other use of characters were the top and bottom rails in the docking bay. These are being dynamically built to move the 3 parallax layers, and then to fade them out by applying different masks to the graphics before combining them.

Intensity.



This was a game without bullets, and the sprite multi-plexor was handling most of the object plotting duties. However there were a couple of character-based features. The hatchways where the crew emerge had a lot of animation frames to show airlocks opening and the lift rising up with the crew member. Lots of animation for just the one character area. 

I wanted to show damage being done by the meanies, so I drew some damaged background characters for when they are landing on the background, as well as pre-damaged bigger chunks to show the space station was under attack. For the sprite shadow effect, the colours have to be rather broad. The shadow sprite colour has to match the shadow colour on the background, i.e. one of the character multi-colours. The main un-shadowed background colour has to be a related colour that is darkened to the one and only shadow colour. The highlighted, usually white, background colour can also be covered by the shadow sprite colour, and space is actually the foreground colour, which obscures the shadow sprites. Yes, space is in front of the shadows!

The walkways are also characters that can modify the background when you land a ship on the end character, allowing the crew to cross. I`m just plotting extra characters into the map until the bridge hits solid platforms. The bridge stays until the ship takes off and doesn't hold the bridge open.

If anyone has managed to get to the final completion screens in the game, I had drawn an escape ship using background characters. I had this idea to make it take off. I had to redraw the space ship in sprites, so between playing the level and revisiting it after completion, the characters are replaced with a flat take-off area, and the sprites do the work. Certainly gave the sprite multi-plexor a work-out. 

As well a using the character map as a reference for allowing landing, behind the scenes the game generates firstly a height map for the screen, to decide whether either of the player craft can fly over or crash. In order to make the craft`s attempts to fly over obstacles smooth, the game also generates a recommended height map. The craft fly at the recommended height or their maximum, whichever is smaller. For space, I could probably have combined them into 4 bits each and make one map, but it was only 1K each, so I had two maps to make access to the data a bit quicker. All this is to show off the shadows showing how high you're flying (c) Uridium. Always work out the tough stuff in advance rather than on-the-fly. Space versus time, if you have space you can store answers in advance to save you time.

Conclusion.

So that's the different ways I was using the fabulous character modes of the C64. The VIC-II chip was tirelessly refreshing the background every frame and giving a level of indirection between the graphics and the screen that we could take advantage of to get some interesting effects. Whilst we can simulate that now, we have to program it ourselves. A step backwards, I feel. However, as resolutions have increased and pixels have consequently got smaller everything has got so fiddly that other techniques have taken over. The Golden Age of Character Graphics is sadly over.
    


0 nhận xét:

Đăng nhận xét