Tron Style 80’s Animated Landscape on Pico-8

The 8-bit style of the Pico-8 lends itself well to the 80’s cyber genre. I’ve been throwing around some ideas for a demo or potential game. A 3d style animated landscape seemed like a good place to start.

The concept is simple enough, a series of horizontal lines generated in a FOR loop that are animated down the screen, their start time is staggered and the lines are accelerated.

The vertical lines that give the illusion of perspective are generated from two FOR loops, one for either side of the middle of the screen. One loop increments while the other decrements.

The _init statement sets up the foundation objects and variables for the animation. Notice the “start” and “offset” vars, together these control the vertical start position of the horizon. “Start” will adjust the starting spacing of the horizontal lines.

function _init()
	start = 5 -- Starting distance between horizontal lines
	offset = 55 -- Starting vertical position for lines
	lineh = {}
	lineh.moveNum = {} -- Create the line object array
	last1 = time() -- Init animation timer
	start_line_num = 0 -- Init line counter, puts a delay on the for loop
	lineCont = {} -- Array 1 of vertical lines (left side of screen)
	lineCont2 = {} -- Array 2 of vertical lines (right side)
	lineNum = 0 -- Start the vertical lines horizontal 
	lineNum2 = 0

	lineEach = 5 -- Starting position and spacing horizontal lines

The Pico-8 _draw function references two sub routines, the first is for the horizontal lines and is basically a huge FOR loop of 800 indexes, each pass through the loop creates a line.

-- Horizontal line generator
function horz(n) -- Param is supplied by _draw, it's an appearance timing offset
	for i=1, 800  do -- Create 800 lines
		if i < n then -- Space the indexes (lines) to be released from the start position based on the supplied timer.
			lineh.moveNum[i] = ((lineh.moveNum[i] * 1.15) / 1.1) -- each line index gets it's acceleration 
			lineEach = lineh.moveNum[i] -- Abstract the index
			if (lineEach < 168) then -- if the acceleration is below the bottom of the screen continue
				lineEach = lineEach
			else -- else just stack the lines up, stop moving them
				lineEach = 168
			lineh.moveNum[i] =  start -- If it's not this lines turn, wait.
		local lines = line(0, (lineEach  + offset), 180, (lineEach + offset), 14) -- Draw the animated line
	line(0, (start + offset), 180, (start  + offset), 14) -- Add static line to fix loading bug

The second subroutine manages the vertical lines, as previously mentioned it comprises of 2 loops to draw each side of the screen. For added effect, I set each line to be perfectly horizontal and animated the y position with an incrementing value until the spacing reaches it’s maximum space of 40 pixels. As with the acceleration of the horizontal lines, I’m multiplying each index of the FOR loop by an external value.

-- Vertical perspective lines
function vert()
	for i = 5, 1, -1 do -- Create 5 lines on the right
		lineCont[i] = (lineNum * i) * i -- Simple drawing of the lines using acceleration 
		line(64, (start + offset), (lineCont[i]+60), 128, 14) -- Draw each line
	for i = 1, 5, 1 do -- Create 5 lines on the left
		lineCont2[i] = (lineNum2 * i) * i
		line(64, (start +  offset), (lineCont2[i]+60), 128, 14)
	if lineNum < 40 and lineNum2 > -40 then -- Space each line by 40 pixels
		lineNum += 1
		lineNum2 -= 1

Lastly, I bring it altogether in the _Draw function. Inside this function I using a simple timer to increment a value that passes up to the horizontal line subroutine. This will create the staggered release of each line.

function _draw()
	if (time() - last1) > 0.20  then -- Simple delay 
		start_line_num += 1
		last1 = time()
	horz(start_line_num) -- Draw horizontal lines, send the delay to the to the sub-routine.


The full project can be found on Github here.

Leave a Reply

Your email address will not be published. Required fields are marked *