Whenever i try run my python code with pygame the window opens and then stops responding after a second or any input - python

heres all the code i have currently, it is just a start of a pacman game i am creating all that should be shown is a tab with press space to play
ive looked at previous responses to similar questions and i have what i should need to make it work but it still doesn't... i hope someone can help, thanks.
import os
import sys
import pygame
Start_window_font = 'arial black'
pygame.init()
vector=pygame.math.Vector2
class RPMGame:
def __init__(self):
self.screen = pygame.display.set_mode((450, 600))
self.clock = pygame.time.Clock()
self.running = True
self.state = 'startwindow'
self.load()
self.node_width = 450//25
self.node_height = 600//30
def run(self):
while self.running:
if self.state == 'startwindow':
self.startwindow_events()
self.startwindow_update()
self.startwindow_draw()
if self.state == 'gaming':
self.gaming_events()
self.gaming_update()
self.gaming_draw()
#fps
self.clock.tick(60)
pygame.quit()
sys.exit()
def draw_text(self, words, screen, position, size, colour, font_name, centered = False):
font = pygame.font.SysFont(font_name, size)
text = font.render(words, False, colour)
text_size = text.get_size()
#centering the starting text##
if centered:
position[0] = position[0]-text_size[0]//2
position[1] = position[1]-text_size[1]//2
screen.blit(text, position)
def load(self):
self.gameboard = pygame.image.load('pacmanmaze.png')
self.gameboard = pygame.transform.scale(self.gameboard, (450, 600))
def draw_maze(self):
for x in range(450//self.node_width):
pygame.draw.line(self.screen, 107,107,107, (x*self.node_height, 0), (x*self.node_width, 600))
def startwindow_events(self):
for event in pygame.event.get():
if event.type == pygame.QUIT:
self.running = False
###key used to start game##
if event.type == pygame.KEYDOWN and event.key == pygame.K_SPACE:
self.state= 'gaming'
def startwindow_update(self):
pass
def startwindow_draw(self):
####colour of background##
self.screen.fill(0,0,0)
##start game text (text, position, text size, colour, centering)##
self.draw_text('CLICK TO START GAME', self.screen, (225,300), 16,(255,0,0),
Start_window_font, centered= True)
self.draw_text('HIGH SCORE', self.screen, [5,0] ,(225,300), 16,(255,255,255),
Start_window_font)
pygame.display.update()
def gaming_events(self):
for event in pygame.event.get():
if event.type == pygame.QUIT:
self.running = False
def gaming_update(self):
pass
def gaming_draw(self):
self.screen.blit(self.gameboard, (0,0))
self.draw_maze()
pygame.display.update()
game = RPMGame()
game.run()
**edit
i realised that i forgot to change the variable for one of my states which was needed for the stop part

You have a number of minor issues. When the app started and then immediately stopped, did you not notice that you're getting syntax and runtime errors on the console?
In this line:
self.screen.fill(0,0,0)
fill accepts one parameter, which is an RGB tuple:
self.screen.fill((0,0,0))
You have the same problem in draw_maze:
pygame.draw.line(self.screen, 107,107,107, (x*self.node_height, 0), (x*self.node_width, 600))
... should be ...
pygame.draw.line(self.screen, (107,107,107), (x*self.node_height, 0), (x*self.node_width, 600))
Then, in this call:
self.draw_text('HIGH SCORE', self.screen, [5,0] ,(225,300), 16,(255,255,255),
I don't know what the [5,0] was trying to do, but draw_text isn't expecting that parameter. Just remove it.
Then, in this code:
if centered:
position[0] = position[0]-text_size[0]//2
position[1] = position[1]-text_size[1]//2
screen.blit(text, position)
you pass in position as a tuple. You can't modify a tuple. You need to build a new tuple, or just change to a list:
pos = list(position)
if centered:
pos[0] = pos[0]-text_size[0]//2
pos[1] = pos[1]-text_size[1]//2
screen.blit(text, pos)
With that, your app at least starts for me.

Related

How can I stop pygame looping though a dictionary uncontrollably?

I'm trying to make my first GUI quiz game. But for some reason when I run the code to loop through the questions, it does it too fast and doesn't wait for the user to choose one on the choices before moving onto the next question.
The number of iterations are supposed to be variable as the user should be able to choose the number of questions, so I can't make a function for each questions and instead am trying to loop though a dictionary and displaying it on the screen. But ran onto more problems. Is there any way for the program to wait until the user chooses one of the choices before moving onto the next question.
import pygame
from pygame.locals import *
from gui import Button, FlagButton
from os import path
from q_gen import generate_question
import sys
pygame.init()
WIDTH, HEIGHT = 1000, 600
win = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("World game")
font_path = path.join("fonts", "PartyConfetti.ttf")
small_font = pygame.font.Font(font_path, 50)
centre = ((win.get_width())//2, (win.get_height())//2)
WHITE = (255, 255, 255)
BLACK = ( 0, 0, 0)
PASTEL_BLUE = (189, 242, 255)
button_img = pygame.image.load(path.join("assets", "button.png"))
button_img = pygame.transform.scale(button_img, (300, 100))
arrow = pygame.image.load(path.join("assets", "arrow.png"))
arrow = pygame.transform.scale(arrow, (50, 50))
up = pygame.transform.rotate(arrow, 90)
down = pygame.transform.rotate(arrow, 270)
def game():
questions = generate_question("capital", 5, 1)
pressed = False
running = True
while running:
for q in questions:
win.fill(PASTEL_BLUE)
mouse_pos = pygame.mouse.get_pos()
title = small_font.render(f"{q}. {questions[q][0]}", True, "black")
title_rect = title.get_rect(center=(centre[0], centre[1]-200))
win.blit(title, title_rect)
choice1 = Button(win, button_img, (300, 500), "CHOICE")
choice1.changeColor(mouse_pos)
choice1.update()
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
if event.type == pygame.MOUSEBUTTONDOWN:
if choice1.checkForInput(mouse_pos):
pressed = True
break
pygame.display.update()
if pressed:
continue
pygame.quit()
sys.exit(0)
The question generator and class for the button, if needed, is below.
from info import info # Large dictionary containing info of countries
from random import choice, randint
modes = ("capital", "flag", "currency")
def generate_question(mode, q_no, difficulty):
questions = {}
new_mode = mode
for i in range(1, q_no+1):
reverse = choice((True, False))
country = choice(list(info.keys()))
choices = []
if mode == "mixed":
new_mode = choice(modes)
if new_mode != "flag":
query = info[country][new_mode]
if reverse:
question = f"Which country is {query} the {new_mode} of?"
answer = country
while len(choices)<=(difficulty-1)*2:
rand_choice = choice(list(info.keys()))
if rand_choice != answer and rand_choice not in choices:
choices.append(rand_choice)
else:
question = f"What is the {new_mode} of {country}?"
answer = query
while len(choices)<=(difficulty-1)*2:
rand_country = choice(list(info.keys()))
rand_choice = info[rand_country][new_mode]
if rand_choice != answer and rand_choice not in choices:
choices.append(rand_choice)
choices.insert(randint(0, len(choices)), answer)
questions[i] = (question, country, new_mode, answer, choices)
else:
question = f"Which one is the flag of {country}?"
answer = f"{country}.png"
return questions
import os
import pygame
pygame.init()
class Button():
def __init__(self, screen, image, pos, text_input, font=os.path.join("fonts", "PartyConfetti.ttf"), text_size=50):
self.screen = screen
self.image = image
self.x_pos = pos[0]
self.y_pos = pos[1]
self.font = pygame.font.Font(font, text_size)
self.rect = self.image.get_rect(center=(self.x_pos, self.y_pos))
self.text_input = text_input
self.text = self.font.render(self.text_input, True, "black")
self.text_rect = self.text.get_rect(center=(self.x_pos, self.y_pos))
def update(self):
self.screen.blit(self.image, self.rect)
self.screen.blit(self.text, self.text_rect)
def checkForInput(self, position):
if position[0] in range(self.rect.left, self.rect.right) and position[1] in range(self.rect.top, self.rect.bottom):
return True
return False
def changeColor(self, position):
if position[0] in range(self.rect.left, self.rect.right) and position[1] in range(self.rect.top, self.rect.bottom):
self.text = self.font.render(self.text_input, True, "white")
else:
self.text = self.font.render(self.text_input, True, "black")
class FlagButton(Button):
def __init__(self, screen, image, pos, text_input, flag_image, font=os.path.join("fonts", "PartyConfetti.ttf"), text_size=50):
super().__init__(screen, image, pos, text_input, font, text_size)
self.flag_image = flag_image
self.flag_image_rect = self.flag_image.get_rect(center=(self.x_pos, self.y_pos))
def update(self):
self.screen.blit(self.image, self.rect)
self.screen.blit(self.flag_image, self.flag_image_rect)
The problem is here
if pressed:
continue
continue does not do what you think it does, it only skips the rest of the current iteration and continues with the loop.
Instead, you could use a while loop to keep looping over the click check until a button is pressed.
import pygame
from pygame.locals import *
from gui import Button, FlagButton
from os import path
from q_gen import generate_question
import sys
pygame.init()
WIDTH, HEIGHT = 1000, 600
win = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("World game")
font_path = path.join("fonts", "PartyConfetti.ttf")
small_font = pygame.font.Font(font_path, 50)
centre = ((win.get_width())//2, (win.get_height())//2)
WHITE = (255, 255, 255)
BLACK = ( 0, 0, 0)
PASTEL_BLUE = (189, 242, 255)
button_img = pygame.image.load(path.join("assets", "button.png"))
button_img = pygame.transform.scale(button_img, (300, 100))
arrow = pygame.image.load(path.join("assets", "arrow.png"))
arrow = pygame.transform.scale(arrow, (50, 50))
up = pygame.transform.rotate(arrow, 90)
down = pygame.transform.rotate(arrow, 270)
def game():
questions = generate_question("capital", 5, 1)
pressed = False
running = True
while running:
for q in questions:
win.fill(PASTEL_BLUE)
title = small_font.render(f"{q}. {questions[q][0]}", True, "black")
title_rect = title.get_rect(center=(centre[0], centre[1]-200))
win.blit(title, title_rect)
choice1 = Button(win, button_img, (300, 500), "CHOICE")
while not pressed and running:
mouse_pos = pygame.mouse.get_pos()
choice1.changeColor(mouse_pos)
choice1.update()
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
if event.type == pygame.MOUSEBUTTONDOWN:
if choice1.checkForInput(mouse_pos):
pressed = True
pygame.display.update()
if not running:
break
pygame.quit()
sys.exit(0)
There are probably still some problems with this code but hopefully this helps fix your problem.

pygame.event.set_grab remains turned on after exception / crash and makes program "unkillable"

I am creating a game with pygame and I am using pygame.event.set_grab(True) to keep cursor locked in the game window (it keeps all keyboard input in the window also), but when the program crashes (either because of syntax error or some other exception) the set_grab stays turned on and it is impossible to turn off the program afterwards. (fortunately I am using Linux so i can access console that overrides everything so i can turn it off manually)
I am wondering if it is possible to make some error handling which will turn it off (or kills the program) or if there is a better way to keep just mouse inputs in the window. (so it is possible to alt+f4)
import pygame
pygame.init()
size = (600, 700)
monitor=pygame.display.Info()
screen = pygame.display.set_mode(size)#pygame.FULLSCREEN)
pygame.display.set_caption("Meteor Galaxy 3")
done = False
clock = pygame.time.Clock()
pygame.mouse.set_visible(0)
pygame.event.set_grab(True) #this is turned on with the initialization
#(doesnt have to be) of the game
When the game crashes it transforms into the usual black window with the exception you cant do anything.
Thank you.
Edit:
The full code:
#coding: utf-8
import pygame
import random
random.randint(0,2)
#TODO:
#Vymyslieť systém na čakanie framov
#Upraviť zmenu rýchlosti hráčovho náboja
##Pygame init
pygame.init()
size = (600, 700)
possible_sizes=[[600,700],[900,1050],[1200,1400],[1800,2100]] #ASI 1200,1400 obrázky a potom downscale?? (ak vôbec zmena rozlisenia..)
monitor=pygame.display.Info()
screen = pygame.display.set_mode(size)#pygame.FULLSCREEN)
pygame.display.set_caption("Meteor Galaxy 3")
done = False
clock = pygame.time.Clock()
pygame.mouse.set_visible(0)
pygame.event.set_grab(True)
#<VARIABLES>
##<COLORS>
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
GREEN = (0, 255, 0)
RED = (255, 0, 0)
##</COLORS>
##<IMAGES> #"../meteo_data/img/"
bg1=[pygame.image.load("../meteo_data/img/bg1.png"),700-5770,True]
#backgrounds.append([pygame.image.load("img/bg2.png"),[600,1924]])
#backgrounds.append([pygame.image.load("img/bg3.png"),[600,1924]])
img_crosshair=pygame.image.load("../meteo_data/img/crosshair.png").convert()
#Ships
img_player=pygame.image.load("../meteo_data/img/ships/player.png").convert()
img_enemy1=pygame.image.load("../meteo_data/img/ships/enemy1.png").convert()
#Bullets
img_b1=pygame.image.load("../meteo_data/img/bullets/bullet.png").convert()
img_player.set_colorkey(BLACK)
img_enemy1.set_colorkey(BLACK)
img_crosshair.set_colorkey(BLACK)
##</IMAGES>
##<SOUNDS>
##</SOUNDS>
menu_game=1 #Nula pre menu , jedna pre hru?? , medzi nula a jedna ostatné??
esc_menu=False
fire=False
level=-1
level_ended=True
## def=0 def=0
##<BULLET TYPES> #[IMAGE,DAMAGE,SPEED,PENETRATION,relX,relY] /relX a relY vziať z relX a relY lode.
B_default=[img_b1,3,4,False,0,0]
##</BULLET TYPES>
##<SHIP TYPES> #[IMAGE,HEALTH,SPEED,RELOAD,X,Y,relX,relY,BULLET_TYPE] /relX a relY je pre bullet
##</SHIP TYPES>
##<LEVELS>
level1=[bg1]
master_level=[level1]
##</LEVELS>
#</VARIABLES>
#<FUNCTIONS>
##<SPRITES>
class bullet(pygame.sprite.Sprite):
def __init__(self,bullet_type):
pygame.sprite.Sprite.__init__(self)
self.image=bullet_type[0]
self.dmg=bullet_type[1]
self.speed=bullet_type[2]
self.penetration=bullet_type[3] ##Prestrelí viac ENIMÁKOV ? True/False
self.rect = self.image.get_rect()
self.rect.x=bullet_type[4] ##Vypočítať pri vystrelení (ship pos -/+ ship.bullet_x(y)) (pre každý typ lode zvlášť)
self.rect.y=bullet_type[5]
self.image.set_colorkey(BLACK)
class ship(pygame.sprite.Sprite):
def __init__(self,ship_type):
pygame.sprite.Sprite.__init__(self)
self.image=ship_type[0]
self.hp=ship_type[1]
self.speed=ship_type[2] ##0 Pre hráča
self.reload=ship_type[3] ##Rýchlosť streľby (koľko framov čakať) 0 = každý frame bullet
self.rect=self.image.get_rect()
self.rect.x=ship_type[4]
self.rect.y=ship_type[5]
self.bullet_x=ship_type[6]
self.bullet_y=ship_type[7]
self.b_type=ship_type[8]
self.image.set_colorkey(BLACK)
class barrier(pygame.sprite.Sprite):
def __init__(self,coord):
pygame.sprite.Sprite.__init__(self)
self.image=pygame.Surface([700,40])
self.image.fill(WHITE)
self.rect=self.image.get_rect()
self.rect.x=coord[0]
self.rect.y=coord[1]
bullets=pygame.sprite.Group()
ships=pygame.sprite.Group()
barriers=pygame.sprite.Group()
player_b_type=B_default
player_b_type[2]=player_b_type[2]*(-1)
player=ship([img_player,100,0,10,279,650,15,3,player_b_type]) ##PLAYER SHIP
wait=player.reload
barrier_top=barrier([-50,-400])
barrier_bottom=barrier([-50,900])
barriers.add(barrier_top)
barriers.add(barrier_bottom)
##</SPRITES>
#</FUNCTIONS>
while not done:
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
elif event.type == pygame.MOUSEBUTTONDOWN:
if event.button==1:
fire=True
elif event.type == pygame.MOUSEBUTTONUP:
if event.button==1:
fire=False
elif event.type==pygame.KEYDOWN:
if event.key==pygame.K_ESCAPE:
if not esc_menu:
esc_menu=True
pygame.event.set_grab(False)
else:
esc_menu=False
pygame.event.set_grab(True)
coord=pygame.mouse.get_pos()
if menu_game==0:
screen.fill(WHITE) #BG
elif menu_game==1:
#GAME LOGIC
if level_ended:
level=level+1
bg1_y=master_level[level][0][1]
level_ended=False
bg1_y=bg1_y+2
player.rect.x=coord[0]-20
pygame.sprite.groupcollide(barriers,bullets,False,True)
pygame.sprite.groupcollide(barriers,ships,False,True)
if fire:
if wait==0:
bullet_modified=player.b_type
bullet_modified[4]=player.rect.x+player.bullet_x
bullet_modified[5]=player.rect.y+player.bullet_y
b=bullet(bullet_modified)
bullets.add(b)
wait=player.reload
else:
wait=wait-1
#RENDERING
screen.fill(BLACK)
screen.blit(master_level[level][0][0],[0,bg1_y]) #BG
screen.blit(player.image,player.rect)
for naboj in bullets:
screen.blit(naboj.image,naboj.rect)
naboj.rect.y=naboj.rect.y+naboj.speed
screen.blit(img_crosshair,[coord[0]-10,coord[1]-10])
pygame.display.flip() #FRAMY
clock.tick(60)
pygame.quit()
#NOTES:
# Dlzka lvl sa urci vyskou bg (5760 px == 48 sec - 1. lvl)
#189 -
#
So, the problem there is that if some exception happens in the middle of that code, pygame.quit() is never called.
All you have to do is to set a try ... fynaly block around your Pygame code, and put the pygame.quit() call on the finally block.
For that I suggest some reformatting which will also improve the modularity of your code, which is to enclose all that code you put on the module level inside a function.
So:
...
def main():
done = False
while not done:
...
for naboj in bullets:
...
screen.blit(img_crosshair,[coord[0]-10,coord[1]-10])
pygame.display.flip() #FRAMY
clock.tick(60)
try:
main()
finally:
pygame.quit()
In this way, any unhandled exception within the code run in main (or in any other function called from it), as well as well behaved main-loop termination, will immediately run the code within the finally block: Pygame is shut down, along with its event handling, and you get the error traceback on the terminal enabling you to fix the code.
update the finally hint is also essential for anyone making a game with pygame that uses fullscreen, regardless of event_grab.

How to remove sprites out of group, outside of the class: Pygame

#Importing Modules
import pygame as pg
import sys
import random
#All pygame stuff under here
pg.init()
#Font definitions
backFont = pg.font.SysFont("monospace",40)
titleFont = pg.font.SysFont("garamond", 100)
cipherFont = pg.font.SysFont("garamond", 50)
buttonFont = pg.font.SysFont("garamond", 25)
bigFont = pg.font.SysFont("garamond",100)
Font = pg.font.SysFont(None,32)
inputFont = pg.font.SysFont('consola', 35)
errorFont = pg.font.SysFont('tahoma',20)
diagramFont = pg.font.SysFont('courier new',25)
#Colour definitions
BackGray = pg.Color('gray60')
screenGray = pg.Color('gray80')
buttonGray2 = pg.Color('gray50')
textColour = pg.Color('navy')
#Screen size set
screen = pg.display.set_mode((400, 400))
clock = pg.time.Clock()
class Button(pg.sprite.Sprite):
def __init__(self, text, x, y, width, height, colour, enabled):
super().__init__()
self.image = pg.Surface((width, height))
self.image.fill(colour)
self.rect = self.image.get_rect()
txt = buttonFont.render(text, True, textColour)
txtRect = txt.get_rect(center = self.rect.center)
self.image.blit(txt, txtRect)
self.rect.topleft = x, y
self.enabled = enabled
def isPressed(self, event):
if self.enabled == True:
if event.type == pg.MOUSEBUTTONDOWN:
if self.rect.collidepoint(event.pos):
return True
return False
def Function():
background = pg.Surface(screen.get_size())
background.fill(screenGray)
Button1 = Button('Encrypt',100,100,125,50,buttonGray2,True)
Button2 = Button('Decrypt',100,200,125,50,buttonGray2,True)
buttonsGroup = pg.sprite.Group(Button1,Button2)
ACTIONPRINT = False
Active1 = False
while True:
for event in pg.event.get():
if event.type == pg.QUIT:
pg.quit()
sys.exit()
elif Button1.isPressed(event):
print("1")
Active1 = True
elif Button2.isPressed(event):
print("2")
if Active1 == True:
ACTIONPRINT = True
buttonsGroup = pg.sprite.Sprite.remove(Button2)
screen.blit(background,(0,0))
buttonsGroup.draw(screen)
pg.display.flip()
clock.tick(60)
Function()
Above is the code for the class of Buttons, and a simple function that runs two buttons. What I'd like to do is remove one of the buttons, when one is pressed, which are set as sprites. When the button is removed from the group, I believe it should disappear from the screen, after one is pressed.
The above code at the minute returns an error saying that there is an AttributeError: 'NoneType' object has no attribute 'draw'. However, in another program, when it did work, it said that in the Sprite.remove method, the parameters must be a sequence and not a button - what does this mean?
I have looked online, and all the removing sprites examples are inside a class. Does that mean that the only way for this to work is by changing the class?
Or can sprites still be removed from outside the class, and if so how is it done?
Any other methods are welcome to
Thanks in advance!
sprite.Group.remove doesn't return anything, it removes the sprite from the group that calls it, so instead of:
buttonsGroup = pg.sprite.Sprite.remove(Button2)
Try:
buttonsGroup.remove(Button2)

My Python program crashes when I press the exit button. It works fine otherwise. I am using Pygame module. The code is attached below

I am reading data from a file. Basically, those are coordinates where I want my ball to appear after every iteration. The code is working fine except for the fact that the output window 'Trial 1' crashes as soon as I press the exit button. This problem wasn't there before I added for t in range (np.size(T)):; however I require that. Please suggest some possible changes in the code to get rid of the problem.
import numpy as np
import pygame
pygame.init()
T = np.loadtxt('xy_shm1.txt', usecols=range(0,1))
Xcor = np.loadtxt('xy_shm1.txt', usecols=range(1,2))
Ycor = np.loadtxt('xy_shm1.txt', usecols=range(2,3))
clock = pygame.time.Clock()
background_colour = (255,255,255)
(width, height) = (800, 800)
class Particle():
def __init__(self, xy, size):
self.x, self.y = xy
self.size = size
self.colour = (0, 0, 255)
self.thickness = 1
def display(self):
pygame.draw.circle(screen, self.colour, (int(self.x), int(self.y)), self.size, self.thickness)
def move(self):
self.x = Xcor[t] + 400
self.y = Ycor[t] + 400
screen = pygame.display.set_mode((width, height))
pygame.display.set_caption('Trial 1')
number_of_particles = 1
my_particles = []
for n in range(number_of_particles):
size = 5
x = Xcor[0] + 400
y = Ycor[0] + 400
particle = Particle((x, y), size)
my_particles.append(particle)
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
for t in range(np.size(T)):
screen.fill(background_colour)
for particle in my_particles:
particle.move()
particle.display()
pygame.display.flip()
clock.tick(60)
pygame.quit()
The main problem is that you are trying to draw multiple frames within a frame. The frame loop should look like this:
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
# Draw one frame here
clock.tick(60) # If the game runs faster than 60fps, wait here
Note that in each iteration of the while loop, only one frame is drawn.
In your current code however, you start the loop, check the events once,
and then you draw a frame for each item in your list, without checking the events again.
This most likely causes the QUIT event to be missed, and the operating system intervening because the game seemingly is not responding.
In general, your code is quite messy. I suggest that you read some tutorials on pygame, or you will run into all sorts of similar problems. See for example: http://programarcadegames.com/python_examples/f.php?file=bouncing_rectangle.py

text not showing up dynamically, pygame

The code below is supposed to create a green button that makes a score text appear. unfortunately the button does nothing, and the only way I've managed to get it to work is by putting the function call for makeText in the while loop instead of in the clickButton function, but if I do that it's no longer dynamic. Can someone explain why the text isn't showing up when I press the button and fix my code so it does show up?
import pygame
import sys
#game stuff
pygame.init()
screen = pygame.display.set_mode((640, 480),0,32)
clock = pygame.time.Clock()
#functions
def makeText(title,text,posx,posy):
font=pygame.font.Font(None,30)
scoretext=font.render(str(title)+ ": " +str(text), 1,(0,0,0))
screen.blit(scoretext, (posx, posy))
def clickButton(name,x,y,width,height):
if x + width > cur[0] > x and y + height > cur[1] > y:
if click == (1,0,0):
makeText("score",300,100,10)
#objects
button1 = pygame.Rect((0,0), (32,32))
while True:
screen.fill((255,255,255))
screen.fill((55,155,0), button1)
#update display
pygame.display.update()
clock.tick(60)
#event handling
for event in pygame.event.get():
if event.type == pygame.QUIT:
quit()
elif event.type == pygame.MOUSEBUTTONDOWN:
cur = event.pos
click = pygame.mouse.get_pressed()
clickButton("button1",button1.left,button1.top,button1.width,button1.height)
The problem is that once you created the text, your main loop keeps going and calls screen.fill, overdrawing the text even before pygame.display.update() is called.
You could change it to:
...
def clickButton(name,x,y,width,height):
print x + width > cur[0] > x and y + height > cur[1] > y
if x + width > cur[0] > x and y + height > cur[1] > y:
if click == (1,0,0):
makeText("score",300,100,10)
#objects
button1 = pygame.Rect((0,0), (32,32))
while True:
screen.fill((255,255,255))
screen.fill((55,155,0), button1)
#event handling
for event in pygame.event.get():
if event.type == pygame.QUIT:
quit()
elif event.type == pygame.MOUSEBUTTONDOWN:
cur = event.pos
click = pygame.mouse.get_pressed()
clickButton("button1",button1.left,button1.top,button1.width,button1.height)
...
so the text is created after filling the screen with the background color and before pygame.display.update() is called, but that does not solve the problem of the screen being filled again the next iteration of the while loop.
So the solution is to keep track of the fact that the button was pressed, a.k.a. keeping track of a state.
Here's an example of a different approach, using classes for the buttons and a dict for the global state (so you don't need global variables, which should you avoid most of the time, because it can get very confusing fast if your game starts becoming more complex).
Click the first button to show or hide the score, and click the second button to change the background color and earn 100 points.
See how easy it becomes to create new buttons; it's just adding a simple function.
import pygame
import sys
import random
pygame.init()
screen = pygame.display.set_mode((640, 480),0,32)
clock = pygame.time.Clock()
# create font only once
font = pygame.font.Font(None,30)
# it's always a good idea to cache all text surfaces, since calling 'Font.render' is
# an expensive function. You'll start to notice once your game becomes more complex
# and uses more text. Also, use python naming conventions
text_cache = {}
def make_text(title, text):
key = "{title}: {text}".format(title=title, text=text)
if not key in text_cache:
text = font.render(key, 1,(0,0,0))
text_cache[key] = text
return text
else:
return text_cache[key]
# we use the 'Sprite' class because that makes drawing easy
class Button(pygame.sprite.Sprite):
def __init__(self, rect, color, on_click):
pygame.sprite.Sprite.__init__(self)
self.rect = rect
self.image = pygame.Surface((rect.w, rect.h))
self.image.fill(color)
self.on_click = on_click
# this happens when the first button is pressed
def toggle_score_handler(state):
state['show_score'] = not state['show_score']
# this happens when the second button is pressed
def toggle_backcolor_handler(state):
state['backcolor'] = random.choice(pygame.color.THECOLORS.values())
state['score'] += 100
# here we create the buttons and keep them in a 'Group'
buttons = pygame.sprite.Group(Button(pygame.Rect(30, 30, 32, 32), (55, 155 ,0), toggle_score_handler),
Button(pygame.Rect(250, 250, 32, 32), (155, 0, 55), toggle_backcolor_handler))
# here's our game state. In a real
# game you probably have a custom class
state = {'show_score': False,
'score': 0,
'backcolor': pygame.color.Color('White')}
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
quit()
# you can check for the first mouse button with 'event.button == 1'
elif event.type == pygame.MOUSEBUTTONDOWN and event.button == 1:
# to check if the mouse is inside the button, you
# can simple use the 'Rect.collidepoint' function
for button in (b for b in buttons if b.rect.collidepoint(event.pos)):
button.on_click(state)
screen.fill(state['backcolor'])
# draw all buttons by simple calling 'Group.draw'
buttons.draw(screen)
if state['show_score']:
screen.blit(make_text("score", state['score']), (100, 30))
pygame.display.update()
clock.tick(60)
You are checking the value of "click" in the clickButton function, but I don't see click defined anywhere that clickButton would have access to it.
Perhaps you should pass click as an argument in the clickButton function, which would then possibly make the if condition true?

Categories