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!
Source code is now available here
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.
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
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")
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 == b and a == b and a == b 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.
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