3

I made a game in pygame which involves main menu, game loop, victory screen and crash screen. My game works how I want it to work, but I know everytime I change a screen, I am just going deeper into the loop and game can crash when i reach 1000 recursions (I think so?)). I don't know how to fix it, so if you could please Help me.

Here is the code:

import pygame
import sys


pygame.init()
pygame.display.set_caption("My Game")
screen_width, screen_height = 1200, 600
screen = pygame.display.set_mode((screen_width, screen_height))
clock = pygame.time.Clock()
BLUE = pygame.Color('dodgerblue3')
ORANGE = pygame.Color('sienna3')
BLACK = (0, 0, 0)
WHITE = (255,255,255)
RED = (255, 0, 0)
GREEN = (13, 255, 0)
YELLOW = (0, 255, 20)
BRIGHT_YELLOW = (255, 255, 20)
font = pygame.font.Font(None, 25)
frame_rate = 60
last_seconds = None

class Walls(pygame.Rect):

    def __init__(self, x, y, w, h):
        super().__init__(x, y, w, h)


class LeftRedRect(pygame.Rect):

    def __init__(self, x, y, w, h, vel):
        # Calling the __init__ method of the parent class
        super().__init__(x, y, w, h)
        self.vel = vel

    def update(self):
        self.x += self.vel  # Moving
        if self.right > 600 or self.left < 320:  # If it's not in this area
            self.vel = -self.vel  # Inverting the direction


class RightRedRect(pygame.Rect):

    def __init__(self, x, y, w, h, vel):
        super().__init__(x, y, w, h)
        self.vel = vel

    def update(self):
        self.x += self.vel
        if self.right > 1180 or self.left < 620:
            self.vel = -self.vel


class UpAndDownRedRect(pygame.Rect):

    def __init__(self, x, y, w, h, vel):
        super().__init__(x, y, w, h)
        self.vel = vel

    def update(self):
        self.y += self.vel
        if self.top < 20 or self.bottom > 535:
            self.vel = -self.vel


def quit_game():
    pygame.quit()
    sys.exit()

def message_display(text):
    largeText = pygame.font.Font(None, 115)
    screen.blit(largeText.render(text, True, BLUE), (370, 250))
    pygame.display.update()

    pygame.time.wait(1500)

def text_objects(text, font):
    textSurface = font.render(text, True, BLACK)
    return textSurface, textSurface.get_rect()

def button(msg, x, y, w, h, ic, ac, action = None):
    mouse = pygame.mouse.get_pos()
    click = pygame.mouse.get_pressed()


    if x + w > mouse[0] > x and y + h  > mouse[1] > y:
        pygame.draw.rect(screen, ac, (x, y, w, h))
        if click[0] == 1 and action is not None:
            action()
    else:
        pygame.draw.rect(screen, ic, (x, y, w, h))

    smallText = pygame.font.Font("freesansbold.ttf",35)
    textSurf, textRect = text_objects(msg, smallText)
    textRect.center = ((x+(w/2)), (y+(h/2)))
    screen.blit(textSurf, textRect)

def restart():
    while True:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                quit_game()

        screen.fill(WHITE)

        largeText = pygame.font.Font(None, 115)
        screen.blit(largeText.render("You lost", True, BLUE), (420, 50))

        button("Restart", 525, 250, 150, 60, BRIGHT_YELLOW, YELLOW, menu)
        button("Quit", 525, 350, 150, 60, BRIGHT_YELLOW, YELLOW, quit_game)

        pygame.display.update()
        pygame.display.flip()
        clock.tick(60)

def victory_screen():
    while True:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                quit_game()

        screen.fill(WHITE)

        #TO DO: POSITION THE TEXT#
        largeText = pygame.font.Font(None, 115)
        screen.blit(largeText.render("Congratulations!", True, BLUE), (300, 50))
        largeText = pygame.font.Font(None, 60)
        screen.blit(largeText.render("You beat the game!", True, BLUE), (400, 150))

        button("Restart", 525, 250, 150, 60, BRIGHT_YELLOW, YELLOW, menu)
        button("Quit", 525, 350, 150, 60, BRIGHT_YELLOW, YELLOW, quit_game)

        pygame.display.update()
        pygame.display.flip()
        clock.tick(frame_rate)

def front_page():
    while True:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                quit_game()

        screen.fill(WHITE)

        largeText = pygame.font.Font(None, 115)
        screen.blit(largeText.render("My Game", True, BLUE), (430, 50))

        button("Start", 525, 250, 150, 60, BRIGHT_YELLOW, YELLOW, menu)
        button("Quit", 525, 350, 150, 60, BRIGHT_YELLOW, YELLOW, quit_game)

        pygame.display.update()
        pygame.display.flip()
        clock.tick(frame_rate)


def menu():
    vel = 4
    vel_left = 5
    vel_right = -5
    vel_up = 7

    player = pygame.Rect(40, 45, 30, 30)

    finish_line = pygame.Rect(620, 535, 560, 45)

    walls = [
        Walls(0, 0, 1200, 20), Walls(0, 0, 20, 600),
        Walls(0, 580, 1200, 20), Walls(1180, 0, 20, 600),
        Walls(300, 0, 20, 530), Walls(20, 100, 230, 20),
        Walls(70, 200, 230, 20), Walls(20, 300, 230, 20),
        Walls(70, 400, 230, 20), Walls(600, 100, 20, 500)
    ]

    leftredrects = [
        LeftRedRect(320, 120, 30, 30, vel_left),
        LeftRedRect(320, 240, 30, 30, vel_left),
        LeftRedRect(320, 360, 30, 30, vel_left),
        LeftRedRect(570, 180, 30, 30, vel_right),
        LeftRedRect(570, 300, 30, 30, vel_right),
        LeftRedRect(570, 420, 30, 30, vel_right)
    ]

    rightredrects = [
        RightRedRect(1140, 120, 30, 30, vel_left),
        RightRedRect(1140, 240, 30, 30, vel_left),
        RightRedRect(1140, 360, 30, 30, vel_left),
        RightRedRect(620, 180, 30, 30, vel_right),
        RightRedRect(620, 300, 30, 30, vel_right),
        RightRedRect(620, 420, 30, 30, vel_right),
    ]

    upanddownredrects = [
        UpAndDownRedRect(620, 20, 30, 30, vel_up),
        UpAndDownRedRect(752, 505, 30, 30, vel_up),
        UpAndDownRedRect(885, 20, 30, 30, vel_up),
        UpAndDownRedRect(1016, 505, 30, 30, vel_up),
        UpAndDownRedRect(1150, 20, 30, 30, vel_up)
    ]

    while True:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                quit_game()

        keys = pygame.key.get_pressed()

        # Player coordinates
        if keys[pygame.K_LEFT] and player.x > 0:
            player.x -= vel
        if keys[pygame.K_RIGHT] and player.x < 1200 - player.width:
            player.x += vel
        if keys[pygame.K_UP] and player.y > 0:
            player.y -= vel
        if keys[pygame.K_DOWN] and player.y < 600 - player.height:
            player.y += vel

        # Game logic
        for wall in walls:
            # Check if the player rectangle collides with a wall rectangle
            if player.colliderect(wall):
                print("Game over")
               # message_display("Game Over")
               # restart()

        for rect in rightredrects:
            rect.update()  # Movement and bounds checking
            if player.colliderect(rect):
                print("Game over")
               # message_display("Game Over")
               # restart()

        for rect in leftredrects:
            rect.update()
            if player.colliderect(rect):
                print("Game over")
               # message_display("Game Over")
                #restart()

        for rect in upanddownredrects:
            rect.update()
            if player.colliderect(rect):
                print("Game over")
                #message_display("Game Over")
                #restart()

        if player.colliderect(finish_line):
            print("You beat the game")
            victory_screen()

        # Drawing everything
        screen.fill(WHITE)

        pygame.draw.rect(screen, BRIGHT_YELLOW, finish_line)

        for wall in walls:
            pygame.draw.rect(screen, BLACK, wall)

        for rect in rightredrects:
            pygame.draw.rect(screen, RED, rect)

        for rect in leftredrects:
            pygame.draw.rect(screen, RED, rect)

        for rect in upanddownredrects:
            pygame.draw.rect(screen, RED, rect)


        pygame.draw.rect(screen, GREEN, player)

        pygame.display.update()

        pygame.display.flip()
        clock.tick(frame_rate)


def main():
    scene = front_page  # Set the current scene.
    while scene is not None:
        # Execute the current scene function. When it's done
        # it returns either the next scene or None which we
        # assign to the scene variable.
        scene = scene()


main()
pygame.quit()
RaZiiiGG
  • 203
  • 1
  • 15
  • 1
    In addition to skrx' answer, take a look at [this](https://stackoverflow.com/questions/14700889/pygame-level-menu-states/14727074#14727074) – sloth Aug 27 '18 at 06:12

2 Answers2

1

for every place where you print('Game over') return True like i did in this part of your code:

        for wall in walls:
        # Check if the player rectangle collides with a wall rectangle
        if player.colliderect(wall):
            print("Game over")
            return True

if you dont the game is still in the while loop and it will print "game over" until you can cook eggs on your computer or it crashes.

Thierry Lathuille
  • 23,663
  • 10
  • 44
  • 50
Stanley
  • 2,434
  • 18
  • 28
1

You could add a next_scene variable to your scene and set it to the next scene function when the button gets pressed. In the while loop you would have to check if next_scene is not None: and then return the next_scene to the main function where it will be called. It would be necessary to define nested callback functions to change the next_scene variable.

def front_page():
    next_scene = None

    def start_game():
        nonlocal next_scene
        # Set the `next_scene` variable in the enclosing scope
        # to the `menu` function.
        next_scene = menu

    while True:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                quit_game()

        # Return the next scene to the `main` function if the variable is not None.
        if next_scene is not None:
            return next_scene

        screen.fill(WHITE)

        button("Start", 525, 250, 150, 60, BRIGHT_YELLOW, YELLOW, start_game)
        button("Quit", 525, 350, 150, 60, BRIGHT_YELLOW, YELLOW, quit_game)

        pygame.display.flip()  # Don't call both display.update and display.flip.
        clock.tick(60)

In the menu function you can just return the restart function when the player touches a wall or a moving rect:

for wall in walls:
    # Check if the player rectangle collides with a wall rectangle
    if player.colliderect(wall):
        print("Game over")
        return restart
skrx
  • 19,980
  • 5
  • 34
  • 48
  • How do I check if there will be no more recursion errors? Because I wrote what u did in front_page() function and next_scene and start_game() are appearing gray... Do I have to write that in the other scenes or? I am not quite sure if I understood this correctly. – RaZiiiGG Aug 26 '18 at 10:49
  • I am sorry, actually everything is good, I just didn't change my button, I can't even copy the code lol i suck – RaZiiiGG Aug 26 '18 at 11:04