I have been assigned a homework problem in pygame to make two rectangles, one red , one green and when clicked, text appears which gives the name of the colour of the button, I have created the shapes successfully, however I don't know how to make the text appear when the button is clicked. Any help is appreciated
I have successfully drawn two rectangles using the pygame.draw.rect() function and I have called pos = pygame.mouse.get_pos() in order to get an x and y value for the user's mouse.
pos = pygame.mouse.get_pos()
x = pos[0]
y = pos[1]
screen.fill(WHITE)
pygame.draw.rect(screen, GREEN, [100, 300, 200, 200])
pygame.draw.rect(screen, RED, [400, 300, 200, 200])
pygame.display.flip()
clock.tick(60)
The result is two rectangles, one green, one red in the centre of the screen.
To verify if amouse button is pressed oyu can use pygame.mouse.get_pressed():
leftClicked = pygame.mouse.get_pressed()[0]
For text rendering you can use pygame.font or pygame.pygame.freetype. e. g:
import pygame.freetype
font = pygame.freetype.SysFont('Times New Roman', 30)
Define a pygame.Rect for each button:
rect_green = pygame.Rect(100, 300, 200, 200)
rect_red = pygame.Rect(400, 300, 200, 200)
Verify if the mouse button is pressed an the mouse coursor is on the button by .collidepoint()). e.g.:
pygame.draw.rect(screen, GREEN, rectGreen)
if leftClicked and rectGreen.collidepoint(pos):
text_surf, text_rect = font.render("GREEN", WHITE, size=30)
text_rect.center = rectGreen.center
screen.blit(text_surf, text_rect)
See the short example:
import pygame
import pygame.freetype
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
pygame.init()
size = (800,600)
screen = pygame.display.set_mode(size)
clock = pygame.time.Clock()
font = pygame.freetype.SysFont('Times New Roman', 30)
rect_green = pygame.Rect(100, 300, 200, 200)
rect_red = pygame.Rect(400, 300, 200, 200)
def DrawButton(surf, cursor_pos, pressed, rect, color, text_color, text):
pygame.draw.rect(surf, color, rect)
if pressed and rect.collidepoint(cursor_pos):
text_surf, text_rect = font.render(text, text_color, size=30)
text_rect.center = rect.center
screen.blit(text_surf, text_rect)
run = True
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
pos = pygame.mouse.get_pos()
x = pos[0]
y = pos[1]
leftClicked = pygame.mouse.get_pressed()[0]
screen.fill(WHITE)
DrawButton(screen, pos, leftClicked, rect_green, GREEN, BLACK, "GREEN")
DrawButton(screen, pos, leftClicked, rect_red, RED, BLACK, "RED")
pygame.display.flip()
clock.tick(60)
pygame.quit()
Related
I don't know how to detect when my mouse is on 2 rect and choose (if two rect has an action) the action. For example, in Windows, when two windows are one over the other, it's the first window that will be selected. I want to do exactly the same thing.
import pygame
class Rectangle(pygame.sprite.Sprite):
def __init__(self, screen, rect, x, y, color, name):
super().__init__()
self.screen = screen
self.name = name
self.screen_str = str(screen)
self.rect = rect
self.color = color
self.x, self.y = x, y
self.r = pygame.Surface((self.rect[2], self.rect[3]), pygame.SRCALPHA)
self.rect = self.r.get_rect()
self.rect.x, self.rect.y = x, y
self.r.fill(self.color)
pygame.init()
screen = pygame.display.set_mode((1280, 720))
pygame.display.set_caption("PyStoneTest")
width, height = screen.get_size()
background_default = "image\Settings\Wallpaper\default_1.jpg"
D = {}
D["Rect2"] = Rectangle(screen, (0, 200, width, 70), 0,
50, (255, 255, 0), "Rect2")
D["Rect1"] = Rectangle(screen, (0, 100, width-200, 200), 0,
100, (255, 0, 255), "Rect1")
Programme = ["Rect1", "Rect2"]
while True:
background = pygame.image.load(background_default).convert()
background = pygame.transform.scale(background, (width, height))
for event in pygame.event.get():
x,y = pygame.mouse.get_pos()
if event.type == pygame.QUIT:
pygame.quit()
for element in Programme:
if D[element].rect.collidepoint(x,y) and event.type == pygame.MOUSEBUTTONDOWN:
del Programme[Programme.index(D[element].name)]
Programme.append(D[element].name)
print(Programme)
screen.blit(background, (0, 0))
for element in Programme:
screen.blit(D[element].r, D[element].rect)
pygame.display.update()
You should first run for-loop to check all windows and use last one which collides with mouse.
elif event.type == pygame.MOUSEBUTTONDOWN:
last = None
for element in Programme:
if D[element].rect.collidepoint(event.pos):
last = element
if last:
Programme.remove(last)
Programme.append(last)
print(Programme)
Or you would have to check in reverse order - from last to first - and break loop on first matching rectangle.
elif event.type == pygame.MOUSEBUTTONDOWN:
last = None
for element in reversed(Programme):
if D[element].rect.collidepoint(event.pos):
last = element
break
if last:
Programme.remove(last)
Programme.append(last)
print(Programme)
Minimal working code with other changes
import pygame
# --- classes ---
class Rectangle(pygame.sprite.Sprite):
# I skip `x,y` because I have it in `rect`
def __init__(self, screen, rect, color, name):
super().__init__()
self.screen = screen
self.color = color
self.name = name
self.rect = pygame.Rect(rect)
self.image = pygame.Surface(self.rect.size, pygame.SRCALPHA)
self.image.fill(self.color)
def draw(self):
self.screen.blit(self.image, self.rect)
# --- functions ---
# empty
# --- main ---
pygame.init()
screen = pygame.display.set_mode((1280, 720))
screen_rect = screen.get_rect() # it can be useful to center elements on screen - `d[name].rect.center = screen_rect.center`
pygame.display.set_caption("PyStoneTest")
# raw string
background_default = r"image\Settings\Wallpaper\default_1.jpg"
# load and rescale before `while`-loop
background = pygame.image.load(background_default).convert()
background = pygame.transform.scale(background, screen_rect.size)
d = {} # PEP8: `lower_case_names` for variable
d["Rect2"] = Rectangle(screen, (0, 0, screen_rect.width-100, 70), (255, 255, 0), "Rect2")
d["Rect2"].rect.center = screen_rect.center
d["Rect1"] = Rectangle(screen, (0, 0, 70, screen_rect.height-100), (255, 0, 255), "Rect1")
d["Rect1"].rect.center = screen_rect.center
programme = ["Rect1", "Rect2"] # PEP8: `lower_case_names` for variable
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
exit() # to skip rest of while-loop
elif event.type == pygame.MOUSEBUTTONDOWN:
selected = None
for name in programme:
if d[name].rect.collidepoint(event.pos):
selected = name
if selected and selected != programme[-1]:
programme.remove(selected)
programme.append(selected)
print('after replace:', programme)
screen.blit(background, (0, 0))
for name in programme:
d[name].draw()
pygame.display.update()
I cannot help with your code since I cannot understand what you are doing but I can offer my own solution. Since pygame renders thigs that are drawn later on the top, you can change the rendering order of your rectangles by checking which rectangle is being clicked and swapping it with the last rectangle in your list.
Here is an example. The colors in my example change weirdly but that's because I am generating them on the fly just to be able to tell the different between the different rects. You shouldn't have this problem.
import pygame
pygame.init()
screen = pygame.display.set_mode((1280, 720))
pygame.display.set_caption("PyStoneTest")
rects = []
for i in range(10):
rects.append(pygame.Rect(i * 25, 100, 30, 30))
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
screen.fill((0, 0, 0))
c = 1
for rect in rects:
pygame.draw.rect(screen, (c, 100, 100), rect)
c += 20
clicked = pygame.mouse.get_pressed()
x,y = pygame.mouse.get_pos()
for rect in rects:
if (rect.collidepoint(x, y) and clicked[0]):
#swap it
rects[-1][:], rect[:] = rect[:], rects[-1][:]
pygame.display.update()
This question already has answers here:
How to Center Text in Pygame
(6 answers)
Pygame: Centering text system font text
(1 answer)
Closed 1 year ago.
So I just wanna get some words written in the centre of the rectangles like 'Play Game' or something like that and I'm not exactly sure what to really do.
def drawText(text, x, y, fSize):
screen_text = pygame.font.SysFont("Calibri", fSize, True).render(text, True, (0, 0, 0))
rect = screen_text.get_rect()
rect.center = ((screen_side_length // 2) + x), ((screen_side_length // 2) + y)
screen.blit(screen_text, rect)
def main_menu():
user_choice = None
while user_choice == None:
screen.fill((255, 255, 255))
drawText('main menu', 0, 0, 20)
x, y = pygame.mouse.get_pos()
button_1 = pygame.Rect(50, 100, 200, 50)
button_2 = pygame.Rect(50, 200, 200, 50)
pygame.draw.rect(screen, (255, 0, 0), button_1)
pygame.draw.rect(screen, (255, 0, 0), button_2)
click = False
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
if event.type == pygame.MOUSEBUTTONDOWN:
if event.button == 1:
click = True
pygame.display.update()
clock.tick(15)
return user_choice
You have to set the center of the text rectangle:
font = pygame.font.SysFont("Calibri", fSize, True)
def drawTextCenter(text, centerx, centery, fSize):
font.render(text, True, (0, 0, 0))
rect = screen_text.get_rect(center = (centerx, centery))
screen.blit(screen_text, rect)
Pass the center of the rectangle to the drawTextCenter function:
button_1 = pygame.Rect(50, 100, 200, 50)
drawTextCenter('main menu', button_1.centerx, button_1.centery, 20)
Do not create the pygame.font.Font object in every frame. Create the font once at the begin of the program. Creating a font object is very time consuming because the font file has to be read and interpreted.
import pygame
pygame.init()
width = 800
height = 600
running = True
screen = pygame.display.set_mode((width, height))
pygame.display.set_caption("Dijkstra's Path-Finding Algorithm Solver")
icon = pygame.image.load('icon.jpg')
pygame.display.set_icon(icon)
def title():
button_font = pygame.font.Font('TrajanPro-Regular.otf', 40)
rect_display = button_font.render('Dijkstra Path-Finding Algorithm', True, (255, 255, 255))
# display global total deaths
screen.blit(rect_display, (12, 10))
def title_underline():
# create the button
rect = pygame.Rect(0, 60, 800, 3)
rect_display = pygame.draw.rect(screen, [255, 255, 255], rect)
button_font = pygame.font.Font('TrajanPro-Regular.otf', 100)
rect_display = button_font.render('', True, (255, 255, 255))
# display global total deaths
screen.blit(rect_display, (270, 198))
def grid():
blockSize = 20 #Set the size of the grid block
for x in range(width):
for y in range(height):
rect = pygame.Rect(x*blockSize, y*blockSize, blockSize, blockSize)
pygame.draw.rect(screen, (200,200,200), rect, 1)
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
screen.fill((0, 0, 0))
title()
title_underline()
grid()
pygame.display.update()
I have created a title for my project and I created a grid for which I want to run my application, but the grid is being made in every single x, y location on my screen. But I want the grid to only be made under the title and not over it.
Define the top left coordinates of the grid (grid_x, grid_y) and add the coordinates when constructing the rectangle of a cell. For instance:
def grid():
grid_x = 12
grid_y = 30
blockSize = 20 #Set the size of the grid block
for x in range(width):
for y in range(height):
rect = pygame.Rect(
grid_x + x*blockSize, grid_y + y*blockSize,
blockSize, blockSize)
pygame.draw.rect(screen, (200,200,200), rect, 1)
I've been working on and off on a text-based adventure game for a few years now. It originally started as a way to learn Python, then I moved on to projects more relevant to my career after I became comfortable with the language. I'm now somewhat competent (Still a noob, but you know, progress) and I'd like to go back to the game to add in more complex functionality.
One thing that is annoying me is a visual bug in my start menu. Below is the code:
import pygame
from pygame_functions import setBackgroundImage
import gamefile
pygame.init()
display_width = 1500
display_height = 750
startMenu = pygame.display.set_mode((display_width, display_height))
pygame.display.set_caption("The Woodsman's Tale")
black = (0, 0, 0)
green = (0, 200, 0)
white = (255, 255, 255)
dark_red = (200, 0, 0)
bright_green = (0, 255, 0)
leaf_green = (0, 175, 75)
brown = (102, 51, 0)
red = (255, 0, 0)
clock = pygame.time.Clock()
def text_objects(text, font):
textSurface = font.render(text, True, black)
return textSurface, textSurface.get_rect()
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(startMenu, ac, (x, y, w, h))
if click[0] == 1 and action is not None:
if action == 'play':
gamefile.rungame()
start_menu().intro = False
elif action == 'quit':
pygame.quit()
quit()
else:
pygame.draw.rect(startMenu, ic, (x, y, w, h))
smallText = pygame.font.Font("freesansbold.ttf", 20)
TextSurf, TextRect = text_objects(msg, smallText)
TextRect.center = (x + (w / 2), (y + (h / 2)))
startMenu.blit(TextSurf, TextRect)
def start_menu():
intro = True
while intro:
setBackgroundImage('startScreen.png')
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
quit()
largeText = pygame.font.Font('BRADHITC.ttf', 90)
TextSurf, TextRect = text_objects("The Woodsman's Tale", largeText)
TextRect.center = (display_width / 2, display_height / 3)
startMenu.blit(TextSurf, TextRect)
button('Play', 350, 500, 200, 150, green, leaf_green, 'play')
button('Quit', 850, 500, 200, 150, dark_red, red, 'quit')
pygame.display.update()
clock.tick(15)
if __name__ == '__main__':
start_menu()
The setbackgroundimage function being imported is this:
def setBackgroundImage(img):
global bgSurface, backgroundImage
surf = loadImage(img)
backgroundImage = surf
screen.blit(surf, [0, 0])
bgSurface = screen.copy()
updateDisplay()
Now, the start menu works great. Everything functions properly. My issue is that when I click on the title bar, for example to move the window, the start menu begins stuttering visually.
Exactly what is happening is this: Once I click on the title bar, the "buttons" of the start menu and the Title text of the game disappear. Once the click is released, the buttons and Title text reappear, but stutter rapidly.
I'm not really sure if I'm providing enough for anyone to be able to tell what's going wrong, so I apologize if that's the case.
The fist issue is that setBackgroundImage seems to update the display (updateDisplay()).
The display has to be updated once at the end of the main application loop, and should not be updated multiple times. That cause the flickering. Remove the display update from setBackgroundImage:
def start_menu():
intro = True
while intro:
setBackgroundImage('startScreen.png') # draw background but do no update the display
# [...]
pygame.display.update() # the one and only update at the end of the loop
clock.tick(15)
The second issue, that the background image is loaded in setBackgroundImage. That cause that the image is loaded continuously in every frame. That causes a performance impact.
Load the image before the main loop and pass the Surface object to setBackgroundImage:
def start_menu():
surf = loadImage('startScreen.png')
intro = True
while intro:
setBackgroundImage(surf)
# [...]
I copied most of this code from an earlier script which worked fine, but when I load up the game, pretty much all the elements are flickering.
Here is the Minimal complete verifiable example here:
import pygame
pygame.init()
displayWidth = 1280
displayHeight = 720
gameDisplay = pygame.display.set_mode((displayWidth, displayHeight))
pygame.display.set_caption('Grow')
black = (0, 0, 0)
brown = (100, 100, 0)
green = (0, 200, 0)
lightGreen = (0, 255, 0)
red = (200, 0, 0)
lightRed = (255, 0, 0)
smallfont = pygame.font.SysFont("comicsansms", 25)
medfont = pygame.font.SysFont("comicsansms", 50)
largefont = pygame.font.SysFont("comicsansms", 80)
clock = pygame.time.Clock()
FPS = 60
def gameIntro():
intro = True
while intro:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
quit()
gameDisplay.fill(brown)
message_to_screen('Welcome to Tanks', green, -100, 'large')
message_to_screen('The objective is to shoot and destroy', black, -30)
message_to_screen('Shoot and destroy the enemy tank before they destroy you', black, 10)
message_to_screen('The more enemies you destroy, the harder they get', black, 50)
button('quit', 500, 500, 100, 50, red, lightRed, action = 'quit')
button('play', 200, 500, 100, 50, green, lightGreen, action = 'play')
pygame.display.update()
clock.tick(0)
def gameLoop():
pygame.quit()
quit()
def button(text, x, y, width, height, inactive_color, active_color, action = None):
cur = pygame.mouse.get_pos()
click = pygame.mouse.get_pressed()
if x + width > cur[0] > x and y + height > cur[1] > y:
pygame.draw.rect(gameDisplay, active_color, (x, y, width, height))
if click[0] == 1 and action != None:
if action == 'quit':
pygame.quit()
quit()
if action == 'play':
gameLoop()
else:
pygame.draw.rect(gameDisplay, inactive_color, (x, y, width, height))
text_to_button(text, black, x, y, width, height)
def text_to_button(msg, colour, buttonx, buttony, buttonwidth, buttonheight, size = 'small'):
textSurf, textRect = text_objects (msg, colour, size)
textRect.center = ((buttonx + (buttonwidth/2)), buttony + (buttonheight/2))
gameDisplay.blit(textSurf, textRect)
pygame.display.update()
def message_to_screen(msg, colour, y_displace = 0, size = 'small'):
textSurf, textRect = text_objects (msg, colour, size)
textRect.center = (displayWidth / 2), (displayHeight / 2) + y_displace
gameDisplay.blit(textSurf, textRect)
pygame.display.update()
def text_objects(text, colour, size):
if size == 'small':
textSurface = smallfont.render (text, True, colour)
elif size == 'medium':
textSurface = medfont.render (text, True, colour)
elif size == 'large':
textSurface = largefont.render (text, True, colour)
return textSurface, textSurface.get_rect()
gameIntro()
For some reason, in the gameIntro function, when I call the message_to_screen and button functions, they all start flickering (everything except the first function called). Changing the order of the functions changes which one will stop flickering. Everything else works fine. The buttons change colour when the cursor is above them and they can still be clicked and have functionality
Don't call pygame.display.update() multiple times in each iteration of your game loop. It will lead to the screen flickering you see.
Simply remove the pygame.display.update() calls in the message_to_screen and text_to_button functions.