Objects as Objects
For any given video game, you are going to have a number of objects to deal with. Even a simple card game is going to have 52 cards to keep track of. With so many things to keep track of, we desperately need a way to keep track of what functions and variables go with which objects and how to keep things working independantly of eachother.
If you’ve already thought to yourself, “obviously, we should represent these things as objects. I mean, you already calld them objects twice,” then you should calm down. Of course we’re doing objects! The real question comes to how you should control those objects.
I’ll cut to the chase. The objects should control themselves. An object should be able to tell itself how to update it’s position on screen, or how to draw itself. As someone who came from a Java background, it was one of the first things that I thought about. But that is also where I had the most problem because I couldn’t figure out how to orchestrate every needed action. After a while I figured it out.
Truth be told, the answer to how you accomplish this can change depending on which language you are using. Lua makes things somewhat more complicated because it doesn’t really have objects. It just has tables. They’re very fancy tables, but that means you’re not going to get some of my favorite OO-forward features like inheritance and security. But it turns out that you don’t really need them. Let’s consider this object definition:
mob = {
sprite = -- sprite image here
position = {24, 89}
rotation = 1.34
-- etc. etc.
}
Sure, there are ways to get this game running by having these game objects act as puppets, controlled and effected by external methods that directly alter it’s data. But rather than thinking of them as remote-control robots like Tetsujin 28-go, it would be better to think of them as robots in the vein of Astro Boy - capable of moving and possibly even making decisions on it’s own. In a previous chapter we covered basic animation. Using this autonomous object framework, we would simply move those functions we wrote inside of the update and draw loops into methods of the object itself. With that in mind you would have a minimal object looking something like this.
mob = {
-- object data here
update = function(self, dt)
-- update function goes here
end,
draw = function(self)
-- draw instructions go here
end
}
From here, you could put all your in-memory objects into a table (or any other iterable collection data structure), and then simply tell each of those objects to iterate like so.
love.update(dt)
for _, v in ipairs(objectCollection) do
v.update(dt)
end
end
love.draw()
for _, v in ipairs(objectCollection) do
v.draw()
end
end
Considering that your objects usually have multiple types of things they might need to do or different animations to display, this actually gives us a lot more flexability. If we want to change what the object is currently doing, we can simply overwrite it’s update
method.
However, I do want to point out that there are going to be situations where you do not want your game objects to act autonomously. For instance, if you are making a game with a tactical AI (think firefights in FPS games or warfare in strategy games), you will need to have some sort of external process running that can see the bigger picture and tell your objects what they should be doing. AI, however, is far beyond the scope of this document. If you want to learn more, I would suggest picking up a copy of AI for Games by Ian Millington. Game AI is a surprisingly broad field, and this book happens to have some of the best practical advice across just about all of it.
Making Objects Easier
Of course, most games are going to have a tremendous amount of objects created and destroyed at any given time. As such, it would be extremely painful to have to hard-code everything.
I won’t go into too much detail on this because there are better sources on so-called DRY techniques that will teach you the things you need to know to determine the optimal solution for your specific case, and far better than I ever could. And beyond that, you probably already know them. But for those who don’t I’d recommend specifically looking into factories. It’s not the only solution out there and it may not always be the best, but it often either is the best or at least good enough.
In the case of lua, though, it’s extremely flexable. You could simply compose your objects in such a way that they simply steal the methods of other objects you might have in memory and pretend they are their own. I’d avoid that work pattern, however, as it can make things very confusing very fast.
Next: Handling Input