Object-Oriented
OOP Tables
Object-oriented programming (OOP) is a way of writing computer programs using objects that have both data (like numbers or words) and actions (like moving or making sounds). It helps in organizing and making programs easier to work with and reuse. Some programming languages are designed with OOP principles in mind, and have classes to easily make objects but Lua does not. However, Lua does support OOP with object tables, metatables, prototypes, and first-class functions. If you don't understand what those mean yet, don't worry, we'll explain!
To create an object table in PICO-8:
obj = {
sprite = 1,
x = 10,
y = 5,
}
obj | |
---|---|
sprite | 1 |
x | 10 |
y | 5 |
Tables in Lua can hold all types of data, including functions! In OOP, the term method refers to a function that is inside of an object table. Here are four ways you can add methods to your table (see Lua Docs):
--1 method inside table
obj = {
sprite = 1,
x = 10,
y = 5,
draw = function(self) ... end
}
--2 method outside table
obj.draw = function(self, a, b) ... end
--3 method outside table
function obj.draw(self, a, b) ... end
--4 method outside table, hidden self
function obj:draw(a, b) ... end
All four examples will do the same thing: add a draw()
method to table obj
. It is up to you which you prefer or which works best in your situation. Just notice that the fourth example uses a colon operator ( :
), which implicitly declares a self
parameter.
Now that you have created methods in your objects, here are four examples of how to call them:
--1 pass self argument
obj.draw(obj)
--2 implicitly pass self
obj:draw()
--3 pass self and arguments
obj.draw(obj, 1, 2)
--4 with arguments, implicitly pass self
obj:draw(1, 2)
Again, it is up to you and your case to determine which style you want to use because all four examples do the same thing: call the draw
method in obj
and pass arguments to the function.
Lua does not have classes (Lua Docs), but it does support prototype-based programming which is a style of OOP and achieves similar goals (code reuse and inheritance).
Now that you know how to create an object table with methods, here is an example of how you might create an enemy object in your game that has update
and draw
methods:
--object table
enemy = {
sp = 1,
x = 0,
y = 0,
dx = 0,
dy = 0,
update = function( self ) --method 1
self.x += self.dx
self.y += self.dy
end,
draw = function( self ) --method 2
spr(self.sp, self.x, self.y)
end
}
We can then turn this enemy
table into a prototype and create new enemy objects from it. This uses metatables and metamethods, so make sure you understand those before continuing.
--add method to prototype for copying/inheriting
function enemy:new(obj)
obj = obj or {} -- if obj isn't passed, create empty object table
--any key not found in obj,
--look for it in prototype (this enemy table)
setmetatable( obj, {__index = self} )
return obj
end
--create goblin from enemy prototype
goblin = enemy:new()
--specify values in goblin table
goblin.sprite = 3
goblin.x = 6
goblin.y = 10
--or you can pass the values to the "new" method
ogre = enemy:new( {sprite=4, x=40, y=50} )
This example is just to show how goblin
and ogre
both use the enemy
prototype's new()
method to be created which leaves enemy
unchanged and can continue to be used as the prototype for more enemies. We can add more variables and functions to the prototype enemy
table as default values for objects created from it. Since we use this prototype as a metatable, when goblin or ogre tables try to use a key that is not found in its own table, it will look for it in the enemy table and use that.
Prototype
enemy | |
---|---|
sprite | 1 |
x | 0 |
y | 0 |
new | function |
update | function |
draw | function |
Object
goblin | |
---|---|
sprite | 3 |
x | 6 |
y | 10 |
Object
ogre | |
---|---|
sprite | 4 |
x | 40 |
y | 50 |
With the metatable being the prototype, you can call goblin:draw()
and even though it is not in the goblin table, it won't error because it will find the draw
method in the prototype enemy
.
Further Reading
Take this to the next level by reading what Merwok wrote on the BBS that has more explanations and examples of taking OOP in PICO-8 another step farther.
After you've got experience working with OOP, and are feeling ready to tackle the next big step to using OOP while saving many tokens, then you're ready to learn about and use _env.
1304
29 Apr 2024