How to create button with transparent background and shadow in pygame? - python

Hi guys I need your help. I want to set opacity of the rectangles which are created by help of class button
this is before class
screen = pygame.display.set_mode((screen_width, screen_height))
pygame.display.set_caption('Snake')
bg_img = pygame.image.load('Graphics/bg.png')
bg_img = pygame.transform.scale(bg_img,(screen_width,screen_height))
class button():
def __init__(self,text,width,height,pos,elevation,image,color,shadow,hover):
self.image = pygame.transform.scale(image, (int(width), int(height)))
self.rect = self.image.get_rect()
self.elevation = elevation
self.dynamic_elecation = elevation
self.original_y_pos = pos[1]
self.color = color
self.color_shadow = shadow
self.hover = hover
self.clicked = False
self.top_rect = pygame.Rect(pos,(width,height))
self.top_color = color
self.bottom_rect = pygame.Rect(pos,(width,height))
self.bottom_color = shadow
font = pygame.font.SysFont('rockwell', 50)
self.text_surf = font.render(text,True,'#FFFFFF')
self.text_rect = self.text_surf.get_rect(center = self.top_rect.center)
here is function for drawing and also detecting if it was clicked
def draw_button(self):
action = False
pos = pygame.mouse.get_pos()
self.top_rect.y = self.original_y_pos - self.dynamic_elecation
self.text_rect.center = self.top_rect.center
self.bottom_rect.midtop = self.top_rect.midtop
self.bottom_rect.height = self.top_rect.height + self.dynamic_elecation
pygame.draw.rect(screen,self.bottom_color, self.bottom_rect, border_radius = 12)
pygame.draw.rect(screen,self.top_color, self.top_rect, border_radius = 12)
screen.blit(self.text_surf, self.text_rect)
if self.top_rect.collidepoint(pos):
self.top_color = self.color
if pygame.mouse.get_pressed()[0]:
self.dynamic_elecation = 0
self.clicked = True
elif pygame.mouse.get_pressed()[0] == 0 and self.clicked == True:
self.clicked = False
action = True
else:
self.dynamic_elecation = self.elevation
self.top_color = self.hover
else:
# self.dynamic_elecation = self.elevation
self.top_color = self.color
screen.blit(self.image, (self.top_rect))
return action
Here I am creating that button
button_snake = '#191919'
hover_snake = '#999999'
shadow_snake = '#191919'
black = button('',600,180,(100,30),0, menu_img, '#191919', '#191919', '#191919')
red_snake = pygame.image.load('Graphics/snake_r.png').convert_alpha()
In conclusion I want to have another value in class button (self,text,...) and there would be opacity
example

See How to separately change the opacity of a text on a button pygame? and Draw a transparent rectangles and polygons in pygame. You have to draw the rectangle on a transparent pygame.Surface and blit the Surface on the screen. A transparent pygame.Surface can be crate with the pygame.SRCALPHA flag:
top_surf = pygame.Surface(self.top_rect.size, pygame.SRCALPHA)
pygame.draw.rect(top_surf, self.top_color, (0, 0, *self.top_rect.size), border_radius = 12)
screen.blit(top_surf, self.top_rect.topleft)
Coplete example:
import pygame
class Button():
def __init__(self, text, width, height, pos, elevation, image, color, shadow, hover):
#self.image = pygame.transform.scale(image, (int(width), int(height)))
#self.rect = self.image.get_rect()
self.elevation = elevation
self.original_y_pos = pos[1]
self.color = color
self.color_shadow = shadow
self.hover = hover
self.clicked = False
self.top_rect = pygame.Rect(pos,(width,height))
self.top_color = color
self.bottom_rect = pygame.Rect(pos,(width,height))
self.bottom_color = shadow
font = pygame.font.SysFont('rockwell', 50)
self.text_surf = font.render(text,True,'#FFFFFF')
self.text_rect = self.text_surf.get_rect(center = self.top_rect.center)
def draw_button(self, screen):
action = False
pos = pygame.mouse.get_pos()
top_rect = self.top_rect.copy()
bottom_rect = self.bottom_rect.copy()
bottom_rect.x += 20
bottom_rect.y += 20
if top_rect.collidepoint(pos):
self.top_color = self.color
if pygame.mouse.get_pressed()[0]:
self.clicked = True
bottom_rect.inflate_ip(self.elevation, self.elevation)
top_rect.inflate_ip(self.elevation, self.elevation)
elif pygame.mouse.get_pressed()[0] == 0 and self.clicked == True:
self.clicked = False
action = True
self.top_color = self.hover
else:
self.top_color = self.color
bottom_surf = pygame.Surface(bottom_rect.size, pygame.SRCALPHA)
pygame.draw.rect(bottom_surf, self.bottom_color, (0, 0, *bottom_rect.size), border_radius = 12)
screen.blit(bottom_surf, bottom_rect.topleft)
top_surf = pygame.Surface(top_rect.size, pygame.SRCALPHA)
pygame.draw.rect(top_surf, self.top_color, (0, 0, *top_rect.size), border_radius = 12)
screen.blit(top_surf, top_rect.topleft)
screen.blit(self.text_surf, self.text_rect)
return action
pygame.init()
screen = pygame.display.set_mode((400, 400))
clock = pygame.time.Clock()
background = pygame.Surface(screen.get_size())
ts, w, h, c1, c2 = 50, *screen.get_size(), (128, 128, 128), (96, 96, 96)
tiles = [((x*ts, y*ts, ts, ts), c1 if (x+y) % 2 == 0 else c2) for x in range((w+ts-1)//ts) for y in range((h+ts-1)//ts)]
for rect, color in tiles:
pygame.draw.rect(background, color, rect)
font = pygame.font.SysFont(None, 80)
text = font.render("Button", True, (255, 255, 255))
button = Button("Button", 200, 70, (80, 120), 20, None, (128, 128, 255, 128), (64, 64, 128, 128), (255, 128, 255, 128))
run = True
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
screen.blit(background, (0, 0))
button.draw_button(screen)
pygame.display.flip()
clock.tick(60)
pygame.quit()
exit()

Related

The button does not become the color I need

I have a button that changes color when the cursor hovers over it, and I want to make it so that when the button is pressed, it leaves the color that it has when the cursor hovers over it. There are my unsuccessful attempts to do this in the code. I think I made a mistake somewhere in the "draw" function in the "Button" class.
import pygame
import pygame_widgets as pw
pygame.init() # Инициализируем модуль pygame
width = 1134 # ширина игрового окна
height = 693 # высота игрового окна
fps = 30 # частота кадров в секунду
game_name = "Тренажёр" # название нашей игры
snd_dir = 'sound/' # Путь до папки со звуками
img_dir = 'images/' # Путь до папки со спрайтами
# Цвета
BLACK = "#000000"
WHITE = "#FFFFFF"
RED = "#FF0000"
GREEN = "#008000"
BLUE = "#0000FF"
CYAN = "#00FFFF"
OneNumber = list(range(1, 10))
TwoNumber = list(range(10, 100))
ThreeNumber = list(range(100, 1000))
button_sound = pygame.mixer.Sound(snd_dir + 'button.mp3')
class Background(pygame.sprite.Sprite):
def __init__(self, image_file, location):
pygame.sprite.Sprite.__init__(self) #call Sprite initializer
self.image = pygame.image.load(image_file)
self.rect = self.image.get_rect()
self.rect.left, self.rect.top = location
def print_text(message, x, y, font_color=(0, 0, 0), font_type='ttf/Ping Pong.ttf', font_size=30):
font_type = pygame.font.Font(font_type, font_size)
text = font_type.render(message, True, font_color)
screen.blit(text, (x, y))
class Button:
def __init__(self, width, height):
self.width = width
self.height = height
self.inactive_color = (255, 165, 0)
self.active_color = (13, 162, 58)
self.button_active = False
def draw(self,x ,y, message, action=None):
mouse = pygame.mouse.get_pos()
click = pygame.mouse.get_pressed()
if x < mouse[0] < x + self.width:
if y < mouse[1] < y + self.height:
pygame.draw.rect(screen, self.active_color, (x, y, self.width, self.height))
if click[0] == 1:
pygame.mixer.Sound.play(button_sound)
pygame.draw.rect(screen, self.active_color, (x, y, self.width, self.height))
self.button_active = True
pygame.time.delay(300)
print(self.button_active)
#if action is not None:
#action()
elif y < mouse[1] < y + self.height and self.button_active == True:
pygame.draw.rect(screen, self.active_color, (x, y, self.width, self.height))
elif x > mouse[0] > x + self.width and self.button_active == False:
pygame.draw.rect(screen, self.inactive_color, (x,y, self.width, self.height))
print_text(message, x + 10, y + 10)
BackGround = Background(img_dir + "fon.jpg", [0,0])
# Создаем игровой экран
screen = pygame.display.set_mode((width, height))
pygame.display.set_caption(game_name) # Заголовок окна
icon = pygame.image.load(img_dir + 'icon.png') # загружаем файл с иконкой
pygame.display.set_icon(icon) # устанавливаем иконку в окно
timer = pygame.time.Clock() # Создаем таймер pygame
run = True
button = Button(90, 50)
while run: # Начинаем бесконечный цикл
timer.tick(fps) # Контроль времени (обновление игры)
for event in pygame.event.get(): # Обработка ввода (события)
if event.type == pygame.QUIT: # Проверить закрытие окна
run = False # Завершаем игровой цикл
# Рендеринг (прорисовка)
screen.fill(WHITE) # Заливка заднего фона
screen.blit(BackGround.image, BackGround.rect)
button.draw(20, 100, "wow")
pygame.display.update() # Переворачиваем экран
pygame.quit() # Корректно завершаем игру
Test if the mouse is on the button:
button_rect = pygame.Rect(x, y, self.width, self.height)
mouse_on_button = button_rect.collidepoint(mouse)
Set self.button_active when the mouse is on the button and the mouse button is pressed:
if mouse_on_button and click[0] == 1:
self.button_active = True
Set the color depending on mouse_on_button and self.button_active:
color = self.inactive_color
if mouse_on_button or self.button_active:
color = self.active_color
pygame.draw.rect(screen, color, button_rect)
class Button:
class Button:
def __init__(self, width, height):
self.width = width
self.height = height
self.inactive_color = (255, 165, 0)
self.active_color = (13, 162, 58)
self.button_active = False
def draw(self,x ,y, message, action=None):
mouse = pygame.mouse.get_pos()
click = pygame.mouse.get_pressed()
button_rect = pygame.Rect(x, y, self.width, self.height)
mouse_on_button = button_rect.collidepoint(mouse)
if mouse_on_button and click[0] == 1:
self.button_active = True
color = self.inactive_color
if mouse_on_button or self.button_active:
color = self.active_color
pygame.draw.rect(screen, color, button_rect)
print_text(message, x + 10, y + 10)
However pygame.mouse.get_pressed() is not an event. If you want to toggle the button on and off you have to use the MOUSEBUTTONDOWN event. pygame.mouse.get_pressed() returns a list of Boolean values ​​that represent the state (True or False) of all mouse buttons. The state of a button is True as long as a button is held down. The MOUSEBUTTONDOWN event occurs once when you click the mouse button and the MOUSEBUTTONUP event occurs once when the mouse button is released.
class Button:
def __init__(self, width, height):
self.width = width
self.height = height
self.inactive_color = (255, 165, 0)
self.active_color = (13, 162, 58)
self.button_active = False
def draw(self,x ,y, message, clicked, action=None):
mouse = pygame.mouse.get_pos()
button_rect = pygame.Rect(x, y, self.width, self.height)
mouse_on_button = button_rect.collidepoint(mouse)
if mouse_on_button and clicked:
self.button_active = not self.button_active
color = self.inactive_color
if mouse_on_button or self.button_active:
color = self.active_color
pygame.draw.rect(screen, color, button_rect)
print_text(message, x + 10, y + 10)
while run:
timer.tick(fps)
left_click = False
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
if event.type == pygame.MOUSEBUTTONDOWN:
left_click = event.button = 1
screen.fill(WHITE)
screen.blit(BackGround, (0, 0))
button.draw(20, 100, "wow", left_click)
pygame.display.update()
Also see Pygame mouse clicking detection.

How do i make a text box pop up when i hover over a button in pygame

I want to be able to make a text box pop up when I hover over the button, now the button changes colour when I hover over it but no text box is displayed how can I make that happen or do I need to something entirely different like make a new screen pop up when I hover over either way the hovering over isnt working and I dont know how to fix it and id like some help please
import pygame
pygame.init()
pygame.font.init()
colours = {"White" : (255, 255, 255), "Black" : (0, 0, 0), "Red" : (255, 0, 0), "Blue" : (0, 0, 255), "Green" : (0, 255, 0)}
orighpamt = 10
currentGoblin = 0
class Screen():
def __init__(self, title, width=400, height=600, fill=colours["White"]):
self.title=title
self.width=width
self.height = height
self.fill = fill
self.current = False
def makeCurrent(self):
pygame.display.set_caption(self.title)
self.current = True
self.screen = pygame.display.set_mode((self.width, self.height))
def endCurrent(self):
self.current = False
def checkUpdate(self):
return self.current
def screenUpdate(self):
if(self.current):
self.screen.fill(self.fill)
def returnTitle(self):
return self.screen
class Button():
def __init__(self, x, y, sx, sy, bcolour, fbcolour, font, fontsize, fcolour, text):
self.x = x
self.y = y
self.sx = sx
self.sy = sy
self.bcolour = bcolour
self.fbcolour = fbcolour
self.fcolour = fcolour
self.fontsize = fontsize
self.text = text
self.current = False
self.buttonf = pygame.font.SysFont(font, fontsize)
def showButton(self, display):
if(self.current):
pygame.draw.rect(display, self.fbcolour, (self.x, self.y, self.sx, self.sy))
else:
pygame.draw.rect(display, self.bcolour, (self.x, self.y, self.sx, self.sy))
textsurface = self.buttonf.render(self.text, False, self.fcolour)
display.blit(textsurface, ((self.x + (self.sx/2) - (self.fontsize/2)*(len(self.text)/2) - 5,(self.y + (self.sy/2) -(self.fontsize/2) - 4))))
def focusCheck(self, mousepos, mouseclick):
if(mousepos[0] >= self.x and mousepos[0] <= self.x + self.sx and mousepos[1] >= self.y and mousepos[1] <= self.y + self.sy):
self.current = True
return mouseclick
else:
self.current = False
return False
class Enemy(pygame.sprite.Sprite):
def __init__(self, dx, dy, filename):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load(filename).convert()
self.rect = self.image.get_rect()
self.rect.x = dx
self.rect.y = dy
def draw(self, screen):
screen.blit(self.image, self.rect)
menuScreen = Screen("Menu Screen")
screen2 = Screen("Screen 2")
win = menuScreen.makeCurrent()
done = False
font = pygame.font.Font('freesansbold.ttf', 32)
clickdamage = 1
hitpoints = orighpamt
DungeonButton = Button(125, 500, 150, 50, colours["Black"], colours["Blue"], "arial", 20, colours["White"], "Dungeon")
hitboxButton = Button(80, 50, 280, 400, colours["White"], colours["Red"], "arial", 20, colours["White"], "")
hitpointamount = Button(100, 0, 200, 50, colours["White"], colours["Black"], "arial", 20, colours["Black"], str(hitpoints))
shopButton = Button(125, 500, 150, 50, colours["Black"], colours["Red"], "arial", 20, colours["White"], "Shop")
Assassin_Text = Button(50, 350, 400, 50, colours["White"], colours["Red"], "arial", 18, colours["Black"], "Sharpen your sword and increase your damage per click")
Assassin_Upgrade = Button(10, 300, 125, 50, colours["Black"], colours["Red"], "arial", 15, colours["White"], "Assassin")
goblin = Enemy(0 , 20, "images\goblin-resized.png")
goblin2 = Enemy(80 , 50, "images\monster2.png")
toggle = False
while not done:
menuScreen.screenUpdate()
screen2.screenUpdate()
mouse_pos = pygame.mouse.get_pos()
keys = pygame.key.get_pressed()
mouse_click = False
for event in pygame.event.get():
if(event.type == pygame.QUIT):
done = True
if event.type == pygame.MOUSEBUTTONDOWN:
mouse_click = True
if menuScreen.checkUpdate():
screen2button = shopButton.focusCheck(mouse_pos, mouse_click)
shopButton.showButton(menuScreen.returnTitle())
if screen2button:
win = screen2.makeCurrent()
menuScreen.endCurrent()
elif screen2.checkUpdate():
returnm = DungeonButton.focusCheck(mouse_pos, mouse_click)
Assassin_Upgrade.showButton(screen2.returnTitle())
if Assassin_Upgrade.focusCheck(mouse_pos):
Assassin_Text.showButton(screen2.returnTitle())
if returnm:
win = menuScreen.makeCurrent()
screen2.endCurrent()
pygame.display.update()
pygame.quit()
Add another text attribute to your class. e.g.:
class Button():
def __init__(self, x, y, sx, sy, bcolour, fbcolour, font, fontsize, fcolour, text, tiptext):
# [...]
self.tiptextsurface = self.buttonf.render(tiptext, False, (0, 0, 0), (255, 255, 0))
Draw the text in a Button.showTip method, when self.current is set:
class Button():
# [...]
def showTip(self, display):
if self.current:
mouse_pos = pygame.mouse.get_pos()
display.blit(self.tiptextsurface, (mouse_pos[0]+16, mouse_pos[1]))
Minimal example based on your code:
I've simplified the Button class, but it behaves exactly like the class in your question.
import pygame
pygame.init()
class Button():
def __init__(self, x, y, sx, sy, bcolour, fbcolour, font, fontsize, fcolour, text, tiptext):
self.rect = pygame.Rect(x, y, sx, sy)
self.bcolour = bcolour
self.fbcolour = fbcolour
self.fcolour = fcolour
self.fontsize = fontsize
self.current = False
self.buttonf = pygame.font.SysFont(font, fontsize)
self.textsurface = self.buttonf.render(text, False, self.fcolour)
self.tiptextsurface = self.buttonf.render(tiptext, False, (0, 0, 0), (255, 255, 0))
def showButton(self, display):
color = self.fbcolour if self.current else self.bcolour
pygame.draw.rect(display, color, self.rect)
display.blit(self.textsurface, self.textsurface.get_rect(center = self.rect.center))
def showTip(self, display):
if self.current:
mouse_pos = pygame.mouse.get_pos()
display.blit(self.tiptextsurface, (mouse_pos[0]+16, mouse_pos[1]))
def focusCheck(self, mousepos, mouseclick):
self.current = self.rect.collidepoint(mousepos)
return mouseclick if self.current else True
screen = pygame.display.set_mode((400, 600))
clock = pygame.time.Clock()
font = pygame.font.Font('freesansbold.ttf', 32)
shopButton = Button(125, 500, 150, 50, "black", "red", "arial", 20, "white", "Shop", "Enter the shop")
done = False
while not done:
clock.tick(60)
for event in pygame.event.get():
if(event.type == pygame.QUIT):
done = True
if event.type == pygame.MOUSEBUTTONDOWN:
mouse_click = True
mouse_pos = pygame.mouse.get_pos()
mouse_click = False
screen2button = shopButton.focusCheck(mouse_pos, mouse_click)
screen.fill((255, 255, 255))
shopButton.showButton(screen)
shopButton.showTip(screen)
pygame.display.update()
pygame.quit()
It's easier to answer questions that provide a Minimal, Reproducible Example. Your sample has external image and font dependencies that will need to be overcome for someone to debug your code.
If your button had an associated rect, then you can tell if the mouse cursor is hovering over the button using Rect.colliderect(position).
Here's a minimal example showing some hovering text when the mouse is in a rectangle:
import pygame
WIDTH = 640
HEIGHT = 480
FPS = 30
pygame.init()
pygame.font.init()
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption(f"Color: purple")
clock = pygame.time.Clock()
# create a centered rectangle to fill with color
color_rect = pygame.Rect(100, 100, WIDTH - 200, HEIGHT - 200)
font = pygame.font.SysFont(None, 50, True)
msg = " Hovering Message "
hover_surface = font.render(msg, True, pygame.Color("chartreuse"), pygame.Color("firebrick"))
hovering = False
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
# update game elements - Get mouse position
mx, my = pygame.mouse.get_pos()
# draw surface - fill background
screen.fill(pygame.Color("grey"))
## draw central rect
screen.fill(pygame.Color("purple"), color_rect)
## display the hovering text if the mouse is over the rectangle
if color_rect.collidepoint((mx, my)):
screen.blit(hover_surface, (mx, my))
# show surface
pygame.display.update()
# limit frames
clock.tick(FPS)
pygame.quit()
Your code uses a lot of raw geometric calculations where it might be easier in the long run to use some of the native pygame elements. For example, you can make your buttons immobile Sprites, and keep them in a Sprite group to make managing them easier.

Pygame - center text in moving rect

i'm making a drop the number game in pygame and have squares which will fall from the top of the screen to the bottom whilst having a numeric value and that number being displayed on each square. I am struggling to get the text centered on the square and it is made more difficult when each number can have 1 - 4 digits in them
class sqr:
def __init__(self):
self.colours = [(255,0,0), (0,255,0), (0,0,255)]
self.Xpositions = [0,125,245,365,485]
self.OnScreen = False
self.X = random.choice(self.Xpositions)
self.Y = 0
self.colour = random.choice(self.colours)
self.number = random.choice([20,40,80])
self.numberFont = pygame.font.Font("TitilliumWeb-Black.ttf", 48)
def drawSquare(self, colour, X, Y):
pygame.draw.rect(screen, colour, (X, Y, 120, 120))
def numberTextFunc(self, X, Y):
numberText = self.numberFont.render(f"{self.number}", True, (87, 63, 63))
screen.blit(numberText, (X , Y))
square = sqr()
gameRunning = True
while gameRunning:
background = screen.fill((0,100,140))
#draw lines for grid
for i in range(5):
pygame.draw.rect(screen, (0,0,0), (line[i], 0 , 5, 800))
for event in pygame.event.get():
if event.type == pygame.QUIT:
gameRunning = False
#square
square.drawSquare(square.colour, square.X, square.Y)
square.numberTextFunc(square.X, square.Y)
square.OnScreen = True
square.Y += 1
if square.Y >= 680:
square.Y = 680
pygame.display.update()
Use pygame.Surface.get_rect to get the text rectangle. pygame.Surface.get_rect.get_rect() returns a rectangle with the size of the Surface object, that always starts at (0, 0). Set the center of the text rectangle by the center of the square. Use the text reectnagle to blit the text. The second argument of blit is either a tuple (x, y) or a rectangle. With a rectangle, only the upper left corner of the rectangle is taken into account. Therefore you can pass the text rectangle directly to blit:
class sqr:
# [...]
def numberTextFunc(self, X, Y):
numberText = self.numberFont.render(f"{self.number}", True, (87, 63, 63))
rect = pygame.Rect(X, Y, 120, 120)
textRect = numberText.get_rect(center = rect.center)
screen.blit(numberText, textRect)
You can simplify your code. X and Y are attributes of the object. Hence there is no need to pass the coordinates to the methods:
class sqr:
def __init__(self):
self.colours = [(255,0,0), (0,255,0), (0,0,255)]
self.Xpositions = [0,125,245,365,485]
self.OnScreen = False
self.X = random.choice(self.Xpositions)
self.Y = 0
self.colour = random.choice(self.colours)
self.number = random.choice([20,40,80])
self.numberFont = pygame.font.Font("TitilliumWeb-Black.ttf", 48)
def drawSquare(self, colour):
pygame.draw.rect(screen, colour, (self.X, self.Y, 120, 120))
def numberTextFunc(self):
numberText = self.numberFont.render(f"{self.number}", True, (87, 63, 63))
rect = pygame.Rect(self.X, self.Y, 120, 120)
textRect = numberText.get_rect(center = rect.center)
screen.blit(numberText, textRect)
gameRunning = True
while gameRunning:
background = screen.fill((0,100,140))
#draw lines for grid
for i in range(5):
pygame.draw.rect(screen, (0,0,0), (line[i], 0 , 5, 800))
for event in pygame.event.get():
if event.type == pygame.QUIT:
gameRunning = False
#square
square.drawSquare(square.colour)
square.numberTextFunc()
square.OnScreen = True
square.Y += 1
if square.Y >= 680:
square.Y = 680
pygame.display.update()

How do my make my image (rect) collide with my randomly generated circles to end the game? [duplicate]

This question already has answers here:
How do I detect collision in pygame?
(5 answers)
Closed 1 year ago.
I have been trying to solve this for weeks. This is a free-falling pit game, where my character (in this case a chimpanzee png) falls from the top of the screen to the bottom while trying to dodge the random black circles. I have tried so many angles at tackling this, I have tried the standard collision I was taught (pygame.sprite.groupcollide(Group1, Group2, False, True, collided = None) I have tried doing collisions with the colour black only, different formats of spawning my image and all that, and I haven't been able to find anything that works with my code. It has resulted in the code being very messy. I have tried to clean it up for this, but it still might be hard to understand, but if anybody has any solution to this, please let me know as I have been stumped for weeks. I just want the game to close or "game over" when they touch a circle Code:
import sys
import pygame as pg
RED = (255, 0, 0)
GRAY = (150, 150, 150)
GREEN =(34, 177, 76)
BLACK = (0,0,0)
import random
import math
def main():
width, height = 1024, 768
hbox, vbox = 80, 80
w, h = 640, 240
screen = pg.display.set_mode((width, height))
BG = pg.image.load('jungle.jpg').convert()
img = pg.image.load('MONKEY.png')
img = pg.transform.scale(img, (80, 80))
img.convert()
rect = img.get_rect()
rect.center = w//2, h//2
clock = pg.time.Clock()
Score = 0
class Circle(pg.sprite.Sprite):
def __init__(self):
#You can initialise the size to a random value
self.pos = [random.randint(0, 1000), random.randint(180, 600)]
self.color = (0,0, 0)
self.radius = 20
def draw(self):
pg.draw.circle(screen, self.color, (self.pos[0], self.pos[1]), self.radius)
circles = []
for i in range(20):
circles.append(Circle())
def checkIntersection(c1, c2):
dx = c1.pos[0] - c2.pos[0]
dy = c1.pos[1] - c2.pos[1]
d = math.hypot(dx, dy)
if d < c1.radius + c2.radius:
return True
return False
for i in range(19):
while checkIntersection(circles[i], circles[i + 1]):
circles[i].pos = random.randint(0, 1000), random.randint(0, 700)
velocity = (0, 0)
done = False
while not done:
for event in pg.event.get():
if event.type == pg.QUIT:
done = True
keys = pg.key.get_pressed()
# booster
move = 8 if keys[pg.K_LSHIFT] else 4
if keys[pg.K_a]: #to move left
rect.x -= move
if rect.x < 0 : rect.x = 0
if keys[pg.K_d]: #to move right
rect.x += move
if rect.x > width-hbox : rect.x = width - hbox
Score += 1
rect.y += 2.5
screen.blit(BG, (0,0))
screen.blit(img,rect)
pg.draw.rect(screen, RED, rect, 1)
pg.draw.polygon(screen, GREEN, ((1024,768), (0,768), (0,640),(1024,640)))
font = pg.font.SysFont("comicsansms", 45)
text = font.render (" " + str(Score), 1, BLACK)
screen.blit(text,(480,700))
pg.event.get()
for circle in circles:
circle.draw()
pg.display.update()
clock.tick(30)
if __name__ == '__main__':
pg.init()
main()
pg.quit()
sys.exit()
Use a "mask" collision. See How can I made a collision mask? and Pygame mask collision
Create a Sprite class with mask (see pygame.mask.from_surface):
class Circle(pg.sprite.Sprite):
def __init__(self):
super().__init__()
self.pos = [random.randint(0, 1000), random.randint(180, 600)]
self.color = (0,0, 0)
self.radius = 20
self.image = pg.Surface((self.radius*2, self.radius*2), pg.SRCALPHA)
pg.draw.circle(self.image , self.color, (self.radius, self.radius), self.radius)
self.rect = self.image.get_rect(center = self.pos)
self.mask = pg.mask.from_surface(self.image)
Create a Sprite class for the player (also with a mask)
class Player(pg.sprite.Sprite):
def __init__(self, x, y):
super().__init__()
self.image = pg.image.load('MONKEY.png')
self.image = pg.transform.scale(self.image, (80, 80)).convert()
self.rect = self.image.get_rect()
self.rect.center = x, y
self.mask = pg.mask.from_surface(self.image)
Manage the Sprites in pygame.sprite.Group and use pygame.sprite.spritecollide and pygame.sprite.collide_mask() for the collision test:
if pg.sprite.spritecollide(player, circles, False, collided = pg.sprite.collide_mask):
done = True
Complete example:
import sys
import pygame as pg
RED = (255, 0, 0)
GRAY = (150, 150, 150)
GREEN =(34, 177, 76)
BLACK = (0,0,0)
import random
import math
class Circle(pg.sprite.Sprite):
def __init__(self):
super().__init__()
self.pos = [random.randint(0, 1000), random.randint(180, 600)]
self.color = (0,0, 0)
self.radius = 20
self.image = pg.Surface((self.radius*2, self.radius*2), pg.SRCALPHA)
pg.draw.circle(self.image , self.color, (self.radius, self.radius), self.radius)
self.rect = self.image.get_rect(center = self.pos)
self.mask = pg.mask.from_surface(self.image)
class Player(pg.sprite.Sprite):
def __init__(self, x, y):
super().__init__()
self.image = pg.image.load('MONKEY.png')
self.image = pg.transform.scale(self.image, (80, 80)).convert()
self.rect = self.image.get_rect()
self.rect.center = x, y
self.mask = pg.mask.from_surface(self.image)
def main():
width, height = 1024, 768
hbox, vbox = 80, 80
w, h = 640, 240
screen = pg.display.set_mode((width, height))
BG = pg.image.load('jungle.jpg').convert()
clock = pg.time.Clock()
Score = 0
player = Player(w//2, h//2)
all_sprites = pg.sprite.Group(player)
circles = pg.sprite.Group()
for i in range(20):
circle = Circle()
circles.add(circle)
all_sprites.add(circle)
def checkIntersection(c1, c2):
dx = c1.pos[0] - c2.pos[0]
dy = c1.pos[1] - c2.pos[1]
d = math.hypot(dx, dy)
if d < c1.radius + c2.radius:
return True
return False
c = circles.sprites()
for i in range(19):
while checkIntersection(c[i], c[i + 1]):
c[i].pos = random.randint(0, 1000), random.randint(0, 700)
velocity = (0, 0)
done = False
while not done:
for event in pg.event.get():
if event.type == pg.QUIT:
done = True
keys = pg.key.get_pressed()
# booster
move = 8 if keys[pg.K_LSHIFT] else 4
if keys[pg.K_a]: #to move left
player.rect.x -= move
if player.rect.x < 0 : player.rect.x = 0
if keys[pg.K_d]: #to move right
player.rect.x += move
if player.rect.x > width-hbox : player.rect.x = width - hbox
Score += 1
player.rect.y += 2.5
screen.blit(BG, (0,0))
pg.draw.rect(screen, RED, player.rect, 1)
pg.draw.polygon(screen, GREEN, ((1024,768), (0,768), (0,640),(1024,640)))
font = pg.font.SysFont("comicsansms", 45)
text = font.render (" " + str(Score), 1, BLACK)
screen.blit(text,(480,700))
pg.event.get()
all_sprites.draw(screen)
pg.display.update()
clock.tick(30)
if pg.sprite.spritecollide(player, circles, False, collided = pg.sprite.collide_mask):
done = True
if __name__ == '__main__':
pg.init()
main()
pg.quit()
sys.exit()

I can't move sprites in pygame

It's the first time I create a game with pygame and I find a logical error. This game consist in a sort of puzzle and to play it you have to click to the piece of puzzle then click on the area where you want it to move. When I try to run my code, sprites doesn't move. I tried to modify the function move(), but it didn't change anything. When I run the program and I selected and move the pieces of puzzle of the game they are drawing again by the computer but in the same position. They aren't moving at the coordinates that I write in the code.
import pygame
from pygame.locals import *
import sys
import os
width = 1100
hight = 700
square1 = (240, 45)
square2 = (25, 55)
square3 = (25, 245)
square4 = (248, 245)
coords1 = [800, 50]
coords2 = [550, 50]
coords3 = [550, 250]
coords4 = [810, 250]
pygame.init()
screen = pygame.display.set_mode((width, hight))
pygame.display.set_caption('Hidden Boys')
background = pygame.Surface(screen.get_size())
background = background.convert()
background.fill((250, 250, 250))
class Puzzle_piece(pygame.sprite.Sprite):
def __init__(self, image_file, location):
pygame.sprite.Sprite.__init__(self)
self.counter = 0
self.image = pygame.image.load(image_file)
self.rect = self.image.get_rect()
self.rect.left, self.rect.top = location
self.state = "still"
self.area = screen.get_rect()
self.angle = 0
self.movepos = [0, 0]
def updates(self, position):
self.rect = position
def move(self):
screen.blit(self.image, self.rect)
def rotate_right(self):
self.angle += -90
self.image = pg.transform.rotozoom(self.orig_image, self.angle, 1)
self.rect = self.image.get_rect(center = self.rect.center)
class Frame(pygame.sprite.Sprite):
def __init__(self, image_file = None, location = None, dim = screen, color = (250, 250, 250)):
pygame.sprite.Sprite.__init__(self) #call Sprite initializer
if image_file and location != None :
self.image = pygame.image.load(image_file)
self.rect = self.image.get_rect()
self.rect.left, self.rect.top = location
self.frame = pygame.Surface(dim.get_size())
self.frame = self.frame.convert()
self.frame.fill(color)
def create_text(dim, blitted, color = (0, 0, 0), font = "", msg = "", position = (width, hight), alias = True):
font = pygame.font.SysFont(font, dim)
text = font.render(msg, alias, color)
textpos = text.get_rect()
textpos.center = position
blitted.blit(text, textpos)
def create_frames(name = "frame0", dim = screen, color = (250, 250, 250), image ="" ):
name = pygame.Surface(dim.get_size())
name = name.convert()
name.fill(color)
return name, img
def blit_frames(frame, coords = (0, 0), image = None, image_rect = None):
if image and image_rect != None :
frame.blit(image, image_rect)
screen.blit(frame, coords)
pygame.display.update()
pygame.display.flip()
def draw_sprites(sprites, frame, group):
for x in range(len(sprites)):
screen.blit(frame, sprites[x].rect, sprites[x].rect)
sprites[x].image = pygame.transform.scale(sprites[x].image, (272, 272))
group.update()
group.draw(screen)
pygame.display.flip()
background = Frame()
Frame1 = Frame("images\\field1.png", [-20, 0])
Piece1 = Puzzle_piece("images\\piece11.png", coords1)
Piece2 = Puzzle_piece("images\\piece21.png", coords2)
Piece3 = Puzzle_piece("images\\piece31.png", coords3)
Piece4 = Puzzle_piece("images\\piece41.png", coords4)
sprites_group = [Piece1, Piece2, Piece3, Piece4]
Piece_sprites = pygame.sprite.RenderPlain(sprites_group)
create_text(font = "squared_display", dim = 120, msg = "HIDDEN BOYS", position = (width/2, hight/4), blitted = background.frame)
create_text(font = "squared_display", dim = 50, msg = "Click to start", position = (width/2, hight/2), blitted = background.frame)
def move_pieces(piece):
for event in pygame.event.get():
if pygame.mouse.get_pos() in square1:
piece.updates(square1)
piece.move()
if pygame.mouse.get_pos() in square2:
piece.updates(square2)
piece.move()
if pygame.mouse.get_pos() in square3:
piece.updates(square3)
piece.move()
if pygame.mouse.get_pos() in square4:
piece.updates(square4)
piece.move()
screen.blit(background.frame, (0, 0))
pygame.display.flip()
def main():
clock = pygame.time.Clock()
while True:
frame = 0
counter = 0
clock.tick(60)
for event in pygame.event.get():
if event.type == QUIT:
return
if event.type == pygame.MOUSEBUTTONDOWN:
if frame == 0:
blit_frames(Frame1.frame, Frame1.rect, image = Frame1.image, image_rect = Frame1.rect)
draw_sprites(sprites_group, Frame1.frame, Piece_sprites)
frame += 1
elif frame == 1:
if pygame.mouse.get_pos() in coords1:
Piece1.state = "selected"
Piece1.counter += 1
move_pieces(Piece1)
if pygame.mouse.get_pos() in coords2:
Piece2.state = "selected"
Piece2.counter += 1
move_pieces(Piece2)
if pygame.mouse.get_pos() in coords3:
Piece3.state = "selected"
Piece3.counter += 1
move_pieces(Piece3)
if pygame.mouse.get_pos() in coords4:
Piece4.state = "selected"
Piece4.counter += 1
move_pieces(Piece4)
if event.type == pygame.MOUSEBUTTONUP:
if pygame.mouse.get_pos() in coords1 or coords2 or coords3 or coords4:
if Piece1.counter % 2 != 0:
Piece1.state = "unselected"
if Piece2.counter % 2 != 0:
Piece2.state = "unselected"
if Piece3.counter % 2 != 0:
Piece3.state = "unselected"
if Piece4.counter % 2 != 0:
Piece4.state = "unselected"
pygame.display.update()
pygame.display.flip()
if __name__ == '__main__': main()
Your updates method assigns the the tuple position to the rect object. Instead, try:
def updates(self, position):
self.rect.x, self.rect.y = position

Categories

Resources