Last months Revision demo party inspired me to take a few evenings to write an “old skool” demo for the Pico-8. It’s a fairly simple demo that is reminiscent of the Amiga and C64 demo scene, it illustrates a randomly generated star field that is animated within the bounds of the Pico’s screen. To add that retro flare, a text marque scrolls across the footer displaying a message. The built in font set didn’t have that sizzle I was looking for, so I opted for a custom sprite based font.
You can watch the full demo over at Pico-8 BBS.
Star Field Generation and Animation
The position and quantity of stars is randomly generated and stored in a 2 dimensional array.
function gen_stars()
for row = 1, star.num do
star_array[row] = {}
for column = 1, star.num do
star.rand = rnd(star.num)
star.rand = flr(star.rand)
star_array[row][column] = star.rand
end
end
end
To ensure theses locations are generated only once, we call the randomization routine from the _init() function:
function _init()
timer = {}
timer.t = 0
timer.c = 0
star = {}
star.start = 0
star.num = 120
gen_stars()
end
The star animation routine is referenced from the _draw() function. To get the randomized position effect, we utilize the x and y axis of the 2d array by switching the x and y axis references and by passing the coordinate into the pset() function to position a pixel on screen. Inside the for loop, we can get the position of each pixel and by utilizing a combination of if statements that listen to the pixels position, then we can switch direction of each pixel and it’s color.
function star_ani()
local x_move = {}
local color_flash = 7
for y = 1, 10 do
for x = 1, 10 do
x_move = (star_array[y][x]) + timer.t
local y_pos = {}
if (x_move >0) then
y_pos = star_array[x][y] + (timer.t / 10)
x_move = x_move * (x_move / 100)
if (x_move < 130) then color_flash = 7 end if (x_move > 130) then
x_move = 200 - (x_move / 2)
color_flash = 5
end
end
if (x_move <0) then
y_pos = star_array[x][y] - (timer.t / 10)
x_move = x_move * (x_move / 100)
if (x_move < 130) then color_flash = 5 end if (x_move > -200) then
x_move = 1 + (x_move * 2)
color_flash = 7
end
end
pset(x_move, y_pos, color_flash)
end
end
if timer.t <= 250 then
timer.t = timer.t + 1
end
if timer.t >= 250 then
timer.t = -200
end
end
Message Marque Animation
The custom font and message display is a simple sprite based system that stores the message letters in an array. An algorithm then loops through the array and uses the array index multiplied by 8 (the width of the font) to pull the character and position it.
Animating the text along the x axis involves decrementing each characters calculated position by 1. Bouncing the text on the y axis is achieved by implementing a simple if, else statement with three conditions.
Here’s the message render that is called from the _draw() function:
function message_print()
timer.c = timer.c +1
local alt_length = message.length - (message.length * 10)
if alt_length > message.x_move then
message.x_move = 128
end
if timer.c > 30 then
message.x_move = message.x_move - 2
if message.y_move > 110 and message.y_move < 120 and y_flag == true then
message.y_move = message.y_move + 1
if message.y_move == 119 then
y_flag = false
end
elseif message.y_move < 120 and message.y_move > 110 and y_flag == false then
message.y_move = message.y_move - 1
if message.y_move == 111 then
y_flag = true
end
end
for i=1, message.length do
local y_ab = flr(message.y_move)
message.message_pos_x[i] = {}
message.message_pos_x[i] = (message.x_start + (i*8)) + message.x_move
message.message_pos_y[i] = {}
message.message_pos_y[i] = message.y_move
spr(message.message[i],message.message_pos_x[i], message.message_pos_y[i], 1, 1)
end
end
end
The actual message itself, that I mentioned, was created from an array and is declared in the _init() function at program load:
function _init()
timer = {}
timer.t = 0
timer.c = 0
star = {}
star.start = 0
star.num = 120
star_array = {}
letter = {}
message = {}
message.x_start =0
message.y_start =0
message.y_move = 117
message.x_move = 128
l = {
a = 0,
b = 1,
c = 2,
d = 3,
e = 4,
f = 5,
g = 6,
h = 7,
i = 8,
j = 9,
k = 10,
l = 11,
m = 12,
n = 13,
o = 14,
p = 15,
q = 16,
r = 17,
s = 18,
t = 19,
u = 20,
v = 21,
w = 22,
x = 23,
y = 24,
z = 25,
exclaim = 26,
quest = 27,
dot = 28,
dash = 29,
one = 30,
two = 31,
three = 32,
four = 33,
five = 34,
six = 35,
seven = 36,
eight = 37,
nine = 38,
zero = 39,
space = 40
}
-- The Message!
message.message = {l.c, l.o, l.s, l.m, l.i, l.c, l.space,
l.w, l.a, l.v, l.e, l.space,
l.r, l.i, l.d, l.e, l.r, l.space,
l.v, l.zero, l.dot, l.one, l.space,
l.dash, l.space,
l.a, l.space,
l.six, l.five, l.z, l.e, l.r, l.o, l.two, l.space,
l.p, l.r, l.o, l.d, l.u, l.c, l.t, l.i, l.o, l.n, l.dot, l.space,
l.s, l.h, l.o, l.u, l.t, l.space,
l.o, l.u, l.t, l.space,
l.a, l.n, l.d, l.space,
l.g, l.r, l.e, l.e, l.t, l.z, l.space,
l.t, l.o, l.space,
l.dash, l.space,
l.a, l.m, l.i, l.g, l.o, l.s, l.space,
l.p, l.o, l.d, l.c, l.a, l.s, l.t, l.space,
l.dash, l.space,
l.c, l.o, l.r, l.r, l.o, l.s, l.i, l.v, l.e, l.six, l.eight, l.zero, l.nine, l.space,
l.a, l.n, l.d, l.space,
l.t, l.h, l.e, l.space,
l.r, l.e, l.t, l.r, l.o, l.space,
l.h, l.o, l.u, l.r, l.space,
l.p, l.o, l.d, l.c, l.a, l.s, l.t, l.exclaim, l.space,
l.a, l.m, l.i, l.g, l.a, l.space,
l.f, l.o, l.r, l.e, l.v, l.e, l.r, l.exclaim, l.space,
l.dash, l.dash, l.space,
l.a, l.n, l.d, l.space,
l.p, l.i, l.c, l.o, l.dash, l.eight, l.space,
l.t, l.o, l.o, l.space,
l.o, l.f, l.space,
l.c, l.o, l.u, l.r, l.s, l.e, l.dot, l.space,
l.v, l.i, l.s, l.i, l.t, l.space,
l.u, l.s, l.space,
l.i, l.n, l.space,
l.t, l.h, l.e, l.space,
l.w, l.e, l.l, l.l, l.space,
l.a, l.t,l.space,
l.w, l.w, l.w, l.dot, l.c, l.o, l.u, l.n, l.t, l.i, l.n, l.g, l.v, l.i, l.r, l.t, l.u, l.a, l.l, l.s, l.h, l.e, l.e, l.p, l.dot, l.c, l.o, l.m, l.space,
l.a, l.n, l.d, l.space,
l.w, l.w, l.w, l.dot, l.w, l.h, l.i, l.t, l.e, l.o, l.u, l.t, l.l, l.a, l.b, l.s, l.dot, l.c, l.o, l.m,
}
message.length = count(message.message)
message.message_pos_x ={}
message.message_pos_y ={}
music(0)
y_flag = false
gen_stars()
end
Notice how I abstracted the sprite positions with letter variables, this makes writing out your message “a little” easier.
You can download the full source code and Pico-8 cart at Github, here.
Thanks for reading, I hope that this demo will help your own endeavors in some way.