How to make a PICO-8 game: Part 5

Score!

Now that things can collide and the player can effectively die (see last time), the next job is to score some points. Doing this is surprisingly easy, and so this lesson is going to be very short.

Part 5: Score!

For the purposes of this game, simply staying alive is how you score points. Maybe we’ll add bonus points or something later, but for now, the player will get one point for every frame they stay alive.

We need to do three things: set an initial score, update that score, and draw the score on the screen. Hopefully you can see where this is going.

In _init(), we’ll initialise the score to zero:

Then, in _update(), we’ll increment it (so it goes up by one point each time _update() is called. We’ll do that at the end of _update() in case we die that frame:

And finally, we’ll draw this on the screen in – you guessed it – _draw(). We need to draw it after we clear the screen with cls (or we won’t see it), but it’s an artistic choice as to whether our sprites are “on top” of the score, or the score is “on top” of the sprites.

Of course, we could have a status area on the screen for this (where the gameplay area won’t encroach), but for now we’re keeping it simple. Let’s print it at 100,0 so it’s in the top right but has space to allow for multiple digits in case we’re that good, and in colour 7 (white):

And that’s literally all there is to it.

I said it was going to be short! Let’s catch up with the code so far:

 

How to make a PICO-8 game: Part 4

Crash! But not that sort of crash.

In Part 3, we made an enemy chase the player. Which was nice, but nothing actually happened when the player was caught. Why not? Because we had no code to check for collisions! Let’s fix that.

Part 4: Collision check

What is a collision? Well, for the purposes of our game – and most games – it’s when two objects overlap. In our case, it’ll be our player sprite and our enemy sprite. That’s pretty simple to understand, but how do we code it?

Let’s look at the definition in plain English. Both sprites are 8 pixels wide, and we need to check if any part of each sprite is “inside” the area of the other. Things will be different for different sized sprites, but let’s keep it simple for now.

The player sprite’s four corners are:

And the enemy sprite coordinates are:

So when they overlap, all of the following must be true (follow along with two squares of paper if that helps!):

  • The right edge of the enemy must be to the right of the left edge of the player (ex+8 > x), AND
  • The left edge of the enemy must be to the left of the right edge of the player (ex < x+8), AND
  • The bottom edge of the enemy must be below the top edge of the player (ey+8 < y), AND
  • The top edge of the enemy must be above the bottom edge of the player (ey > y+8)

In PICO-8, that translates to:

Let’s plan ahead a little, and assume we’re going to reuse the collision code for other things later on. After all, we might add more enemies, right? Let’s create a function to check collisions between two objects, using this code as the base. The function will return true if there’s a collision, and false if not:

Now we can send the coordinates of any two objects to that function and get a true or false in return. We’ll put a call to it in the _update() function (before we move anything), and stop the game if there’s a collision. Later, we can lose a life or something instead, but for now we’ll just stop().

Remember that “if checkcol(x,y,ex,ey) then” implies “if checkcol(x,y,ex,ey) == true”.

Here’s a catchup of all the code so far:

 

The ugvm Podcast is still happening

And I’m just as surprised as anyone.

We’ve done four proper (and one pilot) episodes now. There’s a scattershot selection of ugvm colleagues joining me each time, we’re still tweaking the formula (and the recording method), and the “let’s make it an hour long” goal is now a running joke. But here we are, and I think it’s pretty decent actually.

I’ve no reason to suspect, or even hope for, mass recognition and we’re just one of a billion videogame podcasts, but that isn’t really important. We’ve listeners outside of the ugvm group. We’ve had positive feedback and constructive criticism. That’s not really important either, to be honest. No, I’m finding the best bit is just having a chat about games with some mates. Some of whom I’d never actually spoken to.

Anywho, Episode 4 went up this week. We chatted about lootcrates and Mario and Sonic and all sorts. You can find it here.

How to make a PICO-8 game: Part 3

Chase me.

Last time, we got a square moving round the screen and accounted for bumping into the walls. This time, we’ll give old Squarey a reason to move around – something to chase him. Or her.

Part 3: Chase me

Right back at the start, we created an extra sprite to use as an enemy – Sprite 2. We’ll make use of that now.

Firstly, we need an initial position for our enemy. As we’re using x and y for the player, let’s use ex and ey for the enemy, and start it in the bottom right of the screen. 120, 120 perhaps. We’re also going to assign a speed to it – how far it moves each frame – and call it d (for distance, in pixels). We put these assignments in our _init() function:

We also need to display this enemy on the screen, so in the _draw() function, remembering the enemy sprite is in slot 2, we can use:

All this does so far, though, is show a square in the bottom right. We still need to make it move, and to do that we need to check where it is in relation to the player. In English, we want this:

“If the player is to the left of the enemy, move the enemy left”

“If the player is to the right of the enemy, move the enemy right”

That is, if x is less than ex then the player is to the left, and if x is greater than ex then the player is to the right. We can do the same for up and down with y and ey.

So how do we make it move left? Decrement ex. Right? Increment ex. Up? Decrement ey. Down? Increment ey. And how much do we increment or decrement by? Our variable d. Easy! Let’s do the if checks in _update(), after we’ve moved the player:

Now run the code, and watch how the enemy chases the player. Very, very slowly. Have a play with the value of d in the _init() function to see how this changes the speed of the enemy – try 0.1 or 1 instead of 0.05, for example.

Chase me!

Obviously, the game is very easy at this point, and not least because nothing happens if the enemy catches you. Why not? Because we’ve not coded any collisions yet! That’s what we’ll do next time. For now, here’s all the code so far, with some additional annotations so we’ll remember what everything does:

Play Want Bin Expense: 2017-11-20

I had a Switch once.

It’s been a while. Well, a short while.

Play

Super Mario Odyssey (Switch)
Don’t think that because I put the game down, I put the game down. If you see what I mean. No, because it’s definitely a very good game – there’s no question about that. It just isn’t the best game. Anyway, I’ve gone on about that elsewhere a lot. Now, I’m well into post-game Moon Collectathon and although in some cases the difficulty level has increased, making it more enjoyable, in many it hasn’t. I’ve also hit a bug because I’ve collected 97/100 purple doofers in the Sand Kingdom, but I can’t find the other three. Even following a guide, I’ve collected all 100 but the counter still says 97. Annoying.

Stardew Valley (Switch)
Quite a bit more of this, but playing both Stardew Valley and Mario is hard now because my daughter keeps stealing the Switch for herself. When I do get a change to play, I’m mostly farming the hell out of my, er, farm, while I wait for the relative down-period of Winter so I can attempt the desert mine properly.

Bye-Bye BoxBoy! (3DS)
So I’ve started playing this in Switchless moments. Not that I’m complaining because it’s very good! It’s more of the same sort of stuff as the first two games with some new twists, but that’s fine with me. I think I’ve almost completed it too.

Want

Fire Emblem Warriors (Switch) as always.

Bin

Not having two Switches.

Expense

I got The Lego Ninjago Movie The Videogame for the Switch for essentially free with Nectar points.

Also free from various places, were Syberia (Mac), Watch_dogs (PC) and Killer is Dead (PC). Yay for free!

How to make a PICO-8 game: Part 2

I like to move it.

In the last chapter, I showed you the very basic task of putting a sprite on the screen. That’s all very well, but a bit less interactive than most games are. In this chapter, let’s make it move.

Part 2: Move the player

The first task here is to make PICO-8 check to see if the player is pressing any of the direction keys. You can do this by making use of the btn() function.

The arrow keys (or joypad d-pad, depending how you’re playing the game) are referred to by the numbers 0 to 3. The left key is 0, right is 1, up is 2 and down is 3. If you check to see, for example, if btn(0) is true, then you’re checking to see if the player is pressing left.

You’ll recall that the coordinates of the player sprite on the screen are stored in the variables x and y. To physically make the sprite move left, you need to make the value of x smaller, and to move it right you have to make it larger – remember that the left of the screen is 0, the right is 127. Let’s make it move by one pixel at a time for simplicity’s sake. Ignoring up and down for the moment, this code in the _update() function will do that:

In plain English, this reads as:

“If the left key is being pressed, then decrease the value of x by 1”

“If the right key is being pressed, then increase the value of x by 1”

You could be a little more verbose and use the longer forms if you wish:

When you do a check on a function, looking for a “true” is implied, and I prefer the shorter form of the increment and decrement command, but both work. Just note that when you assign a value to a variable you use one equals sign (=), but when you do an “if” check, you use two (==).

Run your code now, and see how the sprite moves left and right as you press left and right. Magic!

Of course, this only deals with left and right. Let’s add the code for up and down, using btn(2) and (3) and changing y as appropriate:

Now, you’d think that was the end of it, but there’s a flaw here. You may have noticed that the sprite can move off the screen where you can’t see it. That in itself isn’t necessarily a bad thing, but for the purposes of this game we don’t want that to happen.

To fix it, we need an additional check for “screen edge detection”. When moving left, we need to make sure that x is only decremented when x is already greater than 0. Similarly, we can only move up if y is greater than 0.

For the right and bottom edges, it’s slightly more complicated. Although the edges are at 127 pixels, our sprite’s x and y values refer to the top left corner of the sprite, which is 7 pixels before the right and bottom edges of the sprite – the sprite is 8×8 pixels, remember. Instead of then checking to see if the sprite is at 127 pixels, we need to check for 120.

We can add this check at the same point we check for a button press by modifying the “if” statements. For example:

If we finish this off to include up and down, we can complete our code (for now!):

Next time, we’ll add an enemy to chase the player!

How to make a PICO-8 game: Part 1

Let’s Make A Game.

Since I’m now teaching people how to make programs in PICO-8, I thought I might as well put my teaching notes here for others who may find them useful.

A few caveats:

I am not an expert. Yes, there are better ways of doing some of what I’m explaining (and some of the improvements will come in later parts of the “course”), but my “class” consists of children, some of whom will have no coding experience past Scratch so bear that in mind. In particular, I will be using longhand for variables, and objects/classes will be used later on, to keep things readable, simple, and followable. Followable is definitely a word. Also, this won’t teach you how to use PICO-8 itself, just how to program with it. There’s a manual for that.

The game we’re going to create is very simple. There are two sprites, and one sprite is player controlled and chased by the other sprite. That’s it. Told you it was very simple.

Parts of a PICO-8 Game

There are three main functions to deal with in a basic PICO-8 program: _init(), _update() and _draw(). Note the underscores – without them your program won’t work.

_init()

This is called at the start of your program when you run it. Unless called again, it runs only once. It’s here where you put all your setup stuff, variable initialisation, and so on.

_update()

This is called over and over again, 30 times a second. In here you put your “game logic”, updating variables such as object locations, checking for collisions, increment the score, and so on.

_draw()

This function is where all the drawing on the screen takes place. So you’d print the score, draw the sprites, and so on in the _draw() function. It is also called 30 times a second if it is possible to do so – if the code is taking to long to run it may drop  _draw() calls so that _update() has time to run properly.

Just a few things to mention before I show you any code:

Firstly, comments in the code (that is, notes to you rather than actual code) are either lines starting

which comments out that line, or

which comments out everything until

Secondly, it’s good practice to give all your functions and variables useful names. PICO-8 does have a character limit so you may need to make them shorter and therefore less useful in the future, but not yet!

Finally, indent your code. When you open a function, a loop, an if/then or anything like that, indent everything in it. It makes it a lot easier to read.

If you follow these three rules, your code will be nice and readable – important if you come back to it weeks or months later, or give it to someone else, but also it helps a lot when things aren’t working as you’d expect.

Part 1: Put some stuff on the screen

First off, we’ll need a sprite to act as the player. We’ll just draw a blank square for now in the sprite editor, and use the sprite slot 1 to store it. We won’t need it yet but lets also create an enemy sprite the same only a different colour in slot 2.

Two basic sprites

Now, in the code editor, lets put the three basic functions I mentioned above:

We’ll choose now what the start coordinates of the player sprite are. The PICO-8 screen is 128×128 pixels, with the top left of the screen at 0,0, and the bottom right at 127,127. The middle will be roughly 64,64 then. We’ll call the x and y coordinates of the player, well, x and y. Why not?

Since they’re being initialised, we put these in _init():

At this point, we’re not going to move it about, but we do need to draw it. Usually in a PICO-8 game, you’ll want to clear the screen at the start of each draw frame, so we’ll make use of the cls() command to do that. We can choose what colour to clear the screen to by putting the colour number in cls(). You can use this palette to chose one:

cls(2) will clear the screen to a sort of purpley mauve, for example.

Then we’re going to draw the player sprite on the screen at 64, 64 – now known as x and y. This is done with the spr() command, and the coordinates refer to the top left corner of the sprite. We also need to tell spr which sprite to use, in this case the player sprite, which is sprite number 1.

And there we go:

Best. Game. Ever.

Next time, I’ll show you how to make the thing move around the screen, but for now, here’s all the code so far:

Game Dev Diary: Run Baby Run v0.03

No, not the Sheryl Crow song.

Now let me just point out a few things here before I begin:

  • Yes, it has been a long time since I did any game dev stuff
  • No, I have no idea if I’ll finish this
  • Run Baby Run is the best game ever

When I was doing Asset Reuse Invaders, the level table I developed for the invaders made me wonder if I could use the same thing to create a level map for a maze. Then I thought, what if I used the levels from Run Baby Run? Then I thought: what if I remade Run Baby Run?

Then 12 year old me’s brain exploded.

I looked into it a bit further, and found that the levels on the Spectrum original were 32 “tiles” wide, and 22 tiles high. On the Spectrum, each tile is 8×8 pixels, but if I use 4×4 tiles, then 32 tiles fit exactly into the 128 pixel wide PICO-8 display.

I had to do it. Or at least try. The normal mapping method used by PICO-8 relies on 8×8 pixel tiles, so I had to create my own – reusing what I’d learned from the level table structure of my invaders game.

Yes, there’s a way of compressing this or something, but I’m not short on space (yet) and it makes it easier to see in the code. I don’t know why I’ve done level 2 (Huddersfield Foundry) first, but it doesn’t matter.

The code to turn this into an image on the screen is:

And so, this (from the Spectrum):

Magically becomes this, on PICO-8:

Not bad, eh?

That was my first job. My second task was making the car rotate correctly. I managed that successfully, complicated slightly by creating the car out of two 4×4 pixel sprites rather than one single sprite. There’s a reason for this, which I can’t quite remember now, but has something to do with collisions. Then I ran into issues with collisions, so the rotation code needs to be partially re-written. Regardez:

It’s too fast, obviously, but the big issue is that although head-on collisions with the walls is correctly identified, turning left and right into them is not. I’ve identified this is down to the order in which the rotation and the collision detection take place, but to change it means the rotation code needs re-writing.