An Introduction to Object-Oriented Programming
Category / Tutorial
I've always felt that one of the biggest challenges in working on a project using a fantasy console is writing code that is manageable and easy to work with. Building a game in a single text file is a challenging enough prospect for most fantasy console editors, although some tools are starting to fix this.
Even so, the complexity that surrounds most game projects means that code can quickly spiral out of control. Over time, we’ve developed a number of techniques to help improve the way code is organised to improve comprehension and reduce the cognitive load on the developer.
The technique we’ll discuss today is something you are probably already doing, without realising it: object-oriented programming.
What is OOP?
Object-oriented programming (OOP) stems from the idea that certain areas of a program’s code can be thought of as a collection of Objects. In real life, objects are things, with distinct properties, and they can do stuff. For example, a car is red, and it can move. A gun has a number of bullets, and it can be fired. In all these cases, we can see that objects have both a state, and behaviour.
While OOP is used in numerous fields, it can be especially powerful in the context of games because we often try to represent or simulate actual objects in our code, each with their own state and behaviour. This can make it easier to understand what is happening in our code, as it is easier to reason about relatable objects than abstract ideas.
There are other ideas included in the object-oriented paradigm, although it isn’t always practical to embrace all of them. I encourage you to read through this series at a later time, to learn more.
Lua is a very minimalist language. Out of the box, it does not provide the means to create and use objects, but it does give us the tools to build a framework for OOP on our own. We need three core pieces for a framework to easily support objects:
- Ability to make objects, known as a constructor
- Storage for each object’s properties
- A way to attach behaviour to an object.
Quite often, we don’t have just one of a type of object (known as an instance of an object), but many. A game has many enemies. Your player’s weapon might fire several bullets. To make each of these, we use a constructor, which is a function that creates objects, and does any processing necessary for their use. Next, to store the state of our objects we are going to take advantage of the table, the only data structure that Lua provides out of the box. This will also allow us to keep our functions bound to the object.
Here is an example showing these concepts in action for a bullet in a side-scrolling shmup:
self.x = self.x + self.speed
function createBullet(x, y)
While we could define moveBullet inside the createBullet function, this would cause a new function to be created everytime a Bullet was, using up significantly more memory. Instead, we declare it outside the constructor and reference it. Now, we can create and use bullets whenever we need, like this:
local bullet = createBullet(5, 5)
bullet:move() --this is the same as writing bullet.move(bullet)
Here's a cartridge with all the code, as well as a more complex example to explore:
The example above demonstrates a simple implementation of object-oriented principles but it has a couple of problems. Every type of object in a game would need to its own constructor which will add a lot of tokens, a scarce resource in virtual console games. Not only that, but if we wanted to create multiple kinds of bullets, we would end up duplicating a lot of code. Most frameworks support inheritance to extend existing objects with subtypes.
In future articles we will expand on these ideas to build an object framework that is small, flexible and usable across several projects. See you next time!