The assignment I'm working on is a Flappy Bird clone. If you're unfamiliar, the game has a bird flying through gaps between sets of upper and lower pipes. The students are given the assignment code for the complete game and then have to make modifications. I will post the code at the end.
Here are the relevant parts I'm dealing with. We have a class, Pipe.lua, that's used to generate a single pipe. Then we have a class, PipePair.lua, that puts two of the single pipes together into a pipe pair, turning one pipe upside down in the process and setting a gap between them. We also have a State Machine. One of the states, PlayState, governs the play of the game.
PipePair.lua, we set a constant GAP_HEIGHT to 90. This is for the gap between the upper and lower pipe. We then have the init() function where we instantiate a pair of pipes as follows:
self.pipes = {
['upper'] = Pipe('top', self.y),
['lower'] = Pipe('bottom', self.y + PIPE_HEIGHT + GAP_HEIGHT)
}
PIPE_HEIGHT is 288, the size of our pipe sprite.
In PlayState.lua. we have a variable self.y as a starting point to where we will draw each pipe. We set as follows:
self.lastY = -PIPE_HEIGHT + math.random(1,80) + 20
Then in the update method we set a local variable y to the following:
local y = math.max(-PIPE_HEIGHT + 10, math.min(self.lastY + math.random(-20, 20), VIRTUAL_HEIGHT - 90 - PIPE_HEIGHT))
self.lastY = y
table.insert(self.pipePairs, PipePair(y))
VIRTUAL_HEIGHT is 288, same as PIPE_HEIGHT. The purpose of this method is to change the location of the gap for each pipe pair somewhere between 20 pixels lower and 20 pixels higher than the last gap, in order to create a kind of flow. The purpose of the (+10) in the first value of math.max is to make sure the upper pipe is showing at least 10 pixels so you see the pipe. The hard-coded value 90 in the second value of math.min is supposed to represent the gap height. The purpose of this second value in math.min is to make sure the bottom pipe doesn't start being drawn below the screen and is thus not visible.
Finally, Pipe.lua has the render function for each pipe as follows:
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)
I discovered a bug in the code. I noticed in the game that sometimes the bottom pipe was not visible. After doing some testing and calculating, I discovered that it was possible for the y of the bottom pipe to be set to 288, which is the virtual screen height. Unlike the top pipe, the bottom pipe is not set to be visible above the ground as a minimum height. The fix is to change the hard-coded 90 in PlayState's local y calculation to 110. But this only works if the gap height is 90. If the gap height is 60 the value needs to be 80, and if the gap height is 120, the value needs to be 140.
Here's the problem. Part of the assignment is for us to randomize the gap height in each spawn of pipe pairs. I'm trying to randomize the gap height between 60 and 120. But to make sure the bottom pipe is visible I need to provide the same value of gap height to both the PipePair class and the PlayState class. The random number needs to change on each update. But I can't figure out where to put a random number generation that will change for each update and share that value with both the PipePair and PlayState classes. How can I do that?
Here are the relevant files, minus comments:
PlayState.lua
Code: Select all
PlayState = Class{__includes = BaseState}
PIPE_SPEED = 60
PIPE_WIDTH = 70
PIPE_HEIGHT = 288
BIRD_WIDTH = 38
BIRD_HEIGHT = 24
function PlayState:init()
self.bird = Bird()
self.pipePairs = {}
self.timer = 0
self.score = 0
self.lastY = -PIPE_HEIGHT + math.random(80) + 20
end
function PlayState:update(dt)
self.timer = self.timer + dt
if self.timer > 2 then
local y = math.max(-PIPE_HEIGHT + 10,
math.min(self.lastY + math.random(-20,20), VIRTUAL_HEIGHT - 90 - PIPE_HEIGHT))
self.lastY = y
table.insert(self.pipePairs, PipePair(y))
self.timer = 0
end
for k, pair in pairs(self.pipePairs) do
if not pair.scored then
if pair.x + PIPE_WIDTH < self.bird.x then
self.score = self.score + 1
pair.scored = true
sounds['score']:play()
end
end
pair:update(dt)
end
for k, pair in pairs(self.pipePairs) do
if pair.remove then
table.remove(self.pipePairs, k)
end
end
for k, pair in pairs(self.pipePairs) do
for l, pipe in pairs(pair.pipes) do
if self.bird:collides(pipe) then
sounds['explosion']:play()
sounds['hurt']:play()
gStateMachine:change('score', {
score = self.score
})
end
end
end
self.bird:update(dt)
if self.bird.y > VIRTUAL_HEIGHT - 15 then
sounds['explosion']:play()
sounds['hurt']:play()
gStateMachine:change('score', {
score = self.score
})
end
end
function PlayState:render()
for k, pair in pairs(self.pipePairs) do
pair:render()
end
love.graphics.setFont(flappyFont)
love.graphics.print('Score: ' .. tostring(self.score), 8, 8)
self.bird:render()
end
function PlayState:enter()
scrolling = true
end
function PlayState:exit()
scrolling = false
end
Code: Select all
PipePair = Class{}
local GAP_HEIGHT = 90
function PipePair:init(y)
self.scored = false
self.x = VIRTUAL_WIDTH + 32
self.y = y
self.pipes = {
['upper'] = Pipe('top', self.y),
['lower'] = Pipe('bottom', self.y + PIPE_HEIGHT + GAP_HEIGHT)
}
self.remove = false
end
function PipePair:update(dt)
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
Code: Select all
Pipe = Class{}
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