I am trying to build a basic helicopter game where the aim is to avoid hitting the blocks.
When I hit a block I want the game to freeze and then pressing the space bar will start a fresh game.
Here is my code:
Main.py
import pygame
from helicopter import Helicopter
from block import Block
import random
pygame.init()
win = pygame.display.set_mode((700,400))
w, h = pygame.display.get_surface().get_size()
clock = pygame.time.Clock()
blocks = []
score = 0
helicopter = Helicopter(100, 200)
def drawGameWindow():
helicopter.draw(win)
for block in blocks:
if block.visible:
block.draw(win)
else:
blocks.pop(blocks.index(block))
pygame.display.update()
def main():
run = True
blockLimiter = 0
while run:
clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
if blockLimiter > 0:
blockLimiter +=1
if blockLimiter > 100:
blockLimiter = 0
if blockLimiter == 0:
blocks.append(Block(random.randint(50, 350)))
blockLimiter += 1
for block in blocks:
if helicopter.hitbox[1] < block.hitbox[1] + block.hitbox[3] and helicopter.hitbox[1] + helicopter.hitbox[3] > block.hitbox[1]:
if helicopter.hitbox[0] + helicopter.hitbox[2] > block.hitbox[0] and helicopter.hitbox[0] < block.hitbox[0] + block.hitbox[2]:
helicopter.hit()
keys = pygame.key.get_pressed()
if keys[pygame.K_SPACE]:
helicopter.y -= abs(helicopter.speed)
else:
helicopter.y += helicopter.speed
drawGameWindow()
pygame.quit()
main()
block.py
import pygame
class Block(object):
def __init__(self, y):
self.x = 700
self.y = y
self.height = 70
self.width = 40
self.visible = True
self.hitbox = (self.x -3, self.y -3, self.width + 6, self.height + 6)
def draw(self, win):
if self.x + self.width < 0:
self.visible = False
self.x -= 5
self.hitbox = (self.x -3, self.y -3, self.width + 6, self.height + 6)
pygame.draw.rect(win, (0, 255, 0), (self.x, self.y, self.width, self.height))
pygame.draw.rect(win, (255,0,0), self.hitbox, 2)
helicopter.py
import pygame
class Helicopter(object):
def __init__(self, x, y):
self.x = x
self.y = y
self.width = 70
self.height = 30
self.speed = 1 * (self.y - 100)*0.05
self.hitbox = (self.x - 3, self.y - 3, self.width + 6, self.height + 6)
self.alive = True
def draw(self, win):
win.fill((0,0,0))
self.hitbox = (self.x - 3, self.y - 3, self.width + 6, self.height + 6)
pygame.draw.rect(win, (0, 255, 0), (self.x, self.y, 70, 30))
pygame.draw.rect(win, (255, 0, 0), self.hitbox, 2)
def hit(self):
self.alive = False
while not self.alive:
keys = pygame.key.get_pressed()
if keys[pygame.K_SPACE]:
self.alive = True
self.x = 100
self.y = 200
What I want to happen:
helicopter hits block -> helicopter.hit()
is called -> helicopter.alive
is made False
-> Game is checking for space button to be pressed at which point it helicopter.alive becomes True the x,y coordinates of the helicopter is reset and the game starts over (I am yet to implement scoring but the scoring will reset).
What actually happens is the game crashes when I hit a block.
Can anyone explain how I can fix this?
Thanks.
You have an application loop, so please use it. Never implement nested game loops. Your game freezes, because the inner loop doesn't handle the events.
The main application loop has to:
- handle the events, by either
pygame.event.pump()
orpygame.event.get()
. - change the game states dependent on the input events and time
- clear the display
- redraw the scene
- update the display
The class Helicopter
needs 2 functions. hit
sets self.alive = False
and reset
, sets the initial game states:
class Helicopter(object):
# [...]
def hit(self):
self.alive = False
print("hit")
def reset(self):
self.alive = True
self.x = 100
self.y = 200
print("reset")
The game loop has 2 different cases, dependent on the state of helicopter.alive
. It helicopter.alive
is True
, t he game runs. If it is False
, the game waits for SPACE to be pressed and to be continued:
def main():
run = True
blockLimiter = 0
while run:
clock.tick(60)
# handle events
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
# update game states
keys = pygame.key.get_pressed()
if helicopter.alive:
if blockLimiter > 0:
blockLimiter +=1
if blockLimiter > 100:
blockLimiter = 0
if blockLimiter == 0:
blocks.append(Block(random.randint(50, 350)))
blockLimiter += 1
for block in blocks:
if helicopter.hitbox[1] < block.hitbox[1] + block.hitbox[3] and helicopter.hitbox[1] + helicopter.hitbox[3] > block.hitbox[1]:
if helicopter.hitbox[0] + helicopter.hitbox[2] > block.hitbox[0] and helicopter.hitbox[0] < block.hitbox[0] + block.hitbox[2]:
helicopter.hit()
if keys[pygame.K_SPACE]:
helicopter.y -= abs(helicopter.speed)
else:
helicopter.y += helicopter.speed
else:
if keys[pygame.K_SPACE]:
helicopter.reset()
# clear display, draw scene and update diesplay
drawGameWindow()
pygame.quit()
Further more I recommend to iterate on a copy of the blocks (blocks[:]
). Then you can remove a block from the original list with out affecting the iteration (blocks.remove(block)
)
def drawGameWindow():
helicopter.draw(win)
for block in blocks[:]:
if block.visible:
block.draw(win)
else:
blocks.remove(block)
pygame.display.update()