1

I am using pygame to make a flappy bird game. Everything is going well but when I try to end the game my if statement is being completely ignored.

import pygame
import time
import sys
import random
import neat
#initialize pygame 
pygame.init()

#sys vars
screen = pygame.display.set_mode((1080, 720)) #set width and height for game window {Try and find a                     
call for finding the comps defaults}
clock = pygame.time.Clock()
############

#game vars
Floor_x = 0
gravity = 0.25
bird_movement = 0
start = 0
PIPE_LIST = []
SPAWNPIPE = pygame.USEREVENT
pygame.time.set_timer(SPAWNPIPE, 1200)
Pipe_Height = [500, 550, 600, 650, 700, 750]
game_active = True;
###########


#load surfaces
Bg_day = pygame.image.load('Assets/background-day.png').convert() #create a surface to be displayed         
on the display surface
Bg_day = pygame.transform.scale(Bg_day, (1080, 720))
Bg_night = pygame.image.load('Assets/background-night.png').convert()
Bg_night = pygame.transform.scale(Bg_night, (1080, 720))
Bg_floor = pygame.image.load('Assets/base.png').convert()
Bg_floor = pygame.transform.scale(Bg_floor, (1080, 720))
Game_Anims_Bird_midFlap = pygame.image.load('Assets/redbird-midflap.png').convert()
Game_Anims_Bird_midFlap = pygame.transform.scale2x(Game_Anims_Bird_midFlap)
Bird_HitBox = Game_Anims_Bird_midFlap.get_rect(center = (100,355))
Bg_ToUse = Bg_day
Game_Pipes_Red = pygame.image.load('Assets/pipe-red.png').convert()
Game_Pipes_Red = pygame.transform.scale2x(Game_Pipes_Red)
############


#FUNCTIONS
def Draw_Bg(x):
    screen.blit(Bg_ToUse,(0,0)) #display our created surface in the specified coords
    screen.blit(Bg_floor,(x,650))
    screen.blit(Bg_floor,(x + 1080,650))
    if x <= -1080: 
        x = 0

def CreatePipe():
    PipeHeight = random.choice(Pipe_Height)
    top_pipe = Game_Pipes_Red.get_rect(midtop = (1090,PipeHeight))
    bottom_pipe = Game_Pipes_Red.get_rect(midbottom = (1090,PipeHeight - 225))
    return bottom_pipe, top_pipe

def Pipe_Movement(Pipes):
    for pipe in Pipes:
        pipe.centerx -= 5
    return Pipes

def Draw_Pipes(Pipes):
    for pipe in Pipes:
        if pipe.bottom >= 720:
            screen.blit(Game_Pipes_Red, pipe)
        else:
            screen.blit(pygame.transform.flip(Game_Pipes_Red, False, True), pipe)

def CollisionDetection(Pipes):
    for pipe in Pipes:
        if Bird_HitBox.colliderect(pipe):
            game_active = False
            print('Game over')
    if Bird_HitBox.centery >= 720 or  Bird_HitBox.centery <= -30:
        print('out of bounds')
        game_active = False
 ############


while True:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()
            sys.exit()
        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_SPACE:
                if start == 0 and game_active == True:
                    start += 1
                    print('game start')
                    bird_movement = 0
                    bird_movement -= 7
            if event.key == pygame.K_RETURN:
                if Bg_ToUse == Bg_day:
                    Bg_ToUse = Bg_night
                else:
                    Bg_ToUse = Bg_day
        if event.type == SPAWNPIPE and start != 0 and game_active == True:
            if start != 0:
                PIPE_LIST.extend(CreatePipe())

    if game_active != False:    #this is the problematic statement it will 
                                #run even when game_active is set to false
        if start != 0: # game is active d raw everything
            Floor_x -= 1;
            bird_movement += gravity
            Bird_HitBox.centery += bird_movement
            Draw_Bg(Floor_x)
            screen.blit(Game_Anims_Bird_midFlap,Bird_HitBox)
            CollisionDetection(PIPE_LIST)
            PIPE_LIST = Pipe_Movement(PIPE_LIST)
            Draw_Pipes(PIPE_LIST)
            if Floor_x <= -1080: 
                Floor_x = 0
            pygame.display.update()
            clock.tick(120)
        else: #draw floor and bird
            Draw_Bg(Floor_x)
            screen.blit(Game_Anims_Bird_midFlap,Bird_HitBox)
            Floor_x -= 1;
            pygame.display.update()
            clock.tick(120)
            #floor function to make it go forever
    else:
        print('game_active == false')
        PIPE_LIST = []
        bird_movement = 0
        while game_active == False:
            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    pygame.quit()
                    sys.exit()
            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_SPACE:
                    PIPE_LIST = []

The issue is when the bird collides with a pipe or goes out of bounds, game_active is set to false but the if statement still runs.

edit - added a comment where the problematic if statement is

  • 1
    Which `if` statement exactly is ignored? There are many of them in the code you have shown. Please create a [mre]. – mkrieger1 Oct 18 '20 at 02:03
  • What did you find when you tried to debug this? You should have observed that `game_active` is not really set to `False` when you think it is. – mkrieger1 Oct 18 '20 at 02:07
  • @mkrieger1 I set a breakpoint to see if the statement that sets it to false is ran and it is so i cant figure out why it isn't working – JinxedGrimф Oct 18 '20 at 02:09
  • Please provide the expected [MRE](https://stackoverflow.com/help/minimal-reproducible-example). Show where the intermediate results deviate from the ones you expect. We should be able to paste a single block of your code into file, run it, and reproduce your problem. – Prune Oct 18 '20 at 02:34

3 Answers3

3

Your game_active is a global variable. What you are setting in collision detection function is a local variable of same name (created when you first set it). You need to tell Python interpreter you want to work with that global variable by using global game_active before setting it in collision detection function.

In Python variables don't need to be explicitly declared and are created on first use. Also Python interpreter doesn't try to resolve variable names beyond scope of current function (hence the need of global keyword).

See https://docs.python.org/3/reference/simple_stmts.html#global for more details.

NOTE: It is not "pythonic" or good practice to use global variables and global keyword is reserved for few special cases. In Python everything including function is an object and objects can have properties. It would be more "pythonic" to enclose your main loop that breaks on !game_active into a function and make game_active attribute of that function. To break game you'd then simply set property of game loop.

blami
  • 4,897
  • 1
  • 19
  • 25
2

Your code is quite bloated and far from minimal reproducible; But your problem is, that you do not actually set the variable you intent to set.

You are dealing with global variables, which you have to mark in your functions if you intend to edit their global value.

Per default, the variable you set to false is limited in scope to the function.

This question will answer your problem and would've been what was expected of you here. A minimal example and not all your code ;)

How to change global variables in Python

As last hint: don't use global variables. As you witnessed they tend to be counterintuitive and not worth the effort making them work.

Aiyion.Prime
  • 895
  • 7
  • 19
0

The bug in your code is in the CollisionDetection function.

def CollisionDetection(Pipes):
    for pipe in Pipes:
        if Bird_HitBox.colliderect(pipe):
            game_active = False
            print('Game over')
    if Bird_HitBox.centery >= 720 or  Bird_HitBox.centery <= -30:
        print('out of bounds')
        game_active = False

When the statement game_active = False is made, this is declaring game_active in the local scope to False. This means that this instance of game_active can only be referenced in the CollisionDetection function, so the game_active variable found in the while loop is a different instance of the variable.

To fix this, either define game_active in the global scope, or return the value of game_active and set the while loop game_active to the return value of CollisionDetection.

def CollisionDetection(Pipes):
    for pipe in Pipes:
        if Bird_HitBox.colliderect(pipe):
            print('Game over')
            return False
    if Bird_HitBox.centery >= 720 or  Bird_HitBox.centery <= -30:
        print('out of bounds')
        return False

...
if game_active != False:    #this is the problematic statement it will 
                                #run even when game_active is set to false
        if start != 0: # game is active d raw everything
            Floor_x -= 1;
            bird_movement += gravity
            Bird_HitBox.centery += bird_movement
            Draw_Bg(Floor_x)
            screen.blit(Game_Anims_Bird_midFlap,Bird_HitBox)
            
            #Set game_active to the return value of Collision Detection:
            game_active = CollisionDetection(PIPE_LIST)
            PIPE_LIST = Pipe_Movement(PIPE_LIST)
            Draw_Pipes(PIPE_LIST)
            if Floor_x <= -1080: 
                Floor_x = 0
            pygame.display.update()
            clock.tick(120)
        else: #draw floor and bird
            Draw_Bg(Floor_x)
            screen.blit(Game_Anims_Bird_midFlap,Bird_HitBox)
            Floor_x -= 1;
            pygame.display.update()
            clock.tick(120)

Note: While declaring game_active in the global scope will work, it is 'safer' to return the value rather.

Jacob Lee
  • 3,649
  • 2
  • 12
  • 30