The Core Animation Loop
Creating animation is a three-step process repeated every game frame (ideally synchronized with VSync, as discussed in Part 25):
- Erase: Write the background over the sprite’s current location.
- Update: Change the sprite’s X/Y coordinates and/or its graphical frame number.
- Draw: Render the sprite at its new location.
Updating Sprite Coordinates
To move a sprite, you modify the X-Coordinate and Y-Coordinate fields within its memory descriptor (from Part 31).
Example: Moving a Sprite Right by One Pixel
We use the Index Register ($IX$) to point to the sprite’s data block, and the INC
instruction for a fast 16-bit increment of the X-coordinate.
SPRITE_X_ADDR EQU +0 ; X-coordinate starts at offset 0
SPRITE_Y_ADDR EQU +2 ; Y-coordinate starts at offset 2
MOVE_RIGHT:
LD IX, SPRITE_1_ADDR ; IX points to the sprite control block
; Increment the 16-bit X-coordinate (bytes at IX+0 and IX+1)
INC (IX + SPRITE_X_ADDR) ; Increment the low byte of X
JR NZ, NO_CARRY_X ; No carry? Skip high byte
INC (IX + SPRITE_X_ADDR + 1) ; Increment the high byte of X
NO_CARRY_X:
RET
Movement Speed: For smoother motion, you would typically use a separate velocity value (e.g., DX
, DY
) and add that velocity to the X and Y coordinates using a full 16-bit addition routine (`ADD HL, DE′).
Frame-Based Graphics Animation
For effects like walking or firing, you cycle the sprite through a short sequence of pre-drawn graphics (frames).
Method:
- Define a sequence of frame addresses (a lookup table).
- In the game loop, increment the sprite’s Frame Number field (e.g., offset +6).
- Use the Frame Number as an index into the lookup table to get the address of the next graphic.
- Update the Sprite Graphic Address field (offset +4) with the new address.
Example: Cycling the Frame Number
SPRITE_FRAME_NUM EQU +6 ; Frame number at offset 6
NEXT_FRAME:
LD IX, SPRITE_1_ADDR
; Increment the 8-bit Frame Number
INC (IX + SPRITE_FRAME_NUM)
; Check if the frame number has passed the maximum (e.g., 4 frames)
LD A, (IX + SPRITE_FRAME_NUM)
CP 4 ; Compare A with max frame count
JP C, DONE_CYCLE ; If Carry is NOT set (A < 4), we're done
; Reset frame counter to 0 if max is reached
LD (IX + SPRITE_FRAME_NUM), 0
DONE_CYCLE:
RET