Even then, this game turned out to be somewhat bloated compared to my original intentions. So it's a small game that became slightly bigger than I wanted to.
It does have multicolor character graphics, sprites, smooth vertical scrolling, inertia, polar coordinate tables, multiple SID tunes and sound effects.
The weird shape of the player ship gives the game some additional spice. It needs to be turned into a vertical position to help squeeze it through some of the more narrow passages.
Unlike with Fort Django and Digiloi, there's no shooting, which makes this game simpler. But it's likely to be infernally difficult.
There's no PETSCII character graphics this time, I have some ideas about their use for other games, which may or may not be completed!
Tile map layout
As usual, I have come to trust Tiled for map-making. Instead of PETSCII I now used multicolor character graphics. The pipeline from character editing to "meta-tiles" would deserve a closer look but I'll leave that for another time. Let's just say experience in PETSCII gives a good start for optimizing character graphics, but it does have its own peculiarities.
First level in Tiled |
The brutal redraw routine means I can scroll the area in whatever speed I like, and also if I draw changes into the buffer, these are rather simple to calculate, as all Y-coordinates work as increments to the HI-portion of the address. So if the first line of the buffer is at $8000, the next line is at $8100 and so on.
A new thing to me was polar coordinates and inertia, although I already made the routines for a more complex game idea, they found a better home in Leilei Relay.
The UFO coordinates are 24-bit values, but this is less complex than might sound.
24-bit coordinate:
The high and highest byte are transferred directly to sprite screen coordinates, so that "highest" gives the MSB bit for the sprite coordinates higher than 255.
Low byte is kind of sub-pixel coordinates, as they increment over 255, the sprite has moved visibly one pixel.
For thrusting the craft forward, a polar coordinate table is needed. These are 8-bit values centred around the point 128,128 ($80,$80). They have been generated using Processing.
move_anglex:
.byte $80,$80,$81,$81,$82,$82,$82,$83,$83,$84,$84, [...]
move_angley:
.byte $70,$70,$70,$70,$70,$70,$70,$70,$70,$70,$70, [...]
So the first values are $80 and $70, which indicates 128 and 112, denoting a 0,-16 vector.
I have 256 values in the table although the UFO has only 32 visible angles. I won't go into the confusion this might create, I initially thought it would be nice to have more functional angles than visible angles, but after experimenting with it, this is really a no-no.
So, it may be just as well assumed the table is 32 values long.
Oh, and the above table is not directly applied to the sprite coordinates, but to the ship inertia X/Y values, which are in turn 16-bit values.
These again have the initial value of $8000, $8000. (low byte=$0, high byte=$80)
If the ship angle is stored in register Y, then the above table can be used to alter inertia.
sec
lda inertia_x_lo
sbc #$80
sta inertia_x_lo
lda inertia_x_hi
sbc #$0
sta inertia_x_hi
sec
lda inertia_y_lo
sbc #$80
sta inertia_y_lo
lda inertia_y_hi
sbc #$0
sta inertia_y_hi
clc
lda inertia_x_lo
adc move_anglex,y
sta inertia_x_lo
lda inertia_x_hi
adc #$0
sta inertia_x_hi
clc
lda inertia_y_lo
adc move_angley,y
sta inertia_y_lo
lda inertia_y_hi
adc #$0
sta inertia_y_hi
Sooo the $80 is first subtracted from the inertia values, and then the motion (with the $80 baked in the values) is added. There must be a more clever way but this is the one I used.
The inertia values are then used similarly to affect the ship (24-bit) coordinates.
This is for the x coordinate, for the y coordinate it's the same. It may be useful to keep them separate.
sec
lda ship_x_lo
sbc #$0
sta ship_x_lo
lda ship_x_hi
sbc #$80
sta ship_x_hi
lda ship_x_highest
sbc #$0
sta ship_x_highest
clc
lda ship_x_lo
adc inertia_x_lo
sta ship_x_lo
lda ship_x_hi
adc inertia_x_hi
sta ship_x_hi
lda ship_x_highest
adc #$0
sta ship_x_highest
The main game sprite is simple, but it does have a layer of anti-aliasing, using another sprite. These were generated using Processing. There are 32 frames, making a total of 64 sprite frames for the UFO.
For each frame, the sprite graphics data are copied from outside the video bank area to the visible sprite frame, as 64 sprites would take a huge chunk of the video bank and this didn't fit into my plan. I'm now getting to understand how important it is in C64 programming to have a good plan for locating the graphic data and the video bank.
With this technique I could have had 64 frames (128 with anti-aliasing), but I felt it easier for the player if the vertical and horizontal positions can be more clearly discerned.
The thruster flame has only one frame but it is positioned differently in alternating frames to give it more direction and a tiny bit of transparency, again using a polar coordinate shift.
Four sprites are used on the lanterns, although with multiplexing they could have used up less. But I didn't want to practice multiplexing this time, it was enough to handle the above issues and sprite-to-sprite and sprite-to-background collisions.