Exiting If statement loop python - python-3.x

For some reason I cannot exit this loop when I change to intro = false, can anyone help as to how I'd exit this if statement. It is my menu screen, and once I click 'new game' I want it to exit the game_intro function.
This is where I define game_intro:
def game_intro():
intro = True
if intro == True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
quit()
window.fill(superlightgrey)
fontvertical = pygame.font.SysFont("comicsansms", 100)
text = fontvertical.render("Connect 4", True, skyblue)
word = (10, 1)
window.blit(text,word)
ButtonIntro("New Game", 340, 140, 200, 50, superlightgrey, superlightgrey, "Play")
This is where I created the button function:
def ButtonIntro(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(window, ac, (x, y, w, h))
if click[0] == 1 and action != None:
pygame.draw.rect(window, lightgrey, (x, y, w, h))
if action == "Play":
intro = False
##WHAT DO I NEED HERE TO EXIT LOOP
and this is where I call on the function:
while intro == True:
game_intro()
print("loopexited")

The intro variable is inside a function, when you create a variable inside a function it's not connected to anything outside the function.
intro = True
def myfunction():
intro = False
myfunction()
print(intro)
This code prints:
True
The intro variable inside the myfunction is created as an entirely separate variable from the one outside.
It looks like you might have another separate intro variable inside the game_intro function too.
You can get around this using the global keyword, but you might be better off trying to find a different way to structure your code (global is considered a bad practice.)

Related

Why does my program stop responding and crashing?

I was just trying to learn some PyGame which will be needed for my coming project so I would like to know why this sample piece of code is crashing and how I can fix it.
I've tried looking online and haven't found a working solution.
import pygame
# Initialise pygame
pygame.init()
clock = pygame.time.Clock()
# pygame.font.init()
myFont = pygame.font.SysFont('Comic Sans MS', 30)
# Colours
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
BLUE = (0, 0, 255)
GREEN = (0, 255, 0)
RED = (255, 0, 0)
# Screen Size
size = [400, 300]
screen = pygame.display.set_mode(size)
# Initialise Variables
done = False
class Unit:
def __init__(self, name, hp, attack):
self.name = name
self.hp = hp
self.attack = attack
class Infantry(Unit):
def __init__(self, name, hp, attack):
super().__init__(name, hp, attack)
def attack_turn(self):
self.hp -= self.attack
text_surface = myFont.render(self.name + " has " + str(self.hp) + " health points left!", True, (255, 0, 0))
screen.blit(text_surface, (0, 0))
# print(self.name + " has " + str(self.hp) + " health points left!")
x = input("Press ENTER to continue!")
player = Infantry("Panzer", 110, 13) # The attack set here is for the enemy.
enemy = Infantry("T-14", 100, 30) # The attack set here is for the player.
while not done:
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
while player.hp > 0 and enemy.hp > 0:
player.attack_turn()
enemy.attack_turn()
done = True
if player.hp > enemy.hp:
# print(player.name + " wins!")
text_surface = myFont.render(player.name + " wins!", False, (255, 0, 0))
screen.blit(text_surface, (100, 100))
else:
# print(enemy.name + " wins!")
text_surface = myFont.render(enemy.name + " wins!", False, (255, 0, 0))
screen.blit(text_surface, (100, 100))
I hoped that the code would at least run so I could work off there but it's not even doing that.
Don't do another loop inside the main loop. The inner loop prevents the event handling, get rid of it. It is sufficient to check if the game has finished at the end of the main loop:
done = False
while not done:
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
player.attack_turn()
enemy.attack_turn()
if player.hp <= 0 or enemy.hp <= 0:
done = True
pygame.display.flip()
input does not do what you want it is not an input to the game window. Delete it and sperate the attack method to on method which calculates the new health and a ne method draw, which "blit" the text:
class Infantry(Unit):
def __init__(self, name, hp, attack):
super().__init__(name, hp, attack)
def attack_turn(self):
self.hp -= self.attack
### x = input("Press ENTER to continue!") <----- delete
def draw(self):
text_surface = myFont.render(self.name + " has " + str(self.hp) + " health points left!", True, (255, 0, 0))
screen.blit(text_surface, (0, 0))
Use the pygame.KEYUP event to check if the pygame.K_RETURN key was pressed.
Further do the all the drawing at the end of the main loop. Use pygame.Surface.fill to fill screen by a single color (clear the screen). Then draw the text. Finally use pygame.display.flip() to update the full display:
e.g.
done = False
count = 0
while not done:
attack = False
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_RETURN:
attack = True
if attack:
if count % 2 == 0:
player.attack_turn()
else:
enemy.attack_turn()
count += 1
if player.hp <= 0 or enemy.hp <= 0:
done = True
screen.fill(BLACK)
if count % 2 == 0:
player.draw()
else:
enemy.draw()
pygame.display.flip()

While true loops giving me recursion errors

I'm creating a game with pygame for my CompSci course. The game is working well, but now I'm trying to make a restart function and I'm getting alot of errors.
The basic outline of what I'm trying to do is:
first = True
def check(count):
if count == 1000:
return True
else:
return False
def start():
# MAIN MENU
print ("start")
count = 0
while True:
count += 1
if check(count) == True:
Game()
def Game():
count = 0
print("Game")
while True:
# GAMELOOP
count += 1
if check(count) == True:
restart()
def restart():
count = 0
print ("Restart")
while True:
# RESTART
count += 1
if check(count) == True:
start()
if first == True:
first = False
start()
However, whenever I run this, python ultimately crashes with:
RecursionError: maximum recursion depth exceeded while calling a Python object
I'm not sure about how to get around this. I was hoping that someone could suggest another method to do the same thing or something that I've done wrong.
The actual restart and menu functions:
def rest(): #Main Menu
while True:
pygame.display.update()
menu.draw()
if pygame.mouse.get_pressed()[0]:
x, y = pygame.mouse.get_pos()
if x >= 255 and x <= 355 and y >= 400 and y <= 450:
game()
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
def game(): #Gameloop
while True:
if bottom.time == 0 or bottom.time < 0 :
game_screen.fill((0, 0, 0))
restart()
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
def restart():
while True:
pygame.display.update()
menu.restart()
if pygame.mouse.get_pressed()[0]:
x, y = pygame.mouse.get_pos()
if x >= 255 and x <= 355 and y >= 400 and y <= 450:
game_screen.fill((0, 0, 0))
rest()
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
Do you have a sequence of functions calling each other creating a indirect recursion.
start() counts to 1000 and then calls Game(), which counts to 1000 again and then calls restart(), whichs also counts to 1000 an then calls start(), closing the recursion loop. Once you do not have a stop condition, your recursion goes until reach the depth limit of python's recursion stack, then you get the exception.
As solution, you need to create a stop condition inside of one those functions.
Edit
I don't know pygame exactly but, as far as I get from the additional code, to the for in rest() function (which checks the events) be executed, it needs that the game() function returns, which are never happening. You can test this hyphotesis printing something just in the line before the for line. Same applies to the restart function.

How do i stop a timer when i set a game to paused

I have created this game which gives you a time limit to kill as many as the targets as you can. Below is the part of the code which pauses and unpauses the game. The problem I am having is that when I pause the game the timer which sets the time limit is still counting. How do I get this to stop?
paused = False
def button(msg, msg_two, x, y, w, h, i, a, fontsize, action=None):
global paused
mouse = pygame.mouse.get_pos()
click = pygame.mouse.get_pressed()
if (x < mouse[0] < (x+450)) and (y < mouse[1]<(y+100)):
pygame.draw.rect(gameDisplay, a, [x, y, w, h])
largeText = pygame.font.Font('freesansbold.ttf',fontsize)
TextSurf, TextRect = text_objects(msg, largeText, green)
TextRect.center = ((x+(w/2)),(y+(h/2)))
gameDisplay.blit(TextSurf, TextRect)
if click[0] == 1 and action != None:
if action == "play":
gameloop()
elif action == "quit":
game_quit()
elif action == "pause":
paused = True
pause()
elif action == "unpause":
unpause()
else:
pygame.draw.rect(gameDisplay, i, [x, y, w, h])
largeText = pygame.font.Font('freesansbold.ttf',fontsize)
TextSurf, TextRect = text_objects(msg_two, largeText, green)
TextRect.center = ((x+(w/2)),(y+(h/2)))
gameDisplay.blit(TextSurf, TextRect)
def game_quit():
pygame.quit()
quit()
def unpause():
global paused
paused = False
def pause():
pygame.mouse.set_visible(1)
button_x = (display_width/4)-150
button_y = display_height/1.5
button_width = 450
button_height = 100
while paused:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
quit()
gameDisplay.fill(white)
largeText = pygame.font.Font('freesansbold.ttf',72)
TextSurf, TextRect = text_objects('paused', largeText, red)
TextRect.center = ((display_width/2),(display_height/3))
gameDisplay.blit(TextSurf, TextRect)
mouse = pygame.mouse.get_pos()
button("let's go", "carry on", button_x, button_y, button_width, button_height, blue, light_blue, 70, "unpause")
button("Bye then :(", "Leaving?", button_x+600, button_y, button_width, button_height, red, light_red, 70, "quit")
pygame.display.update()
clock.tick(15)
def gameloop():
gameExit = False
start = time.time()
elapsed = 0
while not gameExit and elapsed < 30:
button("Stop", "Stop", 1550, 0, 50, 50, red, light_red, 15, "pause")
elapsed = time.time() - start - (enemy_kills/2)
gameloop()
def game_intro():
pygame.mouse.set_visible(1)
button_x = (display_width/4)-150
button_y = display_height/1.5
button_width = 450
button_height = 100
intro = True
while intro:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
quit()
gameDisplay.fill(white)
largeText = pygame.font.Font('freesansbold.ttf',72)
TextSurf, TextRect = text_objects('Shoot Hitler and not trump', largeText, red)
TextRect.center = ((display_width/2),(display_height/3))
gameDisplay.blit(TextSurf, TextRect)
mouse = pygame.mouse.get_pos()
button("let's go", "wanna play?", button_x, button_y, button_width, button_height, blue, light_blue, 70, "play")
button("Bye then :(", "wanna quit?", button_x+600, button_y, button_width, button_height, red, light_red, 70, "quit")
pygame.display.update()
clock.tick(15)
game_intro()
I apologize if I have missed out important parts of the code. Inform me if I have
There are other strange things in this program that makes it hard to understand it enough to give you a proper answer.
For example, the fact that you call back gameloop() at the end of the gameloop function - it creates a recursive call that should no t be recursive at all.
Anyway, what happens is that you should move most of these functions inside a class - so that some of the state variables like paused can be an attribute of that class. Instances of the class are meant to be game plays.
In that case, your "start" variable would be made an attribute as well, so that it could be modified from within the unpause method.
You then could count the amount of paused time, and add that amount to the "start" tick count .
Or you can just make start a global variable and go on with your current pattern. Since I am not re-organizng all of your code just to answer the question, keeping the things more or less as they are would require you to do:
def gameloop():
global start
...
def pause():
global pause_start
pause_start = time.time()
...
def unpause():
global start, pause_start
pause_duration = time.time() - pause_start
start += pause_duration
...

Making button function run once

I can't work out to make the button function run once when clicked. Whenever the mouse button is down it runs through the function loop until released. I only want to run through the loop once per click. Here is the code:
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(window, ac, (x, y, w, h))
#if event.type == pygame.MOUSEBUTTONDOWN:
if click[0] == 1 and action != None:
pygame.draw.rect(window, lightgrey, (x, y, w, h))
if action == "undo":
print("hey")
if action == "reset":
for row in range(6):
for column in range(7):
board[row][column] = 0
elif action == "quit":
pygame.quit()
quit()
else:
pygame.draw.rect(window, ic, (x, y, w, h))
print("hey") prints roughly 3 times if you click as fast as you can as it is running through the loop repetitively.
pygame.mouse.get_pressed() will return True for as long as the mouse is held down. Assuming this checks every frame, it's going to return 1 or True for multiple frames, thus causing your print statement to occur multiple times.
if event.type == pygame.MOUSEBUTTONDOWN:
This line you commented out is the one you should be using. It only activates when the mouse is actually clicked, but not while it is being held down. Note that for this to work, you must have a loop like for event in pygame.event.get() before this line.
So what you basically need to do is remove click = pygame.mouse.get_pressed() and replace if click[0] == 1 with the line you commented out. Hopefully this works :)

How can I add a delay to a mouse click in pygame?

I'm attempting to make a store in my game, but am having issues with pygame.mouse.get_pressed(). When the user clicks on a button, the program thinks that they clicked on it more than once, therefore using more credits than the user intended. I would like to add a delay so that the game doesn't do this. I would like 1 click to represent them buying one object. I have tried reducing the framerate for that window, but the result is the same. Here is the code I currently have.
This is where all the mouse action occurs.
def button(x, y, w, h, ic, ac, action = None):
global paused
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(gameDisplay, ac, (x, y, w, h))
if click[0] == 1 and action == Game:
Game()
if click[0] == 1 and action == quitgame:
sys.exit()
if click[0] == 1 and action == None:
paused = False
if click[0] == 1 and action == StartScreen:
save()
StartScreen()
if click[0] == 1 and action == LootScreen:
LootScreen()
if click[0] == 1 and action == open_common_drop:
open_common_drop()
if click[0] == 1 and action == open_rare_drop:
open_rare_drop()
else:
pygame.draw.rect(gameDisplay, ic, (x, y, w, h))
This is where the loot store is currently at.
def LootScreen():
global current_drops
loot = True
while loot:
for event in pygame.event.get():
if event.type == pygame.QUIT:
save()
pygame.quit()
sys.exit()
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_t:
open_common_drop()
elif event.key == pygame.K_y:
open_rare_drop()
if event.key == pygame.K_ESCAPE:
StartScreen()
gameDisplay.fill(gray)
title('Loot Chests!')
button(400, 150, 260, 50, blue, bright_blue, open_common_drop)
button(695, 150, 260, 50, red, bright_red, open_rare_drop)
button(display_width * 0.42, display_height / 1.15, 255, 50, red, bright_red, StartScreen)
game_display_text('Open Common Chest (T)', 407, 165)
game_display_text('Open Rare Chest (Y)', 725, 165)
game_display_text('You Got: %s' % current_drops, 50, display_height / 2)
game_display_text('Credits: %.2f' % player_loot_data['credit_count'], 15, 15)
game_display_text('Main Menu', display_width * 0.47, display_height / 1.13)
game_display_text('Janus\': %s' % player_loot_data['loot_data_one'] , 950, 500)
game_display_text('Peace of Minds: %s' % player_loot_data['loot_data_two'], 950, 535)
pygame.display.update()
clock.tick(30)
You will need to use a bool switch in your button function. Here I re-worked the function in a way that should work.
def button(x, y, w, h, ic, ac, action = None, held):
global paused
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(gameDisplay, ac, (x, y, w, h))
if click[0] == 1:
if held == False:
if action == Game:
Game()
elif action == quitgame:
sys.exit()
elif action == None:
paused = False
elif action == StartScreen:
save()
StartScreen()
elif action == LootScreen:
LootScreen()
elif action == open_common_drop:
open_common_drop()
elif action == open_rare_drop:
open_rare_drop()
held = True
else:
held = False
else:
pygame.draw.rect(gameDisplay, ic, (x, y, w, h))
return held
As you see, there is a new variable held added to the function to process whether the button is held or not. Held is taken and returned here, so it won't reset every time the function is called.
With that down, let me show you why I wrote it that way, and just how this logic works at its core.
import pygame
#
display = pygame.display.set_mode((800,600))
clock = pygame.time.Clock()
#
held = False # Variable to handle if mouse button is held
#
RUNNING = True
while RUNNING:
clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
RUNNING = False
#
button = pygame.mouse.get_pressed() # Get mouse state
if button[0]: # Check if left mouse button is pressed
if held == False: # Check if button is held down
print(True) # If button is not held down, print true
held = True # Set held eqaual to true for next iteration
else: # If left mouse button is not pressed
held = False # held is set to false, alowing event to happen again
#
display.fill((0,0,0))
#
pygame.display.flip()
#
pygame.quit()
The above is a very simple stand alone program that also implements the held variable to monitor if the mouse button is held. In this program, the variable held is first declared as False as the mouse is not being pressed. Then, within the main loop, pygame.mouse.get_pressed() is called to get the mouse input, and is immediately followed by a check for the left mouse button. If the left mouse button is pressed, a check for held will happen. If it is false, the program will print True. held is then set to True for the next iteration. The else: statement will fire if the left mouse button is not held down and reset held to its default False state.
I would suggest a timed bool switch. Here's an example that should help out.
import time
boolswitch = False
timer = 0.0
clock = time.time()
x = time
while True:
if timer > 0.25:
boolswitch = True
timer = 0.0
else:
x = time.time()
timer += (x-clock)
clock = x
if boolswitch:
click = pygame.mouse.get_pressed()
if click[0] == 1:
boolswitch = False
clock = time.time()

Resources