CCASSCS028
CCASSCS028
PROJECT REPORT
Submitted By
SNEHA K B
Reg. No. CCASSCS028
for the award of the Degree of
Bachelor of Science (B.Sc.)
in Computer Science
(University of Calicut)
March 2021
DEPARTMENT OF COMPUTER SCIENCE
IRINJALAKUDA
CERTIFICATE
First and foremost we like to thank Lord almighty for his providence
and for being the guiding light throughout the project. We wish to express
my sincere gratitude to our beloved Department head for giving me all the
facilities for our project. We take this opportunity to express my gratitude
to the class teacher Ms. SINI THOMAS and head of the department Ms.
VIJI VISWANATHAN who has been supported us throughout the course of
this project. We are thankful for her aspiring guidance and valuable advice
during the project work. We express my sincere thanks to my project guide
Ms. MINU MARY P J for supporting and guiding throughout the project.We
would take this opportunity to specially thank all other faculty members for
their constant and continuous motivation. Finally we would like to thank my
family and friends for giving valuable advice and moral support throughout
our project.
Declaration
We here by declare that this project work ”GAME DEVELOPMENT” sub-
mitted by Christ College (Autonomous)Irinjalakuda, affiliated to Calicut Uni-
versity in partial fulfillment of the requirement for the award of the Bachelor of
Computer Science, is a record of original work done by us,under the guidance of
Ms.MINU MARY P J, Department of Computer Science.
Place:Irinjalakuda
Date:
1
ABSTRACT
The proposed games PING PONG and Mr.Toad are digital game ver-
sion of the Table Tennis game and T-Rex game . Players will be able to
play the game in two-player mode in PING PONG and is single player in
Mr.Toad.The games PING PONG and Mr.Toad will not deal with in arti-
ficial intelligence and is solely be intended for two-players and single player
respectively. Due to the nature of the games, the graphics will be done in
2D and feel similar to that of the Table Tennis and T-Rex.
Contents
1 Introduction 1
1.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
2 System Analysis 2
2.1 Purpose . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
2.1.1 Existing System . . . . . . . . . . . . . . . . . . . . . . . 2
2.1.2 Proposed System . . . . . . . . . . . . . . . . . . . . . . . 2
2.2 Problem definition . . . . . . . . . . . . . . . . . . . . . . . . . . 2
2.3 FEASIBILITY STUDY . . . . . . . . . . . . . . . . . . . . . . . 2
2.3.1 Technical Feasibility . . . . . . . . . . . . . . . . . . . . . 3
2.3.2 Economical Feasibility . . . . . . . . . . . . . . . . . . . . 3
2.3.3 Operational Feasibility . . . . . . . . . . . . . . . . . . . . 3
4 Design Document 9
4.1 Purpose . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
4.2 Scope . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
4.3 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
4.4 Game Design . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
4.4.1 PING PONG . . . . . . . . . . . . . . . . . . . . . . . . . 9
4.4.2 Mr.Toad . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
4.5 Game Logic . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
4.5.1 AABB Collision . . . . . . . . . . . . . . . . . . . . . . . 10
4.6 Use Case Diagram . . . . . . . . . . . . . . . . . . . . . . . . . . 12
4.6.1 PING PONG . . . . . . . . . . . . . . . . . . . . . . . . . 12
4.6.2 Mr.Toad . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
6 System Testing 14
6.1 Test Plan . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
6.1.1 Scope . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
6.1.2 Software risk issues . . . . . . . . . . . . . . . . . . . . . . 15
6.1.3 Features to be tested . . . . . . . . . . . . . . . . . . . . . 15
6.2 Test consolidation . . . . . . . . . . . . . . . . . . . . . . . . . . 16
6.2.1 Test item . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
6.2.2 Input specifications . . . . . . . . . . . . . . . . . . . . . . 16
6.2.3 PING PONG . . . . . . . . . . . . . . . . . . . . . . . . . 16
6.2.4 Mr.Toad . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
Appendix 20
A USER INTERFACES 20
A.1 PING PONG . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
A.2 Mr.Toad . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
B CODE 25
B.1 PING PONG . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
B.2 Mr.Toad . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40
Christ College(Autonomous) Game Development
Chapter 1
1 Introduction
In this project, we design and implement two video games.
PING PONG is a computer game inspired from table tennis, which was a
popular and mind catching game in the early stages of digital game development.
Two users can play at the same time and it’s portable. This game can be run
on any computer having LOVE2D framework.
fhjdj The main features of Mr.Toad game are - It has a variety of states,
state change requires the keyboard input, mainly focused on detecting rectangle
collision. Mr.Toad is a game where the player controls a frog, attempting to
fly between columns of green pipes. The frog will be jumping until it collides
with a pipe or it falls on ground. It’s a simple game of infinite level type. It’s
a challenging game for all.
.
1.1 Overview
PING PONG
The main features of RETRO PING PONG game are - It has a variety of
states, state change requires the keyboard input, mainly focused on detecting
rectangle collision. It’s a multiplayer game.
Mr.Toad
In this game, the player can control the vertical movement of Mr.Toad (
every pressing on the keyboard makes Mr.Toad leap upward for a little bit, and
he will fall freely without control).
Both are written in lua language and executed using LOVE2D framework.
Chapter 2
2 System Analysis
2.1 Purpose
The primary goal of this project was to increase understanding of the video
game industry’s creation methods, communication behaviors, and attitudes for
the purpose of building more accurate models of preservation and collection
development.
Chapter 3
3 Software Requirement Specification
3.1 Purpose
The purpose of this document is to give a detailed description of the require-
ments for the Game Development. It illustrate the purpose and complete de-
scription for the development of the system. It explain system constraints,
interface. This document is primarily intended to be proposed for digital games
and a reference for the upcoming students of the college.
3.2 Scope
Our project has made it easier for everyone to create their own digital games.
Playing video games fulfills a purpose in their lives. This could include gaming
for: relaxation, opportunities to exert control, enjoyment, creativity, socializa-
tion, prevent boredom, challenge, and achievement. It could also be used as a
coping method or stress management.
3.3 Overview
The purpose of this document is to help the reader to visualise the solution to
the project presented.
performance
security
safety
usability
Performance
Performance requirements concern the speed of operation of a system. Types
of performance requirements :
Response requirements (how quickly the system reacts to a user input).
Reliability
Reliability is the ability of a system to perform its required function under
stated conditions for a specified period of time.
constraints on the runtime behavior of the system. This system is reliable
because its functionalities can be done on the required conditions.
Safety
Safety requirements are not required which exclude unsafe situation from
the possible solution of the system.
Usability
Usability is the ease with which a user can learn to operate, prepare inputs
for, and interpret outputs of system or components. Usability requirements
include :
well-formed user interfaces.
3.9.2 Linux
Linux is an operating system. Linux has been around since the mid-1990s and
has since reached a user-base that spans the globe. In fact, one of the most popu-
lar platforms on the planet, Android, is powered by the Linux operating system.
An operating system is software that manages all of the hardware resources as-
sociated with your desktop or laptop. To put it simply, the operating system
manages the communication between your software and your hardware.Linux is
also distributed under an open source license. Linux has a number of different
versions to suit any type of user.
3.9.3 Mac OS
Mac OS, operating system (OS) developed by the American computer company
Apple Inc. The OS was introduced in 1984 to run the company’s Macintosh
line of personal computers (PCs). The Macintosh heralded the era of graphical
user interface (GUI) systems, and it inspired Microsoft Corporation to develop
its own GUI, the Windows OS. In the 1980s Apple made an agreement allowing
Microsoft to use certain aspects of the Mac interface in early versions of Win-
dows. However, except for a brief period in the 1990s, Mac OS has never been
licensed for use with computers made by manufacturers other than Apple.
Chapter 4
4 Design Document
4.1 Purpose
Game Development is the art of creating games and describes the design, devel-
opment and release of a game. It may involve concept generation, design, build,
test and release.The benefit of this project is very high because you can make
your own games, you don’t need a degree to make games, you get to be cre-
ative. Both games demonstrated in our project are 2-dimensional developed on
the basis of AABB collision theory. PING PONG is made of rectangular blocks.
Player moves the paddle to hit the ball back to the oponent’s side. Mr.Toad
is made of moving pictures where the player moves Mr.Toad to the right using
space bar and pass each pair of pipe to gain points. Purpose of this document
is to give the detailed description of the architecture and design for the games.
4.2 Scope
Game Development is a project aimed at developing the games using lua lan-
guage. It makes easier for everyone to understand the steps in developing games
and it’s ideas.
4.3 Overview
The purpose of this document is to help the reader to visualize the solution to
the project presented.
4.4.2 Mr.Toad
Mr.Toad is a frog (who is very much eager to find his princess in the castle)
. So he need to move between the pairs of pipes without colliding in order to
reach final destination. It’s an infinite loop of pipes. If he touches the pipe, it
will lead to his failure and has to start from the beginning.
Almost all the objects in Breakout are rectangular based objects, so it makes
perfect sense to use axis aligned bounding boxes for detecting collisions. This
is exactly what we’re going to do.
Axis aligned bounding boxes can be defined in several ways. One of them is
to define an AABB by a top-left and a bottom-right position. The GameObject
class that we defined already contains a top-left position (its Position vector),
and we can easily calculate its bottom-right position by adding its size to the
top-left position vector (Position + Size). Effectively, each GameObject contains
4.6.2 Mr.Toad
Chapter 5
5 Development of the System
PING PONG and Mr.Toad is composed of different classes. In PING PONG
each objects in the game like paddle, ball have their own class. Same goes for
Mr.Toad. These classes are later imported to main program file using import
function. Every action even the importing of class is done with the help of
codes. There are a lot of codes available for such purposes in the online forums
which makes it easier for developers to use it without other formalities.
Chapter 6
6 System Testing
Testing is the penultimate step of any game development.With the constant
growth and expansion of the gaming industry worldwide, top leaders in this
industry like AltSpaceVR and BigScreenVR, are accelerating a virtual future
probably faster than many wait for. However, it is vital for the gaming com-
panies to not only focus on future trends but also to identify the user’s needs.
Delivering an end gaming product with errors and bugs will bring about crit-
icism from the end-users, which in turn can lead to a huge reduction in unit
sales.
Functionality Testing
This type of testing is done to confirm whether the end product works following
the specifications. Functionality QA testers mainly hunt for the generic prob-
lems within the game or its graphics and user interface, such as game asset
integrity, stability issues, audio-visual issues, and game mechanic issues. Few
things that proficient game testers consider while testing the interactive ap-
plications comprise performance issues like freezing, crashing, and progression
blockages. User interface (UI) tests, on the flip side, make sure user-friendliness
of the game. UI testing crucially aims at highlighting two significant things,
both the content types and the graphical elements. Besides, localization tests
of the game should also be covered.
Combinatorial Testing
By making use of this method, you can effortlessly test the game rapidly in its
earliest stages. Initially, the initial step is to discover how much testing your
game necessitates. For this reason, you can use this particular type of testing to
determine whether the game satisfies the definite requirements or functions bug-
free. Besides, it also analyzes and scrutinizes all the outputs and inputs of the
game so you can get a clear image concerning distinct conceivable combinations
and outcomes. It is mostly used for commercial software testing to generate test
cases. The execution of combinatorial testing techniques in video game testing
improves the effectiveness of test execution, quality, phase containment, and
cost.
Ad Hoc Testing
Compatibility Testing
Compatibility testing aims to detect any defects in the functionality and shows
if the final product meets the essential requirements of the software, hardware,
and graphics. It’s better to keep the game users happy after all. Conducting
this type of test helps to validate whether the games UI (user interface) is
optimized for varied screen sizes of different handsets. This is done for both
mobile and PC games. For instance, in mobile games, this would be used for
testing whether the game will functions properly on the various sets of Android
devices. This technique can evaluate the compatibility of your game on different
gaming platforms.
Regression testing
6.2.4 Mr.Toad
Key Operation
Space bar To move in right direction
enter To change the state of game
Chapter 7
7 System Implementation and Maintenance
7.1 Implementation
System implementation is the conversion of new system into an operating one
which involves creating compatible files, training clients and installing hardware.
User training is crucial for minimizing resistance to change and giving chance
to prove its worth. Training aids user friendly manuals and healthy screens
provide the user with a good start. Software maintenance follows conversion
to the extent that changes are necessary to maintain satisfactory operations
relative to changes in the user’s environment. Maintenance often includes minor
enhancements or corrections to the problem that surface late in the systems
operations. In the implementation phase, the team builds the components either
from scratch or by composition. Given the architecture document meant from
the design phase and the requirement document from the analysis phase, the
team should build exactly what has been requested, though there is still room for
innovation and flexibility. For example, a component may be narrowly designed
for this particular system, or the component may be made more general to
satisfy a reusability
Careful planning
Investigation of system and constraints
Design the methods to achieve changeover.
Training the staff in the changed phase.
Evaluation of change over method.
The method of implementation and time scale to be adopted are found
out initially.
7.2 Maintenance
This phase occurs as a result of deploying the whole system at the end users
organization. They will perform the beta testing at the end users and inform to
the developers about any needed modification to the application. The customer
records all the problems that are encountered during the beta testing and reports
these to the developer at regular intervals.
Chapter 8
8 Conclusion and Future Scope
8.1 Conclusion
PING PONG: There are two players that can play the game at a time. Each
player can handle one of the paddles using the control keys (up and down keys
, w and s keys).
Mr.Toad : Single player will control the jumping of Mr.Toad using the space
bar and try to pass between the pipes.
Data Base
Online players
Appendix
A USER INTERFACES
A.1 PING PONG
A.2 Mr.Toad
B CODE
B.1 PING PONG
main.lua
[breaklines=true]
push = require ’push’
Class = require ’class’
require ’Paddle’
require ’Ball’
WINDOW_WIDTH = 1280
WINDOW_HEIGHT = 720
VIRTUAL_WIDTH = 432
VIRTUAL_HEIGHT = 243
function love.load()
love.graphics.setDefaultFilter(’nearest’, ’nearest’)
love.window.setTitle(’Pong’)
math.randomseed(os.time())
smallFont = love.graphics.newFont(’retro.ttf’, 8)
fpsFont = love.graphics.newFont(’retro.ttf’, 8)
largeFont = love.graphics.newFont(’retro.ttf’, 16)
scoreFont = love.graphics.newFont(’font.ttf’, 32)
love.graphics.setFont(smallFont)
sounds = {
[’paddle_hit’] = love.audio.newSource(’sounds/ping.mp3’, ’static’ ),
[’score’] = love.audio.newSource(’sounds/score.wav’, ’static’),
[’wall_hit’] = love.audio.newSource(’sounds/bounce.mp3’, ’static’)
}-- called either by sounds.score or sounds[’score’] to execute sounds.score:play()
push:setupScreen(VIRTUAL_WIDTH, VIRTUAL_HEIGHT, WINDOW_WIDTH, WINDOW_HEIGHT, {
fullscreen = true,
resizable = false,
vsync = true
})
player1Score = 0
player2Score = 0
servingPlayer = 1
gameState = ’start’
love.graphics.setBackgroundColor(0, 1, 0)
end
function love.resize(w, h)
push:resize(w,h)
end
function love.update(dt)
if gameState == ’serve’ then
love.graphics.setBackgroundColor(0, 1, 0)
-- before switching to play, initialize ball’s velocity based
-- on player who last scored
ball.dy = math.random(-50, 50)
if servingPlayer == 1 then
ball.dx = math.random(140, 200)
else
ball.dx = -math.random(140, 200)
end
elseif gameState == ’play’ then
love.graphics.setBackgroundColor(0, 1, 0)
-- detect ball collision with paddles, reversing dx if true and
-- slightly increasing it, then altering the dy based on the position of collision
if ball:collides(player1) then
sounds.paddle_hit:play()
ball.dx = -ball.dx * 1.03
ball.x = player1.x + 5
-- detect upper and lower screen boundary collision and reverse if collided
if ball.y <= 0 then
sounds.wall_hit:play()
ball.y = 0
ball.dy = -ball.dy
end
if player2Score == 10 then
winningPlayer = 2
gameState = ’done’
else
gameState = ’serve’
ball:reset()
end
end
if ball.x > VIRTUAL_WIDTH then
sounds.score:play()
servingPlayer = 2
player1Score = player1Score + 1
if player1Score == 10 then
winningPlayer = 1
gameState = ’done’
else
gameState = ’serve’
ball:reset()
end
end
end
-- player 1 movement
if love.keyboard.isDown(’w’) then
player1.dy = -PADDLE_SPEED
elseif love.keyboard.isDown(’s’) then
player1.dy = PADDLE_SPEED
else
player1.dy = 0
end
-- player 2 movement
if love.keyboard.isDown(’up’) then
player2.dy = -PADDLE_SPEED
elseif love.keyboard.isDown(’down’) then
player2.dy = PADDLE_SPEED
else
player2.dy = 0
end
-- update our ball based on its DX and DY only if we’re in play state;
-- scale the velocity by dt so movement is framerate-independent
if gameState == ’play’ then
ball:update(dt)
end
player1:update(dt)
player2:update(dt)
end
function love.keypressed(key)
player1Score = 0
player2Score = 0
if winningPlayer == 1 then
servingPlayer = 2
else
servingPlayer = 1
end
end
end
end
function love.draw()
push:apply(’start’)
love.graphics.setFont(smallFont)
displayScore()
player1:render()
player2:render()
ball:render()
displayFPS()
push:apply(’end’)
end
function displayFPS()
love.graphics.setFont(fpsFont)
love.graphics.print(’FPS: ’ .. tostring(love.timer.getFPS()), 10, 10)
end
function displayScore()
love.graphics.setFont(scoreFont)
love.graphics.print(tostring(player1Score), VIRTUAL_WIDTH / 2 - 50,
VIRTUAL_HEIGHT / 3)
love.graphics.print(tostring(player2Score), VIRTUAL_WIDTH / 2 + 30,
VIRTUAL_HEIGHT / 3)
end
class.lua
--[[
Copyright (c) 2010-2013 Matthias Richter
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
Except as contained in this notice, the name(s) of the above copyright holders
shall not be used in advertising or otherwise to promote the sale, use or
other dealings in this Software without prior written authorization.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
]]--
seen[from] = to
for k,v in pairs(from) do
k = include_helper({}, k, seen) -- keys might also be tables
if to[k] == nil then
to[k] = include_helper({}, v, seen)
end
end
return to
end
-- deeply copies ‘other’ into ‘class’. keys in ‘other’ that are already
-- defined in ‘class’ are omitted
local function include(class, other)
return include_helper(class, other, {})
end
-- class implementation
class.__index = class
class.init = class.init or class[1] or function() end
class.include = class.include or include
class.clone = class.clone or clone
-- constructor call
return setmetatable(class, {__call = function(c, ...)
local o = setmetatable({}, c)
o:init(...)
return o
end})
end
-- the module
return setmetatable({new = new, include = include, clone = clone},
{__call = function(_,...) return new(...) end})
push.lua
-- push.lua v0.2
local push = {
defaults = {
fullscreen = false,
resizable = false,
pixelperfect = false,
highdpi = true,
canvas = true
}
}
setmetatable(push, push)
function push:applySettings(settings)
for k, v in pairs(settings) do
self["_" .. k] = v
end
end
settings = settings or {}
self:initValues()
if self._canvas then
self:setupCanvas({ "default" }) --setup canvas
end
self._borderColor = {0, 0, 0}
self._drawFunctions = {
["start"] = self.start,
["end"] = self.finish
}
return self
end
function push:setupCanvas(canvases)
table.insert(canvases, { name = "_render" }) --final render
self._canvas = true
self.canvases = {}
for i = 1, #canvases do
self.canvases[i] = {
name = canvases[i].name,
shader = canvases[i].shader,
canvas = love.graphics.newCanvas(self._WWIDTH, self._WHEIGHT)
}
end
return self
end
function push:setCanvas(name)
if not self._canvas then return true end
return love.graphics.setCanvas( self:getCanvasTable(name).canvas )
end
function push:getCanvasTable(name)
for i = 1, #self.canvases do
if self.canvases[i].name == name then
return self.canvases[i]
end
end
end
function push:setShader(name, shader)
if not shader then
self:getCanvasTable("_render").shader = name
else
self:getCanvasTable(name).shader = shader
end
end
function push:initValues()
self._PSCALE = self._highdpi and love.window.getDPIScale() or 1
self._SCALE = {
x = self._RWIDTH/self._WWIDTH * self._PSCALE,
y = self._RHEIGHT/self._WHEIGHT * self._PSCALE
}
function push:start()
if self._canvas then
love.graphics.push()
love.graphics.setCanvas(self.canvases[1].canvas)
else
love.graphics.translate(self._OFFSET.x, self._OFFSET.y)
love.graphics.setScissor(self._OFFSET.x, self._OFFSET.y, self._WWIDTH*self._SCALE.x, self._W
love.graphics.push()
love.graphics.scale(self._SCALE.x, self._SCALE.y)
end
end
function push:finish(shader)
love.graphics.setBackgroundColor(unpack(self._borderColor))
if self._canvas then
love.graphics.pop()
--draw canvas
love.graphics.setCanvas(_render.canvas)
for i = 1, #self.canvases - 1 do --do not draw _render yet
local _table = self.canvases[i]
love.graphics.setShader(_table.shader)
love.graphics.draw(_table.canvas)
end
love.graphics.setCanvas()
--draw render
love.graphics.translate(self._OFFSET.x, self._OFFSET.y)
love.graphics.setShader(shader or self:getCanvasTable("_render").shader)
love.graphics.draw(self:getCanvasTable("_render").canvas, 0, 0, 0, self._SCALE.x, self._SCAL
--clear canvas
for i = 1, #self.canvases do
love.graphics.setCanvas( self.canvases[i].canvas )
love.graphics.clear()
end
love.graphics.setCanvas()
love.graphics.setShader()
else
love.graphics.pop()
love.graphics.setScissor()
end
end
function push:setBorderColor(color, g, b)
self._borderColor = g and {color, g, b} or color
end
function push:toGame(x, y)
x, y = x - self._OFFSET.x, y - self._OFFSET.y
local normalX, normalY = x / self._GWIDTH, y / self._GHEIGHT
return x, y
end
self:initValues()
love.window.setFullscreen(self._fullscreen, "desktop")
if not self._fullscreen and (winw or winh) then
love.window.setMode(self._RWIDTH, self._RHEIGHT) --set window dimensions
end
end
function push:resize(w, h)
local pixelScale = love.window.getDPIScale()
if self._highdpi then w, h = w / pixelScale, h / pixelScale end
self._RWIDTH = w
self._RHEIGHT = h
self:initValues()
end
return push
Ball.lua
[verbetim]
--[[
GD50 2018
Pong Remake
-- Ball Class --
Author: Colton Ogden
[email protected]
Represents a ball which will bounce back and forth between paddles
and walls until it passes a left or right boundary of the screen,
scoring a point for the opponent.
]]
Ball = Class{}
-- these variables are for keeping track of our velocity on both the
-- X and Y axis, since the ball can move in two dimensions
self.dy = math.random(2) == 1 and -100 or 100
self.dx = math.random(2) == 1 and math.random(-80, -100) or math.random(80, 100)
end
--[[
Expects a paddle as an argument and returns true or false, depending
on whether their rectangles overlap.
]]
function Ball:collides(paddle)
-- first, check to see if the left edge of either is farther to the right
-- than the right edge of the other
if self.x > paddle.x + paddle.width or paddle.x > self.x + self.width then
return false
end
-- then check to see if the bottom edge of either is higher than the top
-- edge of the other
if self.y > paddle.y + paddle.height or paddle.y > self.y + self.height then
return false
end
--[[
Places the ball in the middle of the screen, with an initial random velocity
on both axes.
]]
function Ball:reset()
self.x = VIRTUAL_WIDTH / 2 - 2
self.y = VIRTUAL_HEIGHT / 2 - 2
self.dy = math.random(2) == 1 and -100 or 100
self.dx = math.random(-50, 50)
end
--[[
Simply applies velocity to position, scaled by deltaTime.
]]
function Ball:update(dt)
self.x = self.x + self.dx * dt
self.y = self.y + self.dy * dt
end
function Ball:render()
love.graphics.setColor(1, 0, 0)
love.graphics.rectangle(’fill’, self.x, self.y, self.width, self.height)
end
Paddle.lua
Paddle = Class{}
--[[
The ‘init‘ function on our class is called just once, when the object
is first created. Used to set up all variables in the class and get it
ready for use.
Our Paddle should take an X and a Y, for positioning, as well as a width
and height for its dimensions.
Note that ‘self‘ is a reference to *this* object, whichever object is
instantiated at the time this function is called. Different objects can
have their own x, y, width, and height values, thus serving as containers
for data. In this sense, they’re very similar to structs in C.
]]
function Paddle:init(x, y, width, height)
self.x = x
self.y = y
self.width = width
self.height = height
self.dy = 0
end
function Paddle:update(dt)
-- math.max here ensures that we’re the greater of 0 or the player’s
-- current calculated Y position when pressing up so that we don’t
-- go into the negatives; the movement calculation is simply our
-- previously-defined paddle speed scaled by dt
if self.dy < 0 then
self.y = math.max(0, self.y + self.dy * dt)
-- similar to before, this time we use math.min to ensure we don’t
-- go any farther than the bottom of the screen minus the paddle’s
-- height (or else it will go partially below, since position is
-- based on its top left corner)
else
self.y = math.min(VIRTUAL_HEIGHT - self.height, self.y + self.dy * dt)
end
end
--[[
To be called by our main function in ‘love.draw‘, ideally. Uses
LÖVE2D’s ‘rectangle‘ function, which takes in a draw mode as the first
argument as well as the position and dimensions for the rectangle. To
change the color, one must call ‘love.graphics.setColor‘. As of the
newest version of LÖVE2D, you can even draw rounded rectangles!
]]
function Paddle:render()
love.graphics.rectangle(’fill’, self.x, self.y, self.width, self.height)
end
B.2 Mr.Toad
main.lua
[breaklines=true]
push = require ’push’
require ’states/BaseState’
require ’states/CountdownState’
require ’states/PlayState’
require ’states/ScoreState’
require ’states/TitleScreenState’
require ’Bird’
require ’Pipe’
require ’PipePair’
local backgroundScroll = 0
local BACKGROUND_SCROLL_SPEED = 30
local GROUND_SCROLL_SPEED = 60
function love.load()
-- initialize our nearest-neighbor filter
love.graphics.setDefaultFilter(’nearest’, ’nearest’)
love.graphics.setFont(flappyFont)
-- https://freesound.org/people/xsgianni/sounds/388079/
[’music’] = love.audio.newSource(’marios_way.mp3’, ’static’)
}
function love.resize(w, h)
push:resize(w, h)
end
function love.keypressed(key)
-- add to our table of keys pressed this frame
love.keyboard.keysPressed[key] = true
--[[
LÖVE2D callback fired each time a mouse button is pressed; gives us the
X and Y of the mouse, as well as the button in question.
]]
function love.mousepressed(x, y, button)
love.mouse.buttonsPressed[button] = true
end
function love.keyboard.wasPressed(key)
return love.keyboard.keysPressed[key]
end
--[[
Equivalent to our keyboard function from before, but for the mouse buttons.
]]
function love.mouse.wasPressed(button)
return love.mouse.buttonsPressed[button]
end
function love.update(dt)
if scrolling then
backgroundScroll = (backgroundScroll + BACKGROUND_SCROLL_SPEED * dt) % BACKGROUND_LOOPING_PO
groundScroll = (groundScroll + GROUND_SCROLL_SPEED * dt) % VIRTUAL_WIDTH
end
gStateMachine:update(dt)
love.keyboard.keysPressed = {}
love.mouse.buttonsPressed = {}
end
function love.draw()
push:start()
love.graphics.draw(background, -backgroundScroll, 0)
gStateMachine:render()
love.graphics.draw(ground, -groundScroll, VIRTUAL_HEIGHT - 16)
push:finish()
end
class.lua
--[[
Copyright (c) 2010-2013 Matthias Richter
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
Except as contained in this notice, the name(s) of the above copyright holders
shall not be used in advertising or otherwise to promote the sale, use or
other dealings in this Software without prior written authorization.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
]]--
seen[from] = to
for k,v in pairs(from) do
k = include_helper({}, k, seen) -- keys might also be tables
if to[k] == nil then
to[k] = include_helper({}, v, seen)
end
end
return to
end
-- deeply copies ‘other’ into ‘class’. keys in ‘other’ that are already
-- defined in ‘class’ are omitted
local function include(class, other)
return include_helper(class, other, {})
end
-- class implementation
class.__index = class
class.init = class.init or class[1] or function() end
class.include = class.include or include
class.clone = class.clone or clone
-- constructor call
return setmetatable(class, {__call = function(c, ...)
local o = setmetatable({}, c)
o:init(...)
return o
end})
end
-- the module
return setmetatable({new = new, include = include, clone = clone},
{__call = function(_,...) return new(...) end})
push.lua
-- push.lua v0.4
local push = {
defaults = {
fullscreen = false,
resizable = false,
pixelperfect = false,
highdpi = true,
canvas = true,
stencil = true
}
}
setmetatable(push, push)
function push:applySettings(settings)
for k, v in pairs(settings) do
self["_" .. k] = v
end
end
settings = settings or {}
windowUpdateMode(self._RWIDTH, self._RHEIGHT, {
fullscreen = self._fullscreen,
resizable = self._resizable,
highdpi = self._highdpi
})
self:initValues()
if self._canvas then
self:setupCanvas({ "default" }) --setup canvas
end
self._borderColor = {0, 0, 0}
self._drawFunctions = {
["start"] = self.start,
["end"] = self.finish
}
return self
end
function push:setupCanvas(canvases)
table.insert(canvases, { name = "_render", private = true }) --final render
self._canvas = true
self.canvases = {}
for i = 1, #canvases do
push:addCanvas(canvases[i])
end
return self
end
function push:addCanvas(params)
table.insert(self.canvases, {
name = params.name,
private = params.private,
shader = params.shader,
canvas = love.graphics.newCanvas(self._WWIDTH, self._WHEIGHT),
stencil = params.stencil or self._stencil
})
end
function push:setCanvas(name)
if not self._canvas then return true end
local canvasTable = self:getCanvasTable(name)
return love.graphics.setCanvas({ canvasTable.canvas, stencil = canvasTable.stencil })
end
function push:getCanvasTable(name)
for i = 1, #self.canvases do
if self.canvases[i].name == name then
return self.canvases[i]
end
end
end
function push:setShader(name, shader)
if not shader then
self:getCanvasTable("_render").shader = name
else
self:getCanvasTable(name).shader = shader
end
end
function push:initValues()
self._PSCALE = (not love11 and self._highdpi) and getDPI() or 1
self._SCALE = {
x = self._RWIDTH/self._WWIDTH * self._PSCALE,
y = self._RHEIGHT/self._WHEIGHT * self._PSCALE
}
function push:start()
if self._canvas then
love.graphics.push()
love.graphics.setCanvas({ self.canvases[1].canvas, stencil = self.canvases[1].stencil })
else
love.graphics.translate(self._OFFSET.x, self._OFFSET.y)
love.graphics.setScissor(self._OFFSET.x, self._OFFSET.y, self._WWIDTH*self._SCALE.x, self._W
love.graphics.push()
love.graphics.scale(self._SCALE.x, self._SCALE.y)
end
end
love.graphics.push()
love.graphics.origin()
local outputCanvas
for i = 1, #shaders do
love.graphics.setCanvas(_canvas)
love.graphics.draw(outputCanvas)
end
love.graphics.setShader(_shader)
end
function push:finish(shader)
love.graphics.setBackgroundColor(unpack(self._borderColor))
if self._canvas then
local _render = self:getCanvasTable("_render")
love.graphics.pop()
--draw canvas
love.graphics.setCanvas(_render.canvas)
for i = 1, #self.canvases do --do not draw _render yet
local _table = self.canvases[i]
if not _table.private then
local _canvas = _table.canvas
local _shader = _table.shader
self:applyShaders(_canvas, type(_shader) == "table" and _shader or { _shader })
end
end
love.graphics.setCanvas()
--draw render
love.graphics.translate(self._OFFSET.x, self._OFFSET.y)
local shader = shader or _render.shader
love.graphics.push()
love.graphics.scale(self._SCALE.x, self._SCALE.y)
self:applyShaders(_render.canvas, type(shader) == "table" and shader or { shader })
love.graphics.pop()
--clear canvas
for i = 1, #self.canvases do
love.graphics.setCanvas(self.canvases[i].canvas)
love.graphics.clear()
end
love.graphics.setCanvas()
love.graphics.setShader()
else
love.graphics.pop()
love.graphics.setScissor()
end
end
function push:setBorderColor(color, g, b)
self._borderColor = g and {color, g, b} or color
end
function push:toGame(x, y)
x, y = x - self._OFFSET.x, y - self._OFFSET.y
local normalX, normalY = x / self._GWIDTH, y / self._GHEIGHT
return x, y
end
self:initValues()
love.window.setFullscreen(self._fullscreen, "desktop")
if not self._fullscreen and (winw or winh) then
windowUpdateMode(self._RWIDTH, self._RHEIGHT) --set window dimensions
end
end
function push:resize(w, h)
if self._highdpi then w, h = w / self._PSCALE, h / self._PSCALE end
self._RWIDTH = w
self._RHEIGHT = h
self:initValues()
end
return push
pipe.lua
[verbetim]
--[[
Pipe Class
Author: Colton Ogden
[email protected]
The Pipe class represents the pipes that randomly spawn in our game, which act as our primar
The pipes can stick out a random distance from the top or bottom of the screen. When the pla
with one of them, it’s game over. Rather than our bird actually moving through the screen ho
the pipes themselves scroll through the game to give the illusion of player movement.
]]
Pipe = Class{}
-- since we only want the image loaded once, not per instantation, define it externally
local PIPE_IMAGE = love.graphics.newImage(’pipe.png’)
function Pipe:init(orientation, y)
self.x = VIRTUAL_WIDTH + 64
self.y = y
self.width = PIPE_WIDTH
self.height = PIPE_HEIGHT
self.orientation = orientation
end
function Pipe:update(dt)
end
function Pipe:render()
love.graphics.draw(PIPE_IMAGE, self.x,
(self.orientation == ’top’ and self.y + PIPE_HEIGHT or self.y),
0, 1, self.orientation == ’top’ and -1 or 1)
end
pipepair.lua
--[[
PipePair Class
Used to represent a pair of pipes that stick together as they scroll, providing an opening
for the player to jump through in order to score a point.
]]
PipePair = Class{}
function PipePair:init(y)
-- flag to hold whether this pair has been scored (jumped through)
self.scored = false
-- y value is for the topmost pipe; gap is a vertical shift of the second lower pipe
self.y = y
function PipePair:update(dt)
-- remove the pipe from the scene if it’s beyond the left edge of the screen,
-- else move it from right to left
if self.x > -PIPE_WIDTH then
self.x = self.x - PIPE_SPEED * dt
self.pipes[’lower’].x = self.x
self.pipes[’upper’].x = self.x
else
self.remove = true
end
end
function PipePair:render()
for l, pipe in pairs(self.pipes) do
pipe:render()
end
end
bird.lua
--[[
Bird Class
Author: Colton Ogden
[email protected]
The Bird is what we control in the game via clicking or the space bar; whenever we press eit
the bird will flap and go up a little bit, where it will then be affected by gravity. If the
the ground or a pipe, the game is over.
]]
Bird = Class{}
local GRAVITY = 20
function Bird:init()
self.image = love.graphics.newImage(’frog3.png’)
self.x = VIRTUAL_WIDTH / 2 - 8
self.y = VIRTUAL_HEIGHT / 2 - 8
self.width = self.image:getWidth()
self.height = self.image:getHeight()
self.dy = 0
end
--[[
AABB collision that expects a pipe, which will have an X and Y and reference
global pipe width and height values.
]]
function Bird:collides(pipe)
-- the 2’s are left and top offsets
-- the 4’s are right and bottom offsets
-- both offsets are used to shrink the bounding box to give the player
-- a little bit of leeway with the collision
if (self.x + 2) + (self.width - 4) >= pipe.x and self.x + 2 <= pipe.x + PIPE_WIDTH then
if (self.y + 2) + (self.height - 4) >= pipe.y and self.y + 2 <= pipe.y + PIPE_HEIGHT then
return true
end
end
return false
end
function Bird:update(dt)
self.dy = self.dy + GRAVITY * dt
function Bird:render()
love.graphics.draw(self.image, self.x, self.y)
end