Sunday 21 March 2021

Leilei Relay


Baby UFOs in UFOland have to train arduously before they can leave their homeworld.

Historically, the most infamous of these training grounds was the Lei Lei Relay.

Collect the lanterns to advance to the next field, up until you reach the UFO cathedral. There you will attain the title of UFO master.

Guide the UFO using Joystick in Joystick port 2. Left and Right turns the ship, while Up is thrust and so is Fire. Pressing key '1' on the keyboard switches the musics on/off.

*

On and off I have been working on new Commodore 64 games. Most of them do not get much farther than early stages, so I really, really wanted to do something so small that it would get finished.

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.


My original intention was to make a one-screen lander-type game. Some inspiration has been drawn from lander games and Space Taxi.

But it's closer to a gravity game like Thrust in the sense there's no actual landing involved. Instead, you need to collect all the lanterns from each room to proceed to the next one. The quicker this can be done, the more bonus points can be achieved. And that's it.

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

At the center of the game routine is the scrolling screen buffer. Instead of updating screen portions piece by piece, the whole screen is redrawn for each frame.

I abandoned the character-specific colors to make this easier. This gives the game a somewhat monochromatic appearance. I tried to alleviate it by using different colors for different levels, and mixing the colors when possible.

There's a huge ordered buffer in the memory, but at least it made some other things easier for me. The level data in itself takes very little space, but testing levels and putting them in a reasonable order is time consuming so I went for a fairly low number.


More technical parts

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:

[Low byte][High byte ][Highest byte  ]

Sprite coordinate:

          [Sprite LSB][Sprite MSB bit]
  

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. 

It's really annoying to see the ship move to a direction it is not pointing at even if the difference is very subtle.

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.


Wrap up

Looking at my notes, I worked on the game intensively for a few weeks in May 2020. Some of the routines had been done previously so I didn't have to "invent" the inertia, polar coordinate and precision coordinate routines then. So adding that I might have worked on this for about month.


It appears I considered a release at end of June 2020, but decided to wait. Then I picked it up on this weekend, with the idea I might be heading towards a phase of adding things to the game. 

But instead I simply cut off loose ends, adjusted some of the musics, added some graphic variety, removed anything that still looked like a bug, tested it on a real C64 and released it.

Design-wise, there was a lot that could have been added, but at this point every addition would have needed another round of testing and assessing the whole game, so it was better to keep the original promise of a "small game".

Leilei Relay at csdb


1 comment:

  1. Neat (if maddeningly tough) little game. I'd been wondering about what exactly the player was supposed to be, but your amusing 'backstory' about training baby UFOs makes sense now -- it's an anthropomorphic flying saucer with eyeballs! Also, thanks for the informative behind-the-scenes programming write-up, it'll come in handy if I ever bite the bullet and start learning assembly (really, there's no excuse not to, with all those oodles of books on Bombjack).

    ReplyDelete