PyGame Sprites Occasionally Not Drawing - python

I am trying to make a basic card game using PyGame. I am currently just trying to draw a single card to the screen. The weird thing is, occasionally it will draw and occasionally it won't. Below is my code:
import pygame
from pygame.locals import *
from socket import *
import sys
import os
import math
import getopt
import random
def load_png(name) :
# Loads an image and returns the image object
fullname = os.path.join('/home/edge/Downloads/Playing Cards/PNG-cards-1.3', name)
image = pygame.image.load(fullname)
if image.get_alpha is None :
image = image.convert()
else :
image = image.convert_alpha()
return image, image.get_rect()
class Card(pygame.sprite.Sprite) :
def __init__(self, suit, val) :
pygame.sprite.Sprite.__init__(self)
self.suit = suit
self.val = val
self.image, self.rect = load_png(val + '_of_' + suit + '.png')
screen = pygame.display.get_surface()
self.area = screen.get_rect()
#self.rect.inflate(-.5, -.5)
def main() :
pygame.init()
pygame.display.set_caption('Card Game Thingy')
screen = pygame.display.set_mode( (1250, 650) )
background = pygame.Surface(screen.get_size() )
background = background.convert()
background.fill( (0, 0, 0) )
x = Card('diamonds', '2')
cardSprite = pygame.sprite.RenderPlain(x)
screen.blit(background, (0, 0) )
cardSprite.draw(screen)
clock = pygame.time.Clock()
# Game Loop
while True :
clock.tick(60)
for event in pygame.event.get() :
if event.type == QUIT :
return
elif event.type == KEYDOWN :
if event.key == K_DOWN :
return
cardSprite.draw(screen)
if __name__ == '__main__' :
main()

You have to update the display in every loop in the while loop with
pygame.display.update()

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.

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

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.

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)

Pygame update bug

I am creating my game on python with pygame but I have got a bug while updating screen :
my older character position are displayed
I am using a pattern to fill my screen and I refresh it each time and this before my character so it shouldn't be laggy, this is my code :
import pygame, sys
from pygame.locals import *
pygame.init()
#Open Pygame window
taille_fenetre = [960, 640]
fenetre = pygame.display.set_mode(taille_fenetre)
pygame.key.set_repeat(400, 30)
clock = pygame.time.Clock()
#Chargement et collage du fond
fond = pygame.image.load("brock.png").convert()
for a in range(taille_fenetre[0] // fond.get_width() + 1):
for i in range(taille_fenetre[1] // fond.get_height() + 1):
fenetre.blit(fond, (a*fond.get_width(),i*fond.get_height()))
room_size = (taille_fenetre[0]-64*2,taille_fenetre[1]-64*2)
room = pygame.Surface(room_size)
room.fill((255,0,0))
terre = pygame.image.load("terre.png").convert()
for a in range(room.get_width() // terre.get_width() + 1):
for i in range(room.get_height() // terre.get_height() + 1):
room.blit(terre, (a*terre.get_width(),i*terre.get_height()))
shadow_room = pygame.image.load('shadow_room.png')
def blit_alpha(target, source, location, opacity):
x = location[0]
y = location[1]
temp = pygame.Surface((source.get_width(), source.get_height())).convert()
temp.blit(target, (-x, -y))
temp.blit(source, (0, 0))
temp.set_alpha(opacity)
target.blit(temp, location)
class Personnage:
def __init__(self):
self.imagesrc = "perso.png"
self.image = pygame.image.load(self.imagesrc).convert_alpha()
self.size = self.image.get_size()
self.position = [0,0]
self.vitesse = 5
self.sante = 5
def bouger(self,facteur):
self.position = [self.position[0] - self.vitesse * facteur[0],self.position[1] - self.vitesse * facteur[1]]
def afficher(self,fenetre):
print(self.position)
fenetre.blit(self.image,self.position)
#INFINITE LOOP
continuer = 1
perso = Personnage()
while continuer:
relachex = True
relachey = True
#Re-collage
fenetre.blit(fond, (0,0))
fenetre.blit(room,(64,64))
blit_alpha(room,shadow_room,(0,0),128)
for event in pygame.event.get(): #Attente des événements
if event.type == QUIT:
continuer = 0
pygame.quit()
sys.exit()
keys = pygame.key.get_pressed()
if keys[K_LEFT]:
perso.bouger([1,0])
if keys[K_RIGHT]:
perso.bouger([-1,0])
if keys[K_DOWN]:
perso.bouger([0,-1])
if keys[K_UP]:
perso.bouger([0,1])
#REFRESH
perso.afficher(fenetre)
pygame.display.flip()
clock.tick(60)
you are drawing over the previous images again and again without clearing out the old stuff. You can't tell with the other images because they are static, but it shows with the character. Add:
fenetre.fill((0, 0, 0))
to the top of your game loop to refresh the screen to draw the new locations.

How To Find Out If A .Rect() Has Been Clicked In Pygame

I'm trying to find out if a user has clicked on a .rect(). I've read a bunch of tutorials, but I'm still running into problems. I have two files: One that is the main python file, and one that defines the .rect().
#main.py
import pygame,os,TextBox
from pygame.locals import *
pygame.init()
myTextBox = TextBox.TextBox()
WHITE = (255, 255, 255)
size = (400, 200)
screen = pygame.display.set_mode(size)
done = False
boxes = [myTextBox]
while not done:
for event in pygame.event.get(): # User did something
if event.type == pygame.QUIT: # If user clicked close
done = True
elif event.type == MOUSEMOTION: x,y = event.pos
for box in boxes:
if myTextBox.rect.collidepoint(x,y): print ('yay')
screen.fill(WHITE)
myTextBox.display(screen, 150, 150, 20, 100)
pygame.display.flip()
pygame.quit()
#TextBox.py
import pygame
from pygame.locals import *
class TextBox:
def __init__(self):
self.position_x = 100
self.position_y = 100
self.size_height = 10
self.size_width = 50
self.outline_color = ( 0, 0, 0)
self.inner_color = (255, 255, 255)
def display(self, screen, x, y, height, width):
self.position_x = x
self.position_y = y
self.size_height = height
self.size_width = width
pygame.draw.rect(screen, self.outline_color, Rect((self.position_x - 1, self.position_y - 1, self.size_width + 2, self.size_height + 2)))
pygame.draw.rect(screen, self.inner_color, Rect((self.position_x, self.position_y, self.size_width, self.size_height)))
The error I get is AttributeError: 'TextBox' object has no attribute 'rect'
How do I solve this?
You're TextBox class doesn't have a rect. Add this somewhere at the bottom of the __init__ method of the TextBox class:
self.rect = pygame.Rect(self.position_x,self.position_y,self.size_width,self.size_height)
Do the same in the update method.

Categories