Why is pygame crashing when trying to restart game?

thd123 :

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.

Rabbid76 :

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() or pygame.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()

Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=3877&siteId=1