loading...
Cover image for Learning Pico-8 & Lua, first run

Learning Pico-8 & Lua, first run

mattiadimeglio profile image MattiaDiMeglio Updated on ・6 min read

What is Pico-8

"PICO-8 is a fantasy console for making, sharing and playing tiny games and other computer programs". Basically is giving yourself a bunch of limitation in quantity of code and memory, because is sincerely fun and fairly easy to pick up.
It's based on a version of Lua and it offers a full suite of creation tools, like a sprite editor and a music creation tool. It's basically an all-in-one system to make games. Also there's a beautiful community based on it, all around the internet, so there's no actual reason not to buy it here

The starting screen of pico. You can load files or explore other people creations with the "splore" command.
The starting screen of pico. You can load files or explore other people creations with the "splore" command.

Starting up

First things first, trying to learn something new is always kinda scary: you don't really know what to do or search for and you try looking at a bunch of tutorials online (some good, some not so much). You follow with the teacher and maybe make something functional, a to-do list, snake or whatnot and then two days pass and you forget whatever you thought you learnt. Been there, done that, and I found out (after reading tutorial about how to learn stuff. Jarring, I know) that a good way to actually learn something is, after watching a couple of tutorials, just to get you on your feet, you need to make something alone, implementing what you learnt and solving actual problems.

First tutorial
The result of one of the tutorials I followed, I'm bad at this game.

So here we are, I first followed the very nice tutorials on the "game dev with pico8" fanzine and now I need something to implement.

My university career came in handy, for once

I'm preparing my last exam, the laboratory part of algorithms and data structures and at the end of the syllabus they talk about pathfinding in graphs and in labyrinths and give some examples of easy labyrinth generation algorithms, so I decided to try to implement those in pico.

The first run

The first thing I needed to do was adapt the code to generate a labyrinth in Lua and make it work in pico. The basic idea was to use tables (That I didn't understand when I started this, and don't feel comfortable with now. My learning plan here is indeed working)

Labyrinth generation, basic idea

It's fairly simple: create a set of random direction and, starting from the first room remove the walls in the direction you got.

It's bad, I know and I will remake it asap. It's like that because it's an adaptation of a recursive algorithm, in which you start in the first room and generate a random direction, then for each direction you remove the wall and call the function on the adjacent room, creating a cleaner result. The problem is, pico doesn't seem to like recursion and I didn't want to implement stacks in pico, 'cause I didn't like tables and I'm lazy).

function make_doors(room)
    local x = (room.x+flr(rectsize/2))
    local y = (room.y+flr(rectsize/2))
    local dirs = {flr(rnd(4)), flr(rnd(4))}
    local id = room.index

    for d in all(dirs) do
        if (d == 0) then
            if(id > 12) then
                room.du = 1
                rooms[id-12].dd = 1
            end
        elseif (d == 1) then
            if ((id%12) > 0) then
                room.dr = 1
                rooms[id+1].dl = 1
            end
        elseif (d == 2) then
            if ((id) < 133) then
                room.dd = 1
                rooms[id+12].du = 1
            end
        elseif (d == 3) then
            if (((id)%12) > 1) then
                room.dl = 1
                rooms[id-1].dr = 1
            end
        end
    end
end
My code is cleaner, usually. I promise

Obviously, "rooms" is a table, which is not 2D, so I need to advance of 12 rooms to get to the next row ( it's a labyrinth made of 12x12 rooms). So to go to the room under or over the current one I need to move by 12 and checking the mod 12 I can know in which column I'm in (i.e. if id%12 == 1 I know I'm in the first column because Lua index is 1-based. It took me like a day to figure that out and my C heritage doesn't like this)

function make_rooms(i, offset, rectsize)
    local cy = offset + (flr((i/12)) * rectsize)
    local cx = offset + ((i%12) * rectsize)
    room = {
        x = cx, //coordinate
        y = cy,
        du = 0, //door up
        dd = 0, //door down
        dl = 0, //door left
        dr = 0, //door right
        index = i+1
    }
    add(rooms, room)
end
This is how I make rooms. Rectsize is the room size and offset is used to centre the labyrinth in the screen. The index is incremented because I use it for the coordinate generation, but I needed it starting from 0

The player, the navigation and "light"

In my mind, the game is an extremely basic and kinda crude top-down dungeon crawler thingy and I wanted to give the idea that every room, other than the one you're in, is pitch black and the character is carrying a light of some sort.

To do that, I just thought of drawing only the room the player is in and faking the effect of light illuminating a doorway.
I initially thought of ray-casting and probably will be the way I'll implement it, in the end, but for now, I wanted something fast and easy to see how it would look in motion (I was stressing out on the labyrinth generator and I needed some wins, ok? I'm sorry)

So what I've done is a really bad false ray-casting of light using horizontal and vertical lines

end result

The end result. It's not too bad and it's working at 30 fps. I know the player sprite is very nice, thank you for noticing.
function draw_rooms(room)
    local x = (room.x+flr(rectsize/2))
    local y = (room.y+flr(rectsize/2))
    rectfill(room.x, room.y, room.x+rectsize, room.y+rectsize, 6)
    if (room.dl == 0) then
        line(room.x,room.y,room.x,room.y+rectsize, 5)
    else
        for i=room.y+1, room.y+rectsize-1 do
            line(room.x, i, room.x-(rectsize/3)+(abs((room.y+(rectsize/2)-i))), i,6)    
        end
    end
    if (room.du == 0) then  
        line(room.x,room.y,room.x+rectsize,room.y, 5)
    else 
        for i=room.x+1, room.x+rectsize-1 do
            line(i, room.y, i, room.y-(rectsize/3)+(abs((room.x+(rectsize/2)-i))), 6)   
        end
    end
    if (room.dr == 0) then  
        line(room.x+rectsize,room.y,room.x+rectsize,room.y+rectsize, 5)
    else 
        for i=room.y+1, room.y+rectsize-1 do
            line(room.x+rectsize, i, (room.x+rectsize+1)+(rectsize/3)-(abs((room.y+(rectsize/2)-i))), i,6)  
        end
    end
    if (room.dd == 0) then  
        line(room.x,room.y+rectsize,room.x+rectsize,room.y+rectsize, 5)
    else 
        for i=room.x+1, room.x+rectsize-1 do
            line(i, room.y+rectsize, i, room.y+rectsize+1+(rectsize/3)-(abs((room.x+(rectsize/2)-i))), 6)   
        end
    end
end
the draw_rooms function. It draws the room and the "light"

Next up

Ok, what I want to do is:

  • Remake(?) the labyrinth generation algorithm
  • Faking raycasting again, maybe with lines starting from the character and running in all direction and stopping when they hit a wall
  • I need to implement A*, to check if the labyrinth has an actual exit
  • Modify the labyrinth without exit or remove them

What I've learned

  • Fake it 'till you decide to actually make it, I suppose

That's all folks, thanks for reading

Posted on by:

mattiadimeglio profile

MattiaDiMeglio

@mattiadimeglio

A computer science student from naples, interested in gamedev and ux design.

Discussion

markdown guide