Walky Robot, a cart for the PICO-8 fantasy console

January 2, 2018

While it’s more a digital toy than a game, I guess you might stretch to calling it a walking simulator.

TL;DR

I made a visual toy with PICO-8.

You can play with Robot in your browser or download the cart as a PNG file.

So what is this thing anyway?

About a year ago, over the holiday break, my friend John released a VSCode plugin for doing PICO-8 development.

This convinced me to try two things I’d not yet used - but had been meaning to try - VisualStudio Code and PICO-8.

PICO-8

PICO-8 is a fantasy console for making, sharing and playing tiny games and other computer programs. When you turn it on, the machine greets you with a shell for typing in Lua commands and provides simple built-in tools for creating your own cartridges. — lexaloffle.com

This spurred me to try it out. With fresh copies of both PICO-8 and VSCode I got started.

“Walky Robot” (robot.p8) was the first thing I made.

Robot

Advice I received years ago was to just finish one small thing no matter how trivial it feels.

I created a little sprite, and could animate him flying? by swapping sprites.

Once I had my sprite I got him moving, up, down, left and right.

This was easy enough to to imperatively, but I wanted to try a different approach.

s = {
  moving=false,
  sp=3,
  facing = "left",
  x = 40,
  y = 60,
}

The robot data table contains x,y, and sprite, and I wrote a function character_directional_movement to get a new state for the table based on the global state, i.e. player key presses.

I wanted to keep functions side effect free, so this function doesn’t mutate the passed in character, all the mutation happens in the core _update function.

Below is the final version with a few details stripped out, but the first version didn’t yet account for facing or sprite swapping.

function character_directional_movement(character, player_id)
  -- Copy old x,y,facing values
  -- mark moving as false
  -- Compute notional velocity based on screen position
  -- Test for button presses
  -- Calculate new pos based on direction & velocity
  -- return new state
  return {
    x = new_x,
    y = new_y,
    facing = new_facing,
    moving = new_moving
    }
end

Robot 2.0

While I really liked the flame effect on my flying robot, I wanted the robot to be able to face in a certain direction, and to be animated walking in each of those directions.

So I made a new robot.

To make my robot move in 4 directions, and look like its actually moving, I needed to both animate it, and add facing directions.

I just used a hash lookup for this, adding fields to the robot data table.

s = {
  -- ...
  facing_sprites = {
    left  = { 3, 4 },
    right = { 19, 20 },
    up    = { 21, 22 },
    down  = { 5, 6 }
    }
  }

Each facing is a pair of sprites, so we pick the correct facing, and toggle between the two sprites about half the time. This function takes the character and the global clock.

function character_sprite(character, time_point)
  local animation_set = character.facing_sprites[character.facing]

  if (time_point % 10 < 5) then
    new_sprite = animation_set[1]
  else
    new_sprite = animation_set[2]
  end

  if (character.moving == false) then
    new_sprite = animation_set[1]
  end
  return new_sprite
end

Aesthetics

I’d been listening to a lot of synthwave, so was aesthetically in that zone. I wanted something reminiscent of the “RetroWave” style.

I found an example cart Invader Overlord overlord.p8 by morningtoast in PICO-8’s “splore” mode which had a grid effect I liked, and copied the code over.

screenshot of invader overlord cart start screen

-- horizon grid, by electricgryphon
grid={
  color=0,mfg=7,mbg=2,
  hfg=13,pbg=2,
  draw=function()
    w=127
    n=15

    background.t+=.75`
    for i=0,n do
      z=(i*n+background.t%n)
      y=w*n/z+32
      line(0,y,w,y,8)
      v=i+background.t%n/n-n/2
      line(v*9+64,40,v*60+64,w,8)
    end
  end
},

Pretty cool, but I thought a glow effect might make it more rad! Dude.

This turned out to be just a simple bit of redraw trickery.

I extracted a function to draw the grid, and let the function take an offset value. This allows drawing a full grid repeatedly.

offset_grid=function(bg, w, n, offset, current_color)
  for i=0,n do
    z= (i * n+bg.dy % n )
    y= w * n/z + 32
    -- Horiz (y)
    line(0, y + offset, w, y + offset, current_color)

    v = i + bg.dx % n / n-n / 2
    -- vert (x)
    line(v*9+64 + offset, 40, v*60+64 + offset, w, current_color)
  end

Then I just call it 3 times in a row with different colours and offsets

Note that I cannot render the shadow and the top lines in the same loop, as subsequent glow lines will overdraw the main grid.

-- Pink underlines first
offset = -1
offset_grid(background, w, n, -1 , grid_bg.color_b)
offset = 1
offset_grid(background, w, n, 1, grid_bg.color_b)

-- Blue overline on top.
offset = 0
offset_grid(background, w, n, offset, grid_bg.color_a)

City Generation

The city terrain is a set of rectangles, generated randomly once at game start. They are drawn in silhouette over the stylized sky, so the fact that they overlap and are all steadily increasing in z-index is not a big deal.

This is easier to see with animation, or more colour!

All coming together

You can play with Robot in your browser or download the cart as a PNG file.

The robot code is also available on github.

You can also shuffle between rendering different layers using the keys on your keyboard, probably z and x.

PICO-8 next

I had heaps of fun making this, picking colours, messing with simple geometry and getting creative within the constraints of the PICO-8 to give some neat results. It’s a fun little toy.

Soon I’ll write up the second thing I made, a clone of scorched earth in the PICO-8.