How to make a PICO-8 game: Part 8

Enemy objects

Last time, we got halfway through butchering our previously working game, leaving it in a state where it refused to even run. This time, we’ll fix that.

Part 8: Enemy Objects

Now that there are multiple enemies, and they’re all objects, we can no longer use any code that refers to the old variables of ex and ey, or any other “single enemy” legacy code. Currently, that also means that collisions no longer work.

Luckily, the function checkcol() only needs to be sent two lots of x and y values, so still works fine providing we send it the right variables, so we can leave that alone. However, we need to replace the code that calls it.

At the moment, it says this in _update():

which doesn’t work – note the ex and ey. Instead, lets call this function for each enemy, and pass that enemy’s x and y to the function. Like this:

Be extra careful with the brackets here – the foreach encloses the code in them!

Hopefully by now what this code does is clear, as it’s similar to the enemy  draw routine: It runs through each enemy in enemies{} and triggers checkcol for each.

That’s collisions sorted. Our code won’t run yet, because we still have to fix the enemy move and enemy speed increase routines.

At the moment, moves are dealt with here:

and again, ex and ey need replacing.

Why not include a move function as part of the enemy object itself, like how we included a draw function? After the en.draw function in make_enemy(), we can do this:

You’ll notice this is essentially the same code, only ex and ey are now this.x and this.y (as they refer to en as “this”), and it’s laid out on fewer lines.

Of course, we need to call this in turn like before, so replace the move code in _update() with:

Now, we need to replace the d += dinc code in _update(). It currently still refers to a single “d”, whereas every enemy has it’s own. It should be easy enough now:

And the code works again! Only… there are no enemies. Let’s put some in.

To start with, we’ll create one enemy in _init(). After the enemies = {} code, we’ll call make_enemy() with some attributes:

Remember, in order, these values are:

Sprite number (2), x and y coordinates (120 and 120), d (“speed” of the enemy, 0.05), how many “animation ticks” there are currently (0), and how many animation ticks there are in total (5).

Now we’re back to where we were before we broke the game. But we did that to create more enemies whenever we wanted, right? Right. Let’s do that then. For simplicity, we’ll create one every 200 ticks, at the same time we speed the enemies up. So, after ticks=0 in _update():

“rnd” means random number, so “rnd(128)” means pick a random number between 0 and 128. It actually means fractions of numbers too, with 0 being the lowest possible number, and 127.999 the highest. That’s accurate enough for us here, and it provides a way of randomly generating x and y coordinates for our new enemy.

Now run the game. Every 200 “ticks”, we get a new enemy! That seems like a lot of work just to get multiple enemies, but you can see if you implement something like this at the start, updating, moving and drawing many, many enemies (or other objects) all in one go is a breeze.

Next time, we’ll look at making our game a bit more visually interesting, by adding animation to the enemies.

All the code so far:



Leave a Reply