cherry, cherry!

Memento coding

Tuesday, 21 May 2019

Collisions and cupcakes with PICO-8


Note: this post was also published to Medium.

Following this post you are going to develop a small game featuring collisions with PICO-8.

Game concept
The game is a tribute to Lazy Jones’ “The Turk”. We are going to develop “The cupcake”, same concept...different dish!

In this game, cupcakes are delivered through a conveyor belt and the player tries to get them with a spoon, while being disturbed by a wasp wandering through the screen.

Sprite design
As usual, you are going to draw sprites first. You need to draw:

- the cupcake
- the spoon
- the wasp
- the belt
- the floor

Here’s how I designed them but feel free to draw your own!


Map design
The map is a grid of sprites and you can place sprites on map by using map editor.
The entire map is 128x128 pixels and every sprite with zoom 1x takes 8 pixels, so the map can be divided into 16 rows and 16 columns.
While x position starts from 0 at left and increases going to the right, y is 0 at top and increases going down to the map floor.

Put the floor and the belt on the map, you will place the spoon, cupcakes and the wasp programmatically on screen.


Just to follow this tutorial, the floor should be 5 tiles high and the belt should be placed 5 tiles from the top.
Game developmentTo keep it simple, just begin by implementing cupcakes moving on the belt, because they only goes from left to right all the time and no user interaction is required.

Define a cupcake variable to hold the cupcake sprite number and the current cupcake position on screen.

cup = {};

Init the cupcake position and sprite in a dedicated function initCup():

function initcup()
  cup.x = -8
  cup.y = 24
  cup.sprite = 1
end

in the moveCup() function, you will define how the cupcakes (even if it’s a single cupcake!) are going to move:

function movecup()
  if (cup.x <= 128) then
    cup.x = cup.x+1
  else
    cup.x=-8
  end
end


Here, if the cupcake goes beyond the right margin of the screen is placed beyond the left side, otherwise it goes left.

To develop wasp behaviour, define a new wasp variable:

wasp = {}

The wasp will pop in a random location on screen, so you will define initwasp() this way:

function initwasp()
  wasp.x = rnd(8*13)
  wasp.y = rnd(8*6)
  wasp.sprite = 3
  wasp.up = true
  wasp.left = true
end


wasp variable also defines two additional fields “up” and “left” which aid to control the “random” movement across the screen.

movewasp() function uses wasp position and flags to determine wasp’s next direction:

function movewasp()
  if (wasp.y >= 64) then
    wasp.up = true
  end
  if (wasp.y <= 0) then
    wasp.up = false
  end
  if (wasp.up) then
    wasp.y = wasp.y-1
  else
    wasp.y = wasp.y+1
  end
  if (wasp.x <= 0) then
    wasp.left = false
  end
  if (wasp.x >= 116) then
    wasp.left = true
  end
  if (wasp.left) then
    wasp.x = wasp.x-1
  else
    wasp.x = wasp.x+1
  end
end


You are going to implement spoon movement last, so you are going to define the last variable “spoon”:

var spoon = {}

Since spoon has to be launched, the variable is going to have an additional flag “launched” which will prevent x-axis movement during the launch phase.

function initspoon()
  spoon.x = 0
  spoon.y = 64
  spoon.sprite = 5
  spoon.launched = false
end


You will also define movespoon() to implement spoon behaviour:

function movespoon()
  if (btn(0) and not spoon.launched) then
    if (spoon.x >= 0) then
      spoon.x=spoon.x-1
    end
  end
  if (btn(1) and not spoon.launched) then
    if (spoon.x<116) then
      spoon.x=spoon.x+1
    end
  end
  if (btn(5)) then
    spoon.launched = true
  end
  if (spoon.launched) then
    spoon.y = spoon.y-1
  end
  if (spoon.y <=0) then
    initspoon()
  end
end


While the spoon is on the ground, it can be moved freely along the x axis; when it has been launched, it can only go up to reach the cupcake. Additionally, when it goes beyond the ceiling, it is restored to the initial position.

You can define the _init() callback to position all the sprites to their own location:

function _init()
  initspoon()
  initwasp()
  initcup()
end


Since all the all the movement behaviours are defined, you can also implement the _draw() callback:

function _draw()
  cls()
  map(0,0,0,0,128,128)
  spr(spoon.sprite, spoon.x, spoon.y)
  spr(wasp.sprite, wasp.x, wasp.y)
  spr(cup.sprite,cup.x,cup.y)
end


map() function simply draws the map all along the screen.

Collisions

Thinking to the map as a grid, it’s simple to define where two objects collide: this happens when their grid cells are the same.

To get the current object cell just divide the sprite x and y by 8 (the size of a cell) and do some rounding:

function checkcupcollision()
  if ((flr(spoon.x/8) == flr(cup.x/8)) and   (flr(spoon.y/8)==flr(cup.y/8))) then
    initcup()
    initspoon()
  end
end


function checkwaspcollision()
  if ((flr(wasp.x/8) == flr(spoon.x/8)) and (flr(wasp.y/8)==flr(spoon.y/8))) then
    initwasp()
    initspoon()
   end
end

Now everything is set, so you can develop _update() callback:

function _update()
  movespoon()
  movewasp()
  movecup()
  checkcupcollision()
  checkwaspcollision()
end

Save

Save your work by pressing “ESC” and typing “SAVE CUPCAKE”.

Run!

No comments:

Post a Comment