How to completely clear pygame window with button - python-3.x

I made this program:
import pygame
pygame.init()
WIDTH = 600
HEIGHT = 700
RED = (255, 0, 0)
BLACK = (0, 0, 0)
GREY = (211, 211, 211)
GREEN = (0, 255, 0)
win = pygame.display.set_mode((WIDTH, HEIGHT))
win.fill(GREY)
def buttons(buttonx, buttony, buttonw, buttonh, color, msg, size): # left, top, width, height, color, message, font size
pos = pygame.mouse.get_pos()
fontb = pygame.font.SysFont("arial", size)
text = fontb.render(msg, True, BLACK)
outline = pygame.Rect(buttonx - 2, buttony - 2, buttonw + 4, buttonh + 4)
win.fill(BLACK, outline)
button = pygame.Rect(buttonx, buttony, buttonw, buttonh)
win.fill(color, button)
textplace = text.get_rect(center=(buttonx + buttonw/2, buttony + buttonh/2))
win.blit(text, textplace)
if button.collidepoint(pos):
win.fill(GREEN, button)
win.blit(text, textplace)
for event in pygame.event.get():
if event.type == pygame.MOUSEBUTTONDOWN:
pass
# this block of code should make window grey
def main_menu():
font1 = pygame.font.SysFont("arial", 45)
welcoming = font1.render("Welcome!", True, BLACK, GREY)
wRect = welcoming.get_rect(center=(WIDTH / 2, 75))
win.blit(welcoming, wRect)
buttons(100, 150, 400, 100, RED, "Play", 60)
buttons(100, 300, 175, 100, RED, "Options", 40)
buttons(325, 300, 175, 100, RED, "Instructions", 30)
buttons(100, 450, 175, 100, RED, "Leaderboard", 30)# left, top, width, height, color, message
buttons(325, 450, 175, 100, RED, "Quit", 60)
main = True
while main:
main_menu()
for event in pygame.event.get():
if event.type == pygame.QUIT:
main = False
pygame.display.update()
If any of the buttons is pressed, the window should turn completely grey (all buttons and texts should disappear). I have tried to put
win.fill(GREY)
pygame.display.update()
in place of
pass
# this block of code should make window grey
in lines 34 and 35, but it does not work. I have also tried to make some kind of functions that return grey window which is same size as the original window and then blit it on the main menu, but it doesn't work either. Can anyone help?

The code you are trying succeeds in filling the window with grey. However, the blank grey screen then has the menu drawn on top of it immediately by your main loop.
Using an additional global variable, current_screen, it is possible to prevent this from happening. The main loop now checks current_screen to see which screen it should draw before doing so, ensuring that the menu will not be drawn when it is not supposed to be shown.
This following code worked for me, but let me know if you have any problems with it.
import pygame
pygame.init()
# declare final variables
WIDTH = 600
HEIGHT = 700
RED = (255, 0, 0)
BLACK = (0, 0, 0)
GREY = (211, 211, 211)
GREEN = (0, 255, 0)
# create window
win = pygame.display.set_mode((WIDTH, HEIGHT))
win.fill(GREY)
# left, top, width, height, color, message, font size
def buttons(buttonx, buttony, buttonw, buttonh, color, msg, size):
global win, current_screen
pos = pygame.mouse.get_pos()
fontb = pygame.font.SysFont("arial", size)
text = fontb.render(msg, True, BLACK)
# draw button outline and fill
outline = pygame.Rect(buttonx - 2, buttony - 2, buttonw + 4, buttonh + 4)
win.fill(BLACK, outline)
button = pygame.Rect(buttonx, buttony, buttonw, buttonh)
win.fill(color, button)
# draw button text
textplace = text.get_rect(
center=(buttonx + buttonw/2, buttony + buttonh/2))
win.blit(text, textplace)
if button.collidepoint(pos): # button mouse-hover
win.fill(GREEN, button)
win.blit(text, textplace)
for event in pygame.event.get():
if event.type == pygame.MOUSEBUTTONDOWN: # button pressed
current_screen = "blank"
def main_menu():
# draw welcome message
font1 = pygame.font.SysFont("arial", 45)
welcoming = font1.render("Welcome!", True, BLACK, GREY)
wRect = welcoming.get_rect(center=(WIDTH / 2, 75))
win.blit(welcoming, wRect)
# draw buttons
buttons(100, 150, 400, 100, RED, "Play", 60)
buttons(100, 300, 175, 100, RED, "Options", 40)
buttons(325, 300, 175, 100, RED, "Instructions", 30)
# left, top, width, height, color, message
buttons(100, 450, 175, 100, RED, "Leaderboard", 30)
buttons(325, 450, 175, 100, RED, "Quit", 60)
main = True
current_screen = "main menu"
while main:
# draw the current screen
if current_screen == "main menu":
main_menu()
elif current_screen == "blank":
win.fill(GREY)
else:
raise NotImplementedError("There is no behavior defined for the current screen, "+current_screen+", in the main loop!")
# end main loop on window close
for event in pygame.event.get():
if event.type == pygame.QUIT:
main = False
pygame.display.update()

Related

Make a Single Word Within a String Bold

Given the following code (from this answer: How to display text in pygame?):
pygame.font.init() # you have to call this at the start,
# if you want to use this module.
my_font = pygame.font.SysFont('Comic Sans MS', 30)
text_surface = my_font.render('Some Text', False, (0, 0, 0))
Is it possible to make the word 'Text' bold while keeping the word 'Some ' regular?
(without rendering multiple times)
No it is not possible. You must use the "bold" version of the font. "bold" isn't a flag or attribute, it's just a different font. So you need to render the word "Text" with one font and the word "Some" with another font.
You can use pygame.font.match_font() to find the path to a specific font file.
Minimal example:
import pygame
pygame.init()
window = pygame.display.set_mode((400, 200))
clock = pygame.time.Clock()
courer_regular = pygame.font.match_font("Courier", bold = False)
courer_bold = pygame.font.match_font("Courier", bold = True)
font = pygame.font.Font(courer_regular, 50)
font_b = pygame.font.Font(courer_bold, 50)
text1 = font.render("Some ", True, (255, 255, 255))
text2 = font_b.render("Text", True, (255, 255, 255))
run = True
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
window.fill(0)
window.blit(text1, (50, 75))
window.blit(text2, (50 + text1.get_width(), 75))
pygame.display.flip()
clock.tick(60)
pygame.quit()
exit()
With the pygame.freetype modle the text can be rendered with different styles like STYLE_DEFAULT and STYLE_STRONG. However, the text can only be rendered with one style at a time. So you still have to render each word separately:
import pygame
import pygame.freetype
pygame.init()
window = pygame.display.set_mode((400, 200))
clock = pygame.time.Clock()
ft_font = pygame.freetype.SysFont('Courier', 50)
text1, rect1 = ft_font.render("Some ",
fgcolor = (255, 255, 255), style = pygame.freetype.STYLE_DEFAULT)
text2, rect2 = ft_font.render("Text",
fgcolor = (255, 255, 255), style = pygame.freetype.STYLE_STRONG)
run = True
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
window.fill(0)
window.blit(text1, (50, 75))
window.blit(text2, (50 + rect1.width, 75))
pygame.display.flip()
clock.tick(60)
pygame.quit()
exit()
See also Text and font

Loop within main loop in pygame

I'm fairly new to python and pygame. I'm trying to make a simple game as a practice. My problem is how do I have a loop (or many loops) inside the main game loop so that the graphic also updates inside sub loops? For example I have a button and a rectangle, if I press on the button I want the rectangle to move across the screen. Things I've tried:
Another while loop, it works but the rectangle won't "move" and just appears wherever the loop is finished
Custom event, however it either works once per frame or continuously in case of set_timer() function
Here's my code:
import pygame as pg
pg.init()
clock = pg.time.Clock()
running = True
window = pg.display.set_mode((640, 480))
window.fill((255, 255, 255))
btn = pg.Rect(0, 0, 100, 30)
rect1 = pg.Rect(0, 30, 100, 100)
while running:
clock.tick(60)
window.fill((255, 255, 255))
for e in pg.event.get():
if e.type == pg.MOUSEBUTTONDOWN:
(mouseX, mouseY) = pg.mouse.get_pos()
if(btn.collidepoint((mouseX, mouseY))):
rect1.x = rect1.x + 1
if e.type == pg.QUIT:
running = False
#end event handling
pg.draw.rect(window, (255, 0, 255), rect1, 1)
pg.draw.rect(window, (0, 255, 255), btn)
pg.display.flip()
#end main loop
pg.quit()
Any help is much appreciated
You have to implement some kind of state. Usually, you would use the Sprite class, but in your case, a simple variable would do.
Take a look at this example:
import pygame as pg
pg.init()
clock = pg.time.Clock()
running = True
window = pg.display.set_mode((640, 480))
window.fill((255, 255, 255))
btn = pg.Rect(0, 0, 100, 30)
rect1 = pg.Rect(0, 30, 100, 100)
move_it = False
move_direction = 1
while running:
clock.tick(60)
window.fill((255, 255, 255))
for e in pg.event.get():
if e.type == pg.MOUSEBUTTONDOWN:
(mouseX, mouseY) = pg.mouse.get_pos()
if(btn.collidepoint((mouseX, mouseY))):
move_it = not move_it
if e.type == pg.QUIT:
running = False
#end event handling
if move_it:
rect1.move_ip(move_direction * 5, 0)
if not window.get_rect().contains(rect1):
move_direction = move_direction * -1
rect1.move_ip(move_direction * 5, 0)
pg.draw.rect(window, (255, 0, 255), rect1, 1)
pg.draw.rect(window, (255, 0, 0) if move_it else (0, 255, 255), btn)
pg.display.flip()
#end main loop
pg.quit()
When the button is pressed, we just set the move_it flag.
Then, in the main loop, we check if this flag is set, and then move the Rect.
You should avoid creating multiple logic loops (sorry, I don't have a better word) in your game; see the problems you mentioned. Aim for one single main loop that does three things: input handling, game logic, and drawing.

Return a sprite to its original position?

So I have a very basic PyGame Python program which is a 'car' that the user controls and one which comes down he road towards it. How can I get the NPC car to return to its original position once it leaves the screen? My current code is shown below, thanks for any help you can provide!
import pygame, random
from car import Car
from car import AutoCar
pygame.init()
play = True
clock = pygame.time.Clock()
screen = pygame.display.set_mode((700, 750))
pygame.display.set_caption("Car Game")
# Define a bunch of colours
GREEN = (20, 255, 100)
GREY = (210, 210, 210)
WHITE = (255, 255, 255)
RED = (255, 0, 0)
PURPLE = (255, 0, 255)
BLUE = (0, 0, 255)
all_sprites = pygame.sprite.Group() # Creates a list of all sprites in the game
playerCar = Car(RED, 20, 30) # Defines the car, its colour, and its
size
playerCar.rect.x = 200
playerCar.rect.y = 300
AutoCar1 = AutoCar(BLUE, 20, 30)
AutoCar1.rect.x = 200
AutoCar1.rect.y = 20
all_sprites.add(playerCar) # Adds the playerCar to the list of sprites
all_sprites.add(AutoCar1) # Adds an automated 'enemy' car which you must avoid
while play:
for event in pygame.event.get():
if event.type == pygame.QUIT: # If the detected event is clicking the exit button, stop the program.
play = False
elif event.type == pygame.KEYDOWN: # If a key is pressed and the key is 'x', quit the game.
if event.key == pygame.K_x:
play = False
keys = pygame.key.get_pressed() # Defines a variable keys that consists of whatever event is happening in the game
if keys[pygame.K_LEFT]:
playerCar.move_left(5)
if keys[pygame.K_RIGHT]:
playerCar.move_right(5)
if keys[pygame.K_DOWN]:
playerCar.move_backward(5)
if keys[pygame.K_UP]:
playerCar.move_forward(5)
all_sprites.update() # Draws in all sprites
AutoCar1.auto_move_forward(3) # Makes the 'enemy' move forward automatically.
# Create the background
screen.fill(GREEN)
# Create the road
pygame.draw.rect(screen, GREY, [100, 0, 500, 750])
# Draw the lines on the road
pygame.draw.line(screen, WHITE, [340, 0], [340, 750], 5)
# Draw in all the sprites
all_sprites.draw(screen)
pygame.display.flip()
clock.tick(60) # Cap limit at 60FPS
pygame.quit()
I also have a file defining the class Car and Autocar:
import pygame
WHITE = (255, 255, 255)
class Car(pygame.sprite.Sprite): # Creates a class representing a car, derived from the sprite class
def __init__(self, color, width, height):
# Call the parent class (i.e. Sprite) constructor
super().__init__()
self.image = pygame.Surface([width,height])
self.image.fill(WHITE)
self.image.set_colorkey(WHITE) # Sets white as transparent for this class
# alternatively load a proper car picture:
# self.image = pygame.image.load("car.png").convert_alpha()
pygame.draw.rect(self.image, color, [0, 0, width, height])
self.rect = self.image.get_rect() # Fetches the rectangle with the dimensions of the image
def move_right(self, pixels): # Adds to the x value of the car the desired amount of pixels
self.rect.x += pixels
def move_left(self, pixels): # Same as move_right()
self.rect.x -= pixels
def move_forward(self, pixels):
self.rect.y -= pixels
def move_backward(self, pixels):
self.rect.y += pixels
class AutoCar(pygame.sprite.Sprite):
def __init__(self, color, width, height):
super().__init__()
self.image = pygame.Surface([width, height])
self.image.fill(WHITE)
self.image.set_colorkey(WHITE)
pygame.draw.rect(self.image, color, [0, 0, width, height])
self.rect = self.image.get_rect()
def auto_move_forward(self, pixels):
self.rect.y += pixels
def return_to_top(self):
Just store the start position as an attribute of the AutoCar, check in the update method if the sprite is below the screen and then set the self.rect.topleft (or one of the other rect attributes) to the self.start_position.
class AutoCar(pygame.sprite.Sprite):
def __init__(self, color, width, height):
super().__init__()
self.image = pygame.Surface([width, height])
self.image.fill(WHITE)
self.image.set_colorkey(WHITE)
pygame.draw.rect(self.image, color, [0, 0, width, height])
self.rect = self.image.get_rect()
self.start_position = (200, -50)
self.rect.topleft = self.start_position
def auto_move_forward(self, pixels):
self.rect.y += pixels
def return_to_top(self):
self.rect.topleft = self.start_position
def update(self):
if self.rect.top > 750: # Screen height.
self.rect.topleft = self.start_position

line 475, in draw self.spritedict[spr] = surface_blit(spr.image, spr.rect)

import pygame
import random
WIDTH = 360
HEIGHT = 480
FPS = 30
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)
pygame.init()
pygame.mixer.init()
screen = pygame.display.set_mode([WIDTH, HEIGHT])
pygame.display.set_caption("My Game")
clock = pygame.time.Clock()
class Player(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface((10, 10))
self.image.fill(GREEN)
self.rect = self.image.get_rect()
self.rect.centerx = WIDTH / 2
self.rect = HEIGHT - 10
all_sprites = pygame.sprite.Group()
Player = Player()
all_sprites.add(Player)
running = True
while running:
clock.tick(FPS)
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
all_sprites.update()
screen.fill(BLACK)
all_sprites.draw(screen)
pygame.display.flip()
pygame.quit()
Hello I am getting this error when i am trying to run this program File , line 54, in
all_sprites.draw(screen)
line 475, in draw
self.spritedict[spr] = surface_blit(spr.image, spr.rect)
TypeError: invalid destination position for blit. Plz help me.
Your rect attribute must be an Rect object but you're overriding it with an integer in the line self.rect = HEIGHT - 10. Since a single integer isn't a valid location to blit a sprite on you get "invalid destination position for blit".

Pygame.set_colorkey() issues

I made this pixel art dude but I just want him not the background. The guys a bmp file since png files arent working in pygame for me, The background color I used was CCCCCC which is 80,80,80 RGB color value, So I did, GameDisplay.set_colorkey(transcolor) after I blited the image. Transcolor was = (80,80,80) and so I thought this would work but it doesnt. Anyone know what I can do? The full code is below.
#!/usr/bin/python
import pygame
import time #Modules
import random
pygame.init()
black = (0,0,0) #Colors
white = (255,255,255)
blue = (72,94,181)
silver = (173,178,189)
brown = (102,51,0)
gray = (64,64,64)
transcolor = (0,0,0)
GameDisplay = pygame.display.set_mode((1400,750)) #Display
pygame.display.set_caption("Internet Adventure")
#Images
TitleScreen = pygame.image.load('InternetAdventureTitleScreen2.bmp')
DefaultMan = pygame.image.load('Buisnessman.bmp')
def mts(text, textcolor, x, y, fs):
font = pygame.font.Font(None,fs)
text = font.render(text, True, textcolor)
GameDisplay.blit(text, [x,y])
def button(x,y,w,h,ic,ac):
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:
print("Kaboom")
else:
pygame.draw.rect(GameDisplay, ic,(x,y,w,h))
def button2(x,y,w,h,ic,ac):
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:
pygame.quit()
quit()
else:
pygame.draw.rect(GameDisplay, ic,(x,y,w,h))
def aboutscreen():
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
quit()
pygame.display.update()
def StartUpScreen():
global transcolor
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
quit()
GameDisplay.blit(DefaultMan, (0,0))
GameDisplay.set_colorkey(transcolor)
button(285, 230, 290, 120, brown, brown)
mts("Play", black, 350, 260, 90)
button2(750, 230, 320, 120, brown, brown)
mts("Quit", black, 840, 260, 90)
pygame.display.update()
StartUpScreen()
You have to call .set_colorkey(transcolor) on the Surface you want to be partially transparent, not the screen surface (GameDisplay).
Call it on DefaultMan after loading the image:
DefaultMan = pygame.image.load('Buisnessman.bmp')
DefaultMan.set_colorkey(transcolor)
...

Resources