Commodore taught sprite animation with a mouse. The worked example in the Commodore 64 Programmer’s Reference Guide, “Michael’s Dancing Mouse”, uses “ONE SPRITE (sprite 0)” and “THREE sets of sprite data to define three separate shapes”. The program never redraws the mouse. It changes one number, the sprite’s pointer, so that the video chip reads a different shape: “The same sprite will be redefined rapidly over and over again as 3 different shapes to produce the dancing mouse animation.” Its speed is set by a BASIC delay loop, FORT=1TO60:NEXT, “which sets the speed at which the mouse dances”.
That is sprite animation in miniature: a set of drawings, a way to pick which one is shown, and a clock that decides when to move on. Done well, it was what reviewers noticed first. ZZAP! 64‘s first issue, in May 1985, gave the Commodore 64 version of Gargoyle Games’ Tir Na Nog the strapline “Stunningly animated arcade adventure”: its hero Cuchulainn, “twice as large as most computer heroes”, “strolls into his quest with long, flowing strides, his hair flapping in the wind”, and the creatures he meets “too are excellently animated”.
Frames and timing
A PAL television picture is redrawn 50 times a second and an NTSC one 60 times. Characters are rarely animated that fast: a walk shown at 50 drawings a second would need far more frames than a game could store. Instead the program counts screen frames and moves to the next drawing every few of them. At one change every five frames, a PAL game animates at 10 drawings a second.
ANIM_SPEED = 6 ; screen frames between drawings
NUM_FRAMES = 4 ; drawings in the cycle
update_animation: ; call once per screen frame
inc anim_timer
lda anim_timer
cmp #ANIM_SPEED
bcc done ; not time yet
lda #0
sta anim_timer
inc anim_frame ; next drawing
lda anim_frame
cmp #NUM_FRAMES
bcc done
lda #0 ; wrap round to the first
sta anim_frame
done rts
anim_timer .byte 0
anim_frame .byte 0Animation that follows movement looks better when its speed matches the movement: a running character can use the same drawings as a walking one, changed more often, so that its feet do not appear to slide.
A walk is usually drawn as a cycle that returns to where it started. The two key drawings are the contact positions, with both feet on the ground and one leg forward, and between them the passing positions, where one leg swings past the other. Four drawings (contact, passing, contact with the other leg, passing) make a basic walk; more drawings between them make it smoother at the cost of memory.
Commodore 64: change the pointer
A C64 sprite is 24×21 pixels, which takes 63 bytes, and each sprite’s shape occupies a 64-byte block: “one more byte is needed as a place holder at the end of each sprite”. The VIC-II finds the shape through eight pointer bytes, which are “the last 8 bytes of the 1K chunk of screen memory”, at 2040 ($07F8) to 2047 unless the screen has been moved. A pointer value times 64 gives the block’s address in the chip’s 16K bank, so with the screen at $0400, frames stored at $2000, $2040, $2080 and so on have pointer values $80, $81, $82.
SPRITE_PTRS = $07f8 ; with screen memory at $0400
animate_player:
ldx anim_frame
lda walk_frames,x
sta SPRITE_PTRS ; sprite 0 now shows this drawing
rts
walk_frames .byte $80, $81, $82, $83 ; blocks at $2000, $2040, $2080, $20c0Because the pointer is a single byte, one write swaps the whole 63-byte drawing at once, and the change cannot be caught halfway through. A program can also prepare the next frame’s data in a spare block while the current one is on screen, then switch the pointer.
ZX Spectrum: draw the frame
The Spectrum has no sprite hardware. A moving character is drawn into the screen bitmap by the program, so animating it means erasing the old drawing and drawing a different one. The animation step is choosing which drawing to hand to the drawing routine:
; Return HL = address of drawing number anim_frame
; for a 16x16 one-colour sprite: 2 bytes x 16 lines = 32 bytes a drawing.
frame_address:
ld a,(anim_frame)
ld l,a
ld h,0
add hl,hl ; x2
add hl,hl ; x4
add hl,hl ; x8
add hl,hl ; x16
add hl,hl ; x32
ld de,frame_data
add hl,de
retThe cost is drawing time: every change of drawing is a redraw, and an image redrawn while the television beam passes over it can flicker or tear.
NES: change the tile numbers
The NES picture processor holds up to 64 sprites, each 8×8 pixels, or 8×16 when bit 5 of PPUCTRL is set. Each sprite has four bytes in object attribute memory (OAM): Y position, tile number, attributes and X position. Most programs keep a copy of OAM in RAM, often at $0200–$02FF, and copy it to the chip each frame by writing its page number to $4014, which halts the CPU for 513 or 514 cycles while the bytes are transferred.
A 16×16 character in 8×8 mode is four sprites. If each drawing uses four consecutive tiles, frame N starts at tile base + N×4:
OAM_BUF = $0200 ; RAM copy of OAM, sent with STA $4014 in vertical blank
animate_player: ; sprites 0-3 make up the player
lda anim_frame
asl
asl ; x4: four tiles per drawing
clc
adc #PLAYER_TILE_BASE
sta OAM_BUF+1 ; sprite 0 tile (top left)
clc
adc #1
sta OAM_BUF+5 ; sprite 1 tile (top right)
clc
adc #1
sta OAM_BUF+9 ; sprite 2 tile (bottom left)
clc
adc #1
sta OAM_BUF+13 ; sprite 3 tile (bottom right)
rtsIn 8×16 mode the same character is two sprites, and the tile byte works differently: bit 0 chooses which pattern table the sprite comes from, and the other seven bits choose an even-numbered tile for the top half, with the next tile below it. Each drawing is still four tiles, but only OAM_BUF+1 and OAM_BUF+5 change, two tiles apart.
Amiga: point the sprite somewhere else
An Amiga hardware sprite is a block of data in chip memory: two control words giving its position, two words for each line, and two end words. The Hardware Reference Manual explains that the sprite’s DMA pointer moves through the data as the picture is drawn, so “you must rewrite the contents of the sprite pointers during each vertical blanking interval”, normally with instructions in the Copper list. Animation uses that rewrite: point it at a different drawing.
; a0 = the Copper list's two moves for sprite 0:
; dc.w SPR0PTH,0,SPR0PTL,0
animate_sprite0:
move.w anim_frame,d0
lsl.w #2,d0 ; 4 bytes per entry in the table
lea anim_table,a1
move.l (a1,d0.w),d1 ; address of this drawing's sprite data
move.w d1,6(a0) ; low word into the SPR0PTL move
swap d1
move.w d1,2(a0) ; high word into the SPR0PTH move
rtsEach drawing carries its own pair of control words, so the program has to keep the position in every drawing it might show, or copy it into the next one before switching. The manual warns against changing control words while the hardware is reading them, which makes the vertical blank the safe time. A Bob is animated by passing the blitter a different image and mask when it redraws the object.
Facing left and right
| Machine | Mirror in hardware? | Usual approach |
|---|---|---|
| Commodore 64 | No. $D01D and $D017 double a sprite’s width and height, but nothing reverses it |
Store mirrored drawings and switch pointers when the character turns |
| NES | Yes: in each sprite’s attribute byte, bit 6 flips it horizontally and bit 7 vertically | One set of tiles serves both directions. For a sprite made of several tiles, the program also swaps which tile goes on which side |
| Amiga sprites | No | Store mirrored drawings |
| Amiga Bobs | No. The blitter’s descending mode only runs a copy backwards through memory, for overlapping copies; it does not mirror the image | Store mirrored drawings. A vertical flip is possible by starting the source at its last line with a negative modulo, so each row steps back up the image |
With stored drawings, the direction can simply pick which table to read:
; X = drawing number within the walk
get_walk_frame:
lda facing_right
bne right
lda walk_left,x
rts
right lda walk_right,x
rts
walk_right .byte $80, $81, $82, $83
walk_left .byte $84, $85, $86, $87States and one-shot animations
Different actions need different cycles: standing, walking, jumping, attacking. A table of addresses lets the current state choose the cycle:
ANIM_IDLE = 0
ANIM_WALK = 1
ANIM_JUMP = 2
ANIM_ATTACK = 3
get_frame_table: ; set ptr (two bytes in zero page) to the current cycle
lda anim_state
asl ; two bytes per address
tax
lda anim_tables,x
sta ptr
lda anim_tables+1,x
sta ptr+1
rts
anim_tables .word idle_frames, walk_frames, jump_frames, attack_framesAn attack or a death plays once and stops. Here the timer counts down; the program starts the animation by setting anim_frame to 0 and anim_timer to ANIM_SPEED, and a timer of 0 means the animation has finished:
update_oneshot:
lda anim_timer
beq finished ; nothing playing
dec anim_timer
bne finished ; same drawing for now
inc anim_frame
lda anim_frame
cmp #ONESHOT_FRAMES
bcs ended
lda #ANIM_SPEED ; show the next drawing
sta anim_timer
rts
ended lda #0
sta anim_frame ; anim_timer is already 0
jmp return_to_idle
finished
rtsThese are small state machines, driven once per frame by the game loop.
Saving memory
| Method | Saving | Cost |
|---|---|---|
| Share drawings between cycles, such as the standing pose in the walk | Fewer drawings | Movements can look alike |
| Fewer drawings per cycle | Less memory | Less smooth |
| Hardware flipping, where the machine has it | Half the drawings for left and right | Only on machines like the NES |
| Recolouring the same drawings | One set of drawings for several characters | The characters look alike |
Sources
- Commodore, Commodore 64 Programmer’s Reference Guide (1983): p. 131, a sprite is “24 by 21 dots”, 63 bytes; pp. 132–133, the 64-byte blocks, sprite pointers as “the last 8 bytes of the 1K chunk of screen memory” and the location formula; pp. 136–137, expansion at 53277 (
$D01D) and 53271 ($D017); pp. 166–172, “The Dancing Mouse — A Sprite Program Example”, including the line-by-line notes on the pointer and the delay loop. - ZZAP! 64 issue 1, May 1985, pp. 80–81: Tir Na Nog review.
- NESdev wiki, “PPU OAM” (web, secondary): the four bytes of each sprite, 8×16 tile selection, the flip bits in byte 2, and OAM DMA through
$4014taking 513 or 514 cycles. https://www.nesdev.org/wiki/PPU_OAM - Commodore-Amiga, Amiga Hardware Reference Manual (Addison-Wesley, 1985): pp. 102–103, the sprite data structure; pp. 106–107, rewriting the sprite pointers in each vertical blank, by the Copper; p. 110, changing control words only when the hardware is not reading them; p. 176, descending mode.