PICO-8: How do I shoot? Dynamic objects and tables

Shooting in Pico-8

PICO-8 is a small and sweet game engine aka fantasy console. This article explains how to add and remove objects like bullets to a game at playtime in PICO-8.

One question that I always get asked is: how do I add and remove multiple objects to a game dynamically. Stuff like projectiles, bullets or particles. No problem: I will show how to create objects on-the-fly in PICO-8.

BulletOne, BulletTwo, BulletThree, BulletFour

Probably the first idea is to make a new table for each object and saving it on a variable. We can just define a bunch of bullets and then set them when we press shoot.

local bulletOne = {
	x = 32, y = 32, 
	active = false, 
	draw = function(self)
		circfill(self.x, self.y, 7, 8)
	end
}

In our _draw callback we execute the draw() function of bulletOne: bulletOne:draw(). Making tables might be ok but having one variable for one object is very limiting. Is there a better way?

There must be a better way

Arrays can save you a lot of writing and duplicate code. Instead of using individual variables we can use a holding object.

bullets = {}
add(bullets, {
	x = 32, y = 32, 
	active = false, 
	draw = function(self)
		circfill(self.x, self.y, 7, 8)
	end
})

This way we can add as many objects to the bullets table as we like. And without creating a new variable every time.

Use PICO-8 iterators

How do we call the draw function for all the bullets in our _draw callback function? We use PICO-8 all() iterator function:

function _draw()
    for bullet in all(bullets) do
        bullet:draw()
    end
end

 

Additional: Global vs Local Variables

Global means that it can be accessed anywhere in your program. But there is also a table for the local scope. Local means that it is only accessible in the current part of your program. You can assign a variable to the local scope using the local keyword.

 

How to set up Vuforia and Unity

This is a getting started guide for augmented reality development. A short but complete tutorial on how to set up Vuforia with Unity 5.6.0f3 personal. In the end you will have a 3D cube on a marker.

  1. Create Account at Unity
  2. Download Unity 5.6.0f3
  3. Create Account at Vuforia
  4. Login into Vuforia.com and download the .unitypackage via Download for Unity
  5. Develop -> License Manager tab, select Add License Key
  6. Develop -> Target Manager
  7. Create new Database by clicking Add
  8. Make digital photo of marker or download this one
  9. Cut out marker in Photoshop/Paint
  10. Targetmanager > yournewdatabase > add target
  11. Upload marker image and click Add. Problems with upload? Use jpg and not png!
  12. Download Database, select Unity Editor > Download
  13. Create new project in unity
  14. Import the vuforia .unitypackage and the database .unitypackage by dragging it into project window
  15. Obsolete API warning: click go ahead and upgrade
  16. Toolbar window > Vuforia > Configuration > paste the App license key there
  17. Add your License Key and activate the datasets

    Toolbar window > Vuforia > Configuration > Datasets > load yournewdatabasename Database and click activate

  18. Project window > Assets > Vuforia > Prefabs > ARCamera add to scene
  19. Project window > Assets > Vuforia > Prefabs > ImageTarget add to scene
  20. Hierachy > Select ImageTarget
  21. Inspector > Find section Image Target Behaviour(Script)
  22. Inspector: set database to yournewdatabase and image target to marker
  23. Add a cube as the child of the ImageTarget gameobject
  24. Run and test your project

Optional next steps: you could add a script to the cube to make it rotate

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class rotate : MonoBehaviour {

    // Use this for initialization
    void Start () {
        
    }
    
    // Update is called once per frame
    void Update () {
        transform.Rotate(Vector3.up*Time.deltaTime*100);
    }
}

Arrays and Tables in Pico-8

2D Arrays

Screen Shot 2016-07-16 at 17.36.17

For beginners, I suggest you use concatenation to index 2D arrays. Create a new object in a 2D cell at (i,j) in a table called myArray with the following code:

myArray[i..","..j] = {}

To iterate over all objects in the myArray you can use the pairs iterator. Caution: the objects are not ordered when using pairs!

for k,v in pairs(myArray) do
	-- v is the cell object
	-- k is a string in the form of "i,j"
end

If we want to access the objects in a particular order we should use nested for loops:

for i=1, 8 do
	for j=1, 8 do
		local cell = myArray[i..","..j] 
		-- do stuff with the cell
	end
end

Objects And Container

PICO-8_5

Entities like the spaceship in this gif are objects. Containers for objects are special in Pico-8 because we have a couple of built-in functions to help us manage insertion and deletion. I strongly suggest to use add(), del() and all() for container and entity management.

Create and add an object to a table with add():

local entities = {}
local player = {
	x = 3,
	y = 3,
	sprite = 5
}
add(entities, player)

In your _update or _draw callbacks, you will most likely want to loop over all objects. You should use all() for that:

for entity in all(entities) do
	-- do stuff here
end

You can use del() to remove an object from the container even while iterating over the container:

for entity in all(entities) do
	del(entities, entity)
end

This only works with all() and del() together! This is great for games where you have dynamic objects such as bullets, effects or timed events that are added and removed dynamically.

I hope that these two hints help you to get started with the awesome Pico-8 engine. For advanced users, other methods might be more efficient. I recommend reading the Pico-8 Docs or the PIL for more information.

NanoFL under Mac OS X

NanoFL 3.0.5 currently only features a .exe binary for windows. The program itself is wrapped with XULRunner from Mozilla and that makes it easy to run natively on Mac OS X.

If you have a current Firefox there is no need to install XULRunner(which anyway seems deprecated). You can execute XULRunner apps with -app switch. Use Wine to install the game and then copy the installation folder somewhere accessible. In the terminal you then execute the following to start NanoFL.

/Applications/Firefox.app/Contents/MacOS/firefox -app 
/Users/headchant/Downloads/nanofl/application.ini

or more generic

/Applications/Firefox.app/Contents/MacOS/firefox -app /path/to/your/application.ini

Use no line breaks and edit the path to your installation of NanoFL.

This should start NanoFL inside a Firefox instance with almost all features as the windows version. Have fun! Screen Shot 2016-04-07 at 14.10.08

Löve – What is Love? An Introduction

Screenshot 2015-11-23 12.21.17

Since my old tutorials were made in 2010 it was time for an update. This is an updated tutorial on Löve. It assumes the Löve version 0.9.2 but a lot of this article might remain relevant to later versions.

In the age of Gamemaker and Unity Löve still stands out as one of my favorite rapid prototyping frameworks. Lua is ideal for the quick ‘n dirty approach where other languages like C# would be overkill. It makes sense to learn Lua and the Löve framework because Lua is used in many different applications today and also in other game engines likes pico-8. Another great feature of Löve is that it is also very easy to distribute, which makes your creations accessible to wider audience.

Multi-platform & Open

The idea behind love is that you create .love files which can be run by the love executable on every platform, e.g. linux, OSX or windows. The .love files are renamed .zip files in which you put your project source files. Another side effect of this is that you can always unzip any .love file you find, view the source and learn from it.

Documentation

The best way to start learning is to check out the wiki on love2d.org. You can find a Getting Started Guide there which helps you install and set up a basic working environment.

Löve is mainly build on the following component:

  • SDL, simple directmedia layer
  • OpenGL, well known graphics library
  • OpenAL, for sounds
  • LuaJIT a variant of Lua, as the programming language

The great thing is that you don’t have to know anything about the first three components to create a game with it! Really the only thing you need to learn is Lua.

For beginners, I highly recommend reading Programming in Lua by Roberto Ierusalimschy who also is the leading architect of the language. That said, I will be covering some of the basics of Lua in this tutorial series.

What can be created?

The frontpage of Löve has some excellent examples of games made with Löve. This includes games like Mari0 and Move Or Die.

mari0

You see, Löve is not only a fast and easy to learn language but also has the power to be used in bigger projects. I hope this series of tutorial can give you some help so you can start to build your dream games soon.

Performance: Tilemaps in Löve with spritebatch

A usual pattern in 2D games is the use of tilemaps. In Löve you can implement those in a variety of ways. In this article I want to compare tilemap implementations in Löve 0.9.1 using spritebatches.

I will use this to create an ascii display.

Screenshot 2014-10-30 15.01.44

Timing

I will use the following code to measure the performance. It is an extended example from kikitos time function. It uses os.clock to measure the time before the execution and again after the execution.

time.lua:

   local last

    return function(title, f, n)
        if title == "clear" then last = nil; return end
        collectgarbage()
        local n = n or 100000
        local startTime = os.clock()
        for i = 1, n do
            f()
        end
        local endTime = os.clock()
        local delta = endTime - startTime
        if not last then last = delta end
        print(title, delta, (delta) - last)
        last = delta
    end

Spritebatch

We will generate a spritebatch with a tilesheet image(aka texture atlas) and use the spritebatches add/set method to change the content. The spritebatch will have NxM size. I used Alloy_curses_12x12 from the dwarf fortress tileset repository.

   local img = love.graphics.newImage("Alloy_curses_12x12.png")
    local spritebatch = love.graphics.newSpriteBatch(img, 87*25, "stream")
    local quads = {}
    for x = 0, 255 do
        local i,j = 1+(x%16), math.floor(x/16)
        quads[x] = love.graphics.newQuad(i*12, j*12, 12, 12, img:getWidth(), img:getHeight())
    end

We can also use the bind and unbind function on the spritebatch to bind the spritebatch into memory before we loop through the whole display grid.

Luajit Tables

We need two arrays: one to store the id and one to store the ascii value of the cell. Map is our display grid and idmap will store the id returned by adding the cells.

   local map = {}
    local idmap = {}
    spritebatch:bind()
    for i=0,86 do
        map[i] = {}
        idmap[i] = {}
        for j=0,24 do
            map[i][j] = 68
            spritebatch:setColor(255,255,255)
            idmap[i][j] = spritebatch:add(quads[map[i][j]], i*12, j*12)
        end 
    end
    spritebatch:unbind()

   local random = math.random
    time("spritebatch, lua table", function()
        spritebatch:bind()
        for i=0,86 do
            for j=0,24 do
                map[i][j] = random(255)
                spritebatch:setColor(0,255,255)
                spritebatch:set(idmap[i][j], quads[map[i][j]], i*12, j*12)
            end
        end
        spritebatch:unbind()
        love.graphics.setColor(255,255,255,255)
        love.graphics.draw(spritebatch)
    end, 1000)

FFI Arrays

We use the ffi.new functions to generate a 2 Dimensional uint8_t array for the character display grid and uint16_t for the id grid.

   local ffi = require "ffi"
    local map = ffi.new("uint8_t[87][25]")
    local idmap = ffi.new("uint16_t[87][25]")

    spritebatch:bind()
    for i=0,86 do
        for j=0,24 do
            map[i][j] = 68
            spritebatch:setColor(255,255,255)
            idmap[i][j] = spritebatch:add(quads[map[i][j]], i*12, j*12)
        end 
    end
    spritebatch:unbind()

    local time = require "time"

    time("spritebatch, ffi array", function()
        local random = math.random
        spritebatch:bind()
        for i=0,86 do
            for j=0,24 do
                map[i][j] = random(255)
                spritebatch:setColor(0,255,255)
                spritebatch:set(idmap[i][j], quads[map[i][j]], i*12, j*12)
            end
        end
        spritebatch:unbind()
        love.graphics.setColor(255,255,255,255)
        love.graphics.draw(spritebatch)
    end, 1000)

Performance

Using Luajit tables is a lot faster than using FFI arrays:

spritebatch, ffi array 2.13035 0 spritebatch, lua table 0.577013 -1.553337

image (2)

FFI Arrays are a lot slower, so you should stick to tables if you want to draw tiles fast.

Slime explains: “The FFI array is probably slower because the loop it’s used in can’t be compiled by the JIT (since C API functions – all of LÖVE’s API, for example – prevent JIT compilation for the block of code they’re called in.)”

One more thing for spritebatch

It’s probably a good idea to only use spritebatch:set when something has changed. You can do this by having two map tables. Check if the map table is different than the cache table and only then set the spritebatch tile.

   spritebatch:bind()
    for i=0,86 do
        for j=0,24 do
            if map[i][j] ~= old[i][j] then
                old[i][j] = map[i][j]
                spritebatch:setColor(255,255,255)
                spritebatch:set(smap[i][j], quads[map[i][j]], i*12, j*12)
            end
        end
    end
    spritebatch:unbind()

I created an example lua file that can be used as a very simple base to render roguelike ascii games:gist. But of course this can be used for any other tile based game too.