A Simple OOP Game Tutorial #3
WELCOME TO THE 4th INSTALLMENT OF ECS VS. OOP WEEKLY SERIES!
Before proceeding, if you have not read the previous post and previous part, please check those first, this post will wait for you. You will need the information and guide provided in the previous posts for this one.
Done? Good! You may now resume your quest!
UPDATES
Source code is now available here
DISCLAIMER
This post will not try to teach Lua coding or any programming logic. This is to focus primarily in OOP design and pattern.
If you want to learn basic programming and game development, let me know. I will make an in-depth guide about it for beginners if requested.
INTRODUCTION
Last time we did a lot of things! Now our simple project is becoming more and more like an actual game, though very simple. But what is a game without score or achievement? What is a game if there is no goal or no way to end the game?
Surely we do not want the player to keep playing a session forever, we want some way to make the player stop like through the concept of virtual death or game over.
But before that, let us add a snippet to the classes we have previously made that will be used for the scoring, namely, we need to be able to get the shape and color field of the instance.
We could easily do this just by accessing the field since there is private
syntax in Lua, we could implement that but that would be out of scope. So in the spirit of OOP, we will do this using getters
.
For classes/base/shape.lua
:
function Shape:new(shape_type)
self.shape_type = shape_type
self.alive = true
end
function Shape:getShapeType() return self.shape_type end
function Shape:getColor() return self.color end
function Shape:isDestroyed() return self.alive == false end
function Shape:isClicked() return self.is_clicked end
function Shape:destroy()
self.alive = false
self.is_clicked = false
end
Also for the following subclasses:
--circle.lua
Circle.super.new(self, "circle") --in the Circle:new
--rectangle.lua
function Rectangle:new(width, height, shape_type)
Rectangle.super.new(self, shape_type or "rectangle")
end
--square.lua
Square.super.new(self, size, size, "square")
Implementing Scoring
Taking a peak at this post, we stated the following for scoring when shapes are destroyed:
- The two clicked objects will be destroyed (a goal) if:
- the two objects are of the same color (increase timer + 2)
- the two objects are of the same shape (increase timer + 4)
- the two objects are of the same shape and same color (increase timer + 8)
In OOP, it will be hard for an instance of a shape to have knowledge or reference about the other clicked instance. So we can not put that logic in the shape class and its subclasses. If you remember the spawner class which we used as part of the general gameplay logic, we will also make that logic a sepate class.
Make a new file called classes/score.lua
and write the following:
local Class = require("modules.classic")
local Score = Class:extend()
local function compare_color(a, b)
return a[1] == b[1] and
a[2] == b[2] and
a[3] == b[3]
end
function Score:new(color_score, shape_score, both_score)
self.color_score = color_score
self.shape_score = shape_score
self.both_score = both_score
self.score = 0
end
function Score:update_clicked(obj)
if self.clicked1 and self.clicked1 ~= obj then
self.clicked2 = obj
self:check_score()
else
self.clicked1 = obj
end
end
function Score:check_score()
if self.clicked1 and self.clicked2 then
local shape1 = self.clicked1:getShapeType()
local shape2 = self.clicked2:getShapeType()
local color1 = self.clicked1:getColor()
local color2 = self.clicked2:getColor()
local same_color = false
local same_shape = false
if compare_color(color1, color2) then
self.score = self.score + self.color_score
same_color = true
end
if shape1 == shape2 then
self.score = self.score + self.shape_score
same_shape = true
end
if same_color and same_shape then
self.score = self.score + self.both_score
end
self.clicked1:destroy()
self.clicked2:destroy()
self.clicked1 = nil
self.clicked2 = nil
end
end
function Score:draw()
love.graphics.setColor(1, 0, 0, 1)
love.graphics.print("Score: " .. self.score, 8, 8)
end
return Score
Now let us implement it into our main.lua
file (I will only show the new lines):
local Score = require("classes.score")
local score = Score(2, 4, 8)
function love.update(dt)
for i, obj in ipairs(shapes) do
if obj:isClicked() then
score:update_clicked(obj)
end
end
--checking for destroyed shapes
for i = #shapes, 1, -1 do
local shape = shapes[i]
if shape:isDestroyed() then
table.remove(shapes, i)
end
end
end
function love.draw()
score:draw()
end
Now test the game, you should see a score text in the upper left corner of the screen, that is just a prototype, we will use a proper UI class for that next time.
Clicking on two shapes will yield score, for now there is no feedback to the user about what just happened, again, we will improve it next time when we add those juicy effects.
I am sorry that this week’s post is very short! To make up for it, I will add images to the posts so readers can see what we are building and the progress so far.
Here are some screenshots to show what we have done so far.
Next Week’s Post:
For the next post, we will continue to:
- implement the UI classes and subclasses
- implement state classes and gameplay logic
Stay tuned via RSS or follow me on Twitter