A Simple OOP Game Tutorial #4
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 I promised for a media (image/video) to show the progress of what we are making right? Well of course I do not disappoint! So here you go:
Looks good, doesn’t it?
Okay so for now I want to make this post short as well due to time constraints. So let us implement game states!
IMPLEMENTING GAME STATES/SCENES
What is a game state?
To put it simply, it is like a container
for the different states possible in our game. The whole game should be separated according to its state
which as much as possible should be in their own, no dependency with other state.
States can also be thought of as screens
. Some engines/frameworks use that. Now that I remember, Godot use scene
, wait no, it kinda is, but at the same time even for the concept of object
they use the term scene
. I digress.
We will use screen
and state
interchangably okay?
Here is a list of the states we will need:
- Title screen
- Play - go to
Game screen
- Quit - exit the game
- Play - go to
- Game screen
- Playing
- Paused
- Back - go to
game screen
- Quit - exit the game
- Back - go to
- Game over screen
- Retry - go to
Game screen
- Back - go to
Title screen
- Retry - go to
The basic thing we need of the screen base class are the following:
- ID - passed in constructor
- Events - update, draw, etc. Basically all the events from the
love
API that we will use
So we will need a base class for the screens/states
Make a new file named scene.lua
in the classes/base/
and put the following:
local Class = require("modules.classic")
local Scene = Class:extend()
function Scene:new(id, manager)
self.id = id
self.manager = manager
self.is_active = false
end
function Scene:set_active(bool)
self.is_active = bool
end
function Scene:get_active() return self.is_active end
return Scene
Quite simple right? But why do we have that is_active
flag? Well, it is for allowing us to have multiple screens at the same time. This is needed in the case for making the game screen
shown even when the pause screen
is shown on top of it.
The manager
field will be the gamestate manager we will implement next post. There are multiple ways to do this, but we will go for this approach as to avoid overcomplications with dependencies and whatnot.
Next we will implement the subclasses.
For classes/title_screen.lua
:
local Scene = require("classes.base.scene")
local TitleScreen = Scene:extend()
local title = "Shape Clicker"
local font
local window_width, window_height
local text_width, text_height
function TitleScreen:new(manager)
TitleScreen.super.new(self, "title_screen", manager)
font = love.graphics.newFont(32)
window_width, window_height = love.graphics.getDimensions()
text_width = font:getWidth(title)
text_height = font:getHeight(title)
end
function TitleScreen:draw()
love.graphics.setColor(1, 0, 0, 1)
love.graphics.setFont(font)
love.graphics.print(title,
window_width/2, window_height/2,
0, 1, 1,
text_width/2, text_height/2)
end
function TitleScreen:keypressed(key)
if key == "enter" or key == "space" then
self.manager:switch("game_screen")
elseif key == "escape" then
love.event.quit()
end
end
return TitleScreen
For classes/game_screen.lua
:
local Scene = require("classes.base.scene")
local GameScreen = Scene:extend()
function GameScreen:new(manager)
GameScreen.super.new(self, "game_screen", manager)
end
function GameScreen:keypressed(key)
local pause_screen = self.manager:get("pause_screen")
if key == "escape" then
pause_screen:set_active(not pause_screen:get_active())
end
end
return GameScreen
For classes/pause_screen.lua
:
local Scene = require("classes.base.scene")
local PauseScreen = Scene:extend()
function PauseScreen:new(manager)
PauseScreen.super.new(self, "pause_screen", manager)
end
function PauseScreen:keypressed(key)
local game_screen = self.manager:get("game_screen")
if key == "return" or key == "space" then
self:set_active(false)
game_screen:set_active(true)
elseif key == "escape" then
self:set_active(false)
self.manager:switch("title_screen")
end
end
return PauseScreen
For classes/gameover_screen.lua
:
local Scene = require("classes.base.scene")
local GameOverScreen = Scene:extend()
function GameOverScreen:new(manager)
GameOverScreen.super.new(self, "game_screen", manager)
end
function GameOverScreen:keypressed(key)
if key == "r" then
self.manager:switch("game_screen")
elseif key == "escape" then
self.manager:switch("title_screen")
end
end
return GameOverScreen
Phew, that was a lot! But if you can notice, there is a lot of issues with it.
First, when there are multiple screens active at the same time, doing a keypress will result in conflicting logic as multiple screens will capture that event.
Second, there is nothing yet in any state. We will move the content of our existing game logic which is in main.lua
and move those to their respective scenes.
We will fix those when we finally make our manager.
This is for now! Sorry if the posts are falling short. Time is kinda tight right now as well as some personal things going on.
Next Week’s Post:
For the next post, we will continue to:
- implement Gamestate/screen manager
- implement the UI classes and subclasses
Stay tuned via RSS or follow me on Twitter