Why is the circle moving in a straight line instead of at an angle in pygame? - python-3.x

I am learning the basics of pygame in my free time at work. I wanted to move the bottom boundary of my program up, but when I changed the boundary collision condition, the ball moves in a straight line instead of at an angle.
When I delete the 525 part of the condition in the bounceCircle definition, it moves as expected. When I place it back, it moves in a horizontal line.
import pygame
import sys
import random
import math
# Initalize the game engine
pygame.init()
# Define common colors:
WHITE = (255, 255, 255)
BLUE = (0, 0, 255)
# Set window size, title, and background color
(width, height) = (900, 600)
screen = pygame.display.set_mode((width, height))
pygame.display.set_caption("Ball Playground")
screen.fill(WHITE)
# Used to manage how fast the screen updates
clock = pygame.time.Clock()
# Ball class
class Particles():
def __init__(self, position, radius):
self.x = position[0]
self.y = position[1]
self.radius = radius
self.color = (BLUE)
# thickness = 0 means filled
# thickness > 0 thicker border
# thickness < 0 nothing
self.thickness = 1
self.speed = 0
self.angle = 0
# Definition for drawing circle
def drawCircle(self):
pygame.draw.circle(screen, self.color, (int(self.x), int(self.y)), self.radius, self.thickness)
# Definition for moving the circle
def moveCircle(self):
self.x += math.sin(self.angle) * self.speed
self.y -= math.cos(self.angle) * self.speed
# Definition for bouncing off of surfaces
def bounceCircle(self):
if (self.x > width - self.radius) or (self.x < self.radius):
self.angle = - self.angle
elif (self.y > height - self.radius) or (self.y < 525 - self.radius):
self.angle = math.pi - self.angle
ball = Particles((450, 300), 40)
ball.speed = 2
ball.angle = random.uniform(0, math.pi*2)
# --------- Main Program Loop ----------
while True:
# --- Main Event Loop
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
pygame.display.update()
screen.fill(WHITE)
#--- Game Logic
ball.moveCircle()
ball.bounceCircle()
ball.drawCircle()
#--- Drawings
pygame.draw.line(screen, BLACK, [0, 525], [900, 525], 2)
# Prints tiny diaginal lines to mark surface
x1 = 0
x2 = 5
for i in range(0, width):
pygame.draw.line(screen, BLACK, [x1, 530], [x2, 525], 2)
x1 += 5
x2 += 5
pygame.display.flip()
clock.tick(60)

You need to edit the bounceCircle function to:
def bounceCircle(self):
if (self.x + self.radius > width ) or (self.x - self.radius < 0):
self.angle = - self.angle
elif (self.y + self.radius > (525)) or (self.y - self.radius < 0):
self.angle = math.pi - self.angle
Whole Code (fixed a few bugs):
import pygame
import sys
import random
import math
# Initalize the game engine
pygame.init()
# Define common colors:
WHITE = (255, 255, 255)
BLUE = (0, 0, 255)
# Set window size, title, and background color
(width, height) = (900, 600)
screen = pygame.display.set_mode((width, height))
pygame.display.set_caption("Ball Playground")
screen.fill(WHITE)
# Used to manage how fast the screen updates
clock = pygame.time.Clock()
# Ball class
class Particles():
def __init__(self, position, radius):
self.x = position[0]
self.y = position[1]
self.radius = radius
self.color = (BLUE)
# thickness = 0 means filled
# thickness > 0 thicker border
# thickness < 0 nothing
self.thickness = 1
self.speed = 0
self.angle = 0
# Definition for drawing circle
def drawCircle(self):
pygame.draw.circle(screen, self.color, (int(self.x), int(self.y)), self.radius, self.thickness)
# Definition for moving the circle
def moveCircle(self):
self.x += math.sin(self.angle) * self.speed
self.y -= math.cos(self.angle) * self.speed
print(self.angle, self.x, self.y)
# Definition for bouncing off of surfaces
def bounceCircle(self):
if (self.x > width - self.radius) or (self.x < self.radius):
self.angle = - self.angle
elif (self.y + self.radius > (height-100)) or (self.y - self.radius < 0):
self.angle = math.pi - self.angle
ball = Particles((450, 300), 40)
ball.speed = 20
ball.angle = random.uniform(0, math.pi*2)
print(ball.angle)
# --------- Main Program Loop ----------
while True:
# --- Main Event Loop
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
pygame.display.update()
screen.fill(WHITE)
#--- Game Logic
ball.moveCircle()
ball.bounceCircle()
ball.drawCircle()
#--- Drawings
pygame.draw.line(screen, BLUE, [0, 525], [900, 525], 2)
# Prints tiny diaginal lines to mark surface
x1 = 0
x2 = 5
for i in range(0, width):
pygame.draw.line(screen, BLUE, [x1, 530], [x2, 525], 2)
x1 += 5
x2 += 5
pygame.display.flip()
clock.tick(60)

Related

Why do my player images "streak" when they go outside the court on my screen?

My players move fine until they move outside they court, until the images streak. Why is this?
I tried changing screen.fill(OUT) to drawing a rectangle the size of the screen.
import pygame
import time
pygame.init()
# Define some colors
BLACK = (0, 0, 0)
OUT = (193, 58, 34)
COURT = (69, 150, 81)
WHITE = (255, 255, 255)
GREEN = (0, 255, 0)
SKIN = (232, 214, 162)
ballspeed = 2
# Create the screen
windowsize = (700, 650)
screen = pygame.display.set_mode(windowsize)
pygame.display.set_caption('Tennis')
# Player Sprites
class Robert(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load("Robert_tennis.png")
self.rect = self.image.get_rect()
self.rect.center = (400, 575)
self.speedx = 0
self.speedy = 0
def update(self):
self.speedx = 0
self.speedy = 0
keystate = pygame.key.get_pressed()
if keystate[pygame.K_LEFT]:
self.speedx = -4
if keystate[pygame.K_RIGHT]:
self.speedx = 4
self.rect.x += self.speedx
if self.rect.right > 700:
self.rect.right = 700
if self.rect.right < 0:
self.rect.left = 0
if keystate[pygame.K_UP]:
self.speedy = -5
if keystate[pygame.K_DOWN]:
self.speedy = 3
self.rect.y += self.speedy
if self.rect.y < 325:
self.rect.y = 325
if self.rect.y < 0:
self.rect.y = 0
class Camden(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load("camden_tennis_front.png")
self.rect = self.image.get_rect()
self.rect.center = (260, 80)
self.speedx = 0
self.speedy = 0
def update(self):
self.speedx = 0
self.speedy = 0
keystate = pygame.key.get_pressed()
if keystate[pygame.K_a]:
self.speedx = -6
if keystate[pygame.K_d]:
self.speedx = 6
self.rect.x += self.speedx
if self.rect.right > 700:
self.rect.right = 700
if self.rect.right < 0:
self.rect.left = 0
if keystate[pygame.K_w]:
self.speedy = -7
if keystate[pygame.K_s]:
self.speedy = 5
self.rect.y += self.speedy
if self.rect.y > 250:
self.rect.y = 250
if self.rect.y < 0:
self.rect.y = 0
class Ball(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load("tennisball.png")
self.rect = self.image.get_rect()
self.rect.center = (420, 450)
self.speedx = 0
self.speedy = 0
def update(self):
#Robert's forehand
if tennisball.rect.colliderect(robert) and tennisball.rect.x > robert.rect.x + 10:
robert.image = pygame.image.load("Robert_tennis2 (1).png")
effect = pygame.mixer.Sound('tennisserve.wav')
effect.play(0)
robert.rect.y -5
self.speedy = -8
self.speedx = 4
#Robert's backhand
if tennisball.rect.colliderect(robert) and tennisball.rect.x < robert.rect.x - 10:
robert.image = pygame.image.load("Robert_tennis2_backhand.png")
effect = pygame.mixer.Sound('tennisserve.wav')
effect.play(0)
robert.rect.y -5
self.speedy = -7
self.speedx = -3
#Camden's forehand
if tennisball.rect.colliderect(camden) and tennisball.rect.x < camden.rect.x -10:
camden.image = pygame.image.load("camden_front_forehand.png")
effect = pygame.mixer.Sound('tennisserve.wav')
effect.play(0)
camden.rect.y -5
self.speedy = 9
self.speedx = 2
keystate = pygame.key.get_pressed()
if keystate[pygame.K_TAB]:
self.speedx = -7
self.speedy = -12
self.speedy = self.speedy * .98
self.speedx = self.speedx * .978
self.rect = self.rect.move(self.speedx, self.speedy)
#Add people
all_sprites = pygame.sprite.Group()
robert = Robert()
camden = Camden()
tennisball = Ball()
all_sprites.add(robert)
all_sprites.add(tennisball)
all_sprites.add(camden)
carryOn = True
clock = pygame.time.Clock()
global score
score = 0
screen.fill(OUT)
while carryOn:
font = pygame.font.Font('freesansbold.ttf', 32)
camden.update()
robert.update()
tennisball.update()
epsilonComp = .1
if abs(tennisball.speedx) < epsilonComp and abs(tennisball.speedy) < epsilonComp:
if tennisball.rect.x != 360 and tennisball.rect.y != 325:
if time.time() > 1000:
score = 15
scorebox = font.render(str(score), True, WHITE, BLACK)
scoreRect = scorebox.get_rect()
scoreRect.center = (625, 50)
screen.blit(scorebox, scoreRect)
for event in pygame.event.get():
if event.type == pygame.QUIT:
carryOn = False
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_x:
carryOn = False
all_sprites.update()
#Fill the screen with a sky color
# Draw the court
pygame.draw.rect(screen, COURT, [175, 75, 350, 500])
#outer left line
pygame.draw.line(screen, WHITE, (175,574), (175,75), 7)
#outer right line
pygame.draw.line(screen, WHITE, (525,574), (525,75), 7)
#top center line
pygame.draw.line(screen, WHITE, (175, 200), (525,200), 7)
#top outer line
pygame.draw.line(screen, WHITE, (175, 78), (525,78), 7)
#bottom outer line
pygame.draw.line(screen, WHITE, (175, 571), (525,571), 7)
#bottom center line
pygame.draw.line(screen, WHITE, (175, 450), (525,450), 7)
#center white line
pygame.draw.line(screen, WHITE, (345,200), (345,450), 7)
#net
pygame.draw.line(screen, BLACK, (175,325), (525,325), 10)
# Update
all_sprites.draw(screen)
pygame.display.update()
clock.tick(60)
pygame.quit()
I just need the player's images to not streak and stay on the screen, and for them to move normally like they do while they're on the court.
I suspect the reason you see this corruption is the code is not clearing away that part of the background.
Each frame (event-loop iteration), the "court" is being re-painted, yet the are outstide this is only painted before the main loop starts.
E.g.:
screen.fill(OUT) # <-- HERE - paint the area outside
while carryOn:
font = pygame.font.Font('freesansbold.ttf', 32)
# ... rest of loop-body
Moving the screen.fill( OUT ) to inside the loop will clear the background too:
font = pygame.font.Font('freesansbold.ttf', 32)
while carryOn:
camden.update()
robert.update()
tennisball.update()
# ... Removed code for the sake of brevity
all_sprites.update()
# ensure the whole screen is cleared
screen.fill(OUT) # <-- HERE!
#Fill the screen with a sky color
# Draw the court
pygame.draw.rect(screen, COURT, [175, 75, 350, 500])
# ... etc
I also moved the font-loading outside the loop, there's no need to load it every frame.

How to compile code into a Class with functions

I don't know how to turn this code into a class statement and then run it!
I want to have the same result as this but using class statements.
import pygame
import random
pygame.init()
winh = 500
winl = 500
win = pygame.display.set_mode((winh, winl))
width = 20
vel = 5
y = 250
x = 250
score = 0
direction = "up"
class Dot():
def __init__(self):
self.x = x
self.y = y
self.width = width
self.direction = 'right'
def direction(self):
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
keys = pygame.key.get_pressed()
if keys[pygame.K_w]:
self.direction = "up"
if keys[pygame.K_a]:
self.direction = "left"
if keys[pygame.K_s]:
self.direction = "down"
if keys[pygame.K_d]:
self.direction = "right"
p = True
run = True
while run:
pygame.time.delay(100)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
keys = pygame.key.get_pressed()
if keys[pygame.K_w]:
direction = "up"
if keys[pygame.K_a]:
direction = "left"
if keys[pygame.K_s]:
direction = "down"
if keys[pygame.K_d]:
direction = "right"
if direction == "up":
y -= width
if direction == "down":
y += width
if direction == "left":
x -= width
if direction == "right":
x += width
if y > winh - 20:
y = 20
if y < 20:
y = winh - 20
if x > winl - 20:
x = 20
if x < 0:
x = winl - 20
win.fill((0,0,0))
dot = pygame.draw.rect(win, (0, 255, 0), (x, y, width, width))
pygame.display.update()
pygame.quit()
So you're class was getting there already. A class is an encapsulation of data and functions that are common to that "object". Anything for or about that object should go into this class. But when code for keyboard handling was put into a Dot class, this was going in the wrong direction. A Dot is a thing draw to a screen, it has a size and colour, and position. It should not be responsible for handling user-input. That's outside the scope of what a Dot is.
When updating the class, I chose to base it on the PyGame sprite class. This is a little bit more work at first, and it needs to be written in a particular way - have an image, and a rect, and (hopefully) an update() function. But being a sprite it gets lots of functionality that's already written, including the PyGame collision routines.
Near the main loop, there's a couple of additions. The first is creating the sprite dotty, and a sprite-group sprites to hold it. It's a bit weird to create a sprite group for a single sprite, but I assumed in the future there would be more than one.
Then in the actual loop, the code calls sprites.update(). This is a handy routine that calls the update() function for every sprite in the group. Then later on, there's a call to sprites.draw( win ) which paints every sprite in the group to the screen. This uses the sprite's image and the rect to know where, and what to paint.
import pygame
import random
pygame.init()
WIN_H = 500
WIN_L = 500
win = pygame.display.set_mode((WIN_H, WIN_L))
width = 20
vel = 5
y = 250
x = 250
score = 0
direction = "up"
class Dot( pygame.sprite.Sprite ):
def __init__( self, x,y, size=20, direction='right', colour=(0,255,0) ):
pygame.sprite.Sprite.__init__( self )
self.size = size
self.colour = colour
self.direction = direction
self.image = pygame.Surface( ( size, size ), pygame.SRCALPHA )
self.rect = self.image.get_rect()
self.rect.x = x
self.rect.y = y
self.image.fill( colour )
def move( self, x_dir, y_dir ):
global WIN_H, WIN_L
# Adjust our co-oridinates
self.rect.x += x_dir
self.rect.y += y_dir
# Stay on the screen, and wrap around
if (self.rect.left >= WIN_L ):
self.rect.right = 0
elif (self.rect.right <= 0 ):
self.rect.left = WIN_L
if (self.rect.top >= WIN_H ):
self.rect.bottom = 0
elif (self.rect.bottom <= 0):
self.rect.top = WIN_H
def update( self ):
# TODO - handle animation, collisions, whatever
pass
# make some sprites
dotty = Dot( x,y )
sprites = pygame.sprite.Group() # a group, for a single sprite (or more)
sprites.add( dotty )
p = True
run = True
while run:
pygame.time.delay(100)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
keys = pygame.key.get_pressed()
if keys[pygame.K_w]:
direction = "up"
if keys[pygame.K_a]:
direction = "left"
if keys[pygame.K_s]:
direction = "down"
if keys[pygame.K_d]:
direction = "right"
if direction == "up":
dotty.move( 0, -width )
if direction == "down":
dotty.move( 0, width )
if direction == "left":
dotty.move( -width, 0 )
if direction == "right":
dotty.move( width, 0 )
# update all the sprites
sprites.update()
# repaint the screen
win.fill((0,0,0))
sprites.draw( win )
#dot = pygame.draw.rect(win, (0, 255, 0), (x, y, width, width))
pygame.display.update()
pygame.quit()
The movement code was moved into the Dot class. This allows the Dot to adjust its position, but also take care of screen-wrapping issues. If this sprite was say, some kind of projectile, maybe when it crossed the screen boundary it would call the sprite.kill() function to remove itself from the sprite group. But since a Dot is clearly not a projectile, it can warp to the other side.

My player sprite keeps on disappearing if it lands on the platform

I am trying to build my first Platformer Game. So far, I have make moving left and right but unfortunately, I have encountered a error that I have not been able to fix when I implemented collisions and gravity. My player keeps on dissolving like spider man if it lands on the platform. The character is still existent, and lands on the platform, but unfortunately, he becomes invisible. There is no error message, and I suspect is has to do with the collision check.
hits = pygame.sprite.spritecollide(object, allPlatforms, False)
if hits:
object.rect.y = hits[0].rect.top + 1
object.vy = 0
print(object.rect.midbottom)
It prints out the Players location in the code, and the player is still existent and movable, but it just doesn't show. Is there something that I did that makes the character vanish?
import pygame
import random
WIDTH = 500
HEIGHT = 400
FPS = 30
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)
playerImage = "blockBandit/BlockBandit.png"
class Player(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface((50, 50))
self.image = pygame.image.load(playerImage).convert()
self.rect = self.image.get_rect()
self.rect.center = (WIDTH / 2, HEIGHT / 2)
self.vx = 0
self.vy = 0
class Platform(pygame.sprite.Sprite):
def __init__(self, x, y, w, h):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface((w, h))
self.image.fill(GREEN)
self.rect = self.image.get_rect()
self.rect.x = x
self.rect.y = y
pygame.init()
pygame.mixer.init()
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Block Bandit")
clock = pygame.time.Clock()
allPlatforms = pygame.sprite.Group()
all_sprites = pygame.sprite.Group()
player = Player()
all_sprites.add(player)
p1 = Platform(0, HEIGHT - 40, WIDTH, 40)
all_sprites.add(p1)
allPlatforms.add(p1)
def moveCharacter(object):
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT]:
object.vx += -2
if keys[pygame.K_RIGHT]:
object.vx += 2
object.vx = object.vx * 0.9
if (abs(object.vx) < 1):
object.vx = 0
if (abs(object.vx) > 10):
if(object.vx < 0):
object.vx = -10
else:
object.vx = 10
object.vy = object.vy + 1
object.rect.x += object.vx
object.rect.y += object.vy
hits = pygame.sprite.spritecollide(object, allPlatforms, False)
if hits:
object.rect.y = hits[0].rect.top + 1
object.vy = 0
print(object.rect.midbottom)
running = True
while running:
clock.tick(FPS)
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
moveCharacter(player)
#Update State
all_sprites.update()
#Render
screen.fill(BLACK)
all_sprites.draw(screen)
#screen.blit(player.icon, (20, 40))
pygame.display.flip()
pygame.quit()
Am I doing something wrong? Thanks!
The y attribute of the rect is the same as the top coordinate, so you're setting the top of the player sprite to the top of the platform sprite here object.rect.y = hits[0].rect.top + 1. And if the platform comes later in the sprite group, it will be blitted after the player and the player won't be visible.
Just change that line to object.rect.bottom = hits[0].rect.top + 1.

Collision between two circle in pygame [duplicate]

This question already has answers here:
Pygame how to let balls collide
(2 answers)
pygame Get the balls to bounce off each other
(1 answer)
Closed 2 years ago.
I am trying to create my first game, where I have successfully added a bouncing ball and another ball around the mouse-pointer. The collision between these two circles works fine except sometimes the circles intersects each other hugely and the collision does not occur in a single frame, and some strange behaviour thereafter. How do I fix this?
import pygame
from pygame.locals import *
import numpy as np
# Define the colors we will use in RGB format
BLACK = ( 0, 0, 0)
WHITE = (255, 255, 255)
BLUE = ( 0, 0, 255)
GREEN = ( 0, 255, 0)
RED = (255, 0, 0)
width = 800
height = 600
radius = 40
mRadius = 60
base = 20
g = 0.2
pygame.init()
screen = pygame.display.set_mode((width,height))
pygame.display.set_caption('Testing')
clock = pygame.time.Clock()
class Circle:
def __init__(self):
self.pos = np.array([100,100])
self.velocity = np.array([2,0])
def upgrade(self,mouse):
self.pos = np.add(self.velocity,self.pos)
if ((self.pos[0] > width - radius - 1) or (self.pos[0] < 0 + radius)):
self.velocity[0] *= -1
if self.pos[1] > height - radius - base - 1:
self.velocity[1] *= -1
#print(str(self.velocity[1])+"\t"+str(self.pos[1]))
else:
self.velocity = np.add(self.velocity,[0,g])
if ((radius + mRadius)**2 >= ((self.pos[0] - mouse[0])**2 +(self.pos[1] - mouse[1])**2)):
self.pos = np.subtract(self.pos,self.velocity)
lx = self.pos[0] - mouse[0]
ly = self.pos[1] - mouse[1]
A = (lx**2 - ly**2)/(lx**2 + ly**2)
B = 2*lx*ly/(lx**2 + ly**2)
M = np.array([[A,B],[B,(-1)*A]])
self.velocity = list((-1)*M.dot(self.velocity))
print(str(lx)+"\t"+str(ly)+"\t"+str(mouse))
pygame.draw.circle(screen, GREEN, [int(self.pos[0]),int(self.pos[1])], radius)
return True
def main():
mouse = [100,100]
running = True
ball = Circle()
while running:
(mouse[0],mouse[1]) = pygame.mouse.get_pos()
screen.fill(WHITE)
pygame.draw.circle(screen, BLUE, mouse, mRadius)
running = ball.upgrade(mouse)
pygame.draw.rect(screen,RED,(0,height-base,width,base))
pygame.display.update()
clock.tick(120)
for event in pygame.event.get():
if event.type == QUIT:
running = False
if event.type == KEYDOWN and event.key == K_ESCAPE:
running = False
pygame.display.quit()
if __name__ == '__main__':
main()
Here is the github link

Progress bar inside a button in Pygame

The desired behavior is:
When user hold the mouse on the button, the dark gray progress bar appears and starts to get incremented at a constant pace. I want to be able to determine how long it will take for it to completely fill (like 2 seconds).
If the mouse move out the button BEFORE the progress bar has reached 100%, the progress bar should go straight to 0%.
If the bar reaches 100%, the program should print something in the terminal.
Here is the code:
import sys
import pygame
import time
from pygame.locals import *
from os import path
pygame.init()
screen = pygame.display.set_mode((900, int(900 * (16 / 9))))
clock = pygame.time.Clock()
BLACK = (0, 0, 0)
GREEN = (0, 255, 0)
WHITE = (255, 255, 255)
BACKGROUND_COLOR = (237, 225, 192)
LIGHT_GRAY = (60, 60, 60)
GRAY = (30, 30, 30)
class Button:
def __init__(self, screen, x, y, w, h, button_color_active, button_color_inactive, text, font, size = 50, text_color = BLACK):
self.screen = screen
self.game_folder = path.dirname(__file__)
self.font = path.join(self.game_folder, font + '.ttf')
self.x, self.y, self.w, self.h = x, y, w, h
self.button_color_active = button_color_active
self.button_color_inactive = button_color_inactive
self.text, self.size = text, size
self.text_color = text_color
self.button_rect = pygame.Rect(self.x, self.y, self.w, self.h)
self.button_font = pygame.font.Font(self.font, self.size)
self.label = self.button_font.render(self.text, 1, self.text_color)
def draw(self):
if self.button_rect.collidepoint(pygame.mouse.get_pos()):
#pygame.draw.rect(self.screen, self.button_color_inactive, self.button_rect)
for progress in range(42):
pygame.draw.rect(screen, LIGHT_GRAY, pygame.Rect(50,600,10*progress,80))
pygame.display.update()
else:
pygame.draw.rect(self.screen, self.button_color_active, self.button_rect)
self.screen.blit(self.label, (self.x + 20, self.y + 5))
def is_clicked(self, mouse_pos):
return bool(self.button_rect.collidepoint(mouse_pos))
def set_new_color(self, active_color, inactive_color):
self.button_color_active = active_color
self.button_color_inactive = inactive_color
button_start = Button(screen, 50, 600, 400, 80, GRAY, LIGHT_GRAY, 'START', 'roboto-black', 50, WHITE)
while True:
screen.fill(BACKGROUND_COLOR)
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
#pygame.draw.rect(screen, GRAY, pygame.Rect(50,600,400,80))
#pygame.draw.rect(screen, LIGHT_GRAY, pygame.Rect(50,600,10*progress,80))
button_start.draw()
pygame.display.flip()
clock.tick(60)
a = str(input('shomething: '))
First you need a timer. You can use the dt (delta time) that pygame.clock.tick(fps) returns to increase a time variable. Do this only if the mouse is hovering over the button, otherwise reset the timer.
To calculate the width of the rect you can do this (proportionality):
width = time * coefficient
Here's a minimal example:
import pygame as pg
pg.init()
screen = pg.display.set_mode((640, 480))
clock = pg.time.Clock()
FONT = pg.font.Font(None, 36)
BACKGROUND_COLOR = (237, 225, 192)
LIGHT_GRAY = (120, 120, 120)
GRAY = (30, 30, 30)
# Button variables.
button_rect = pg.Rect(50, 100, 200, 80)
max_width = 200 # Maximum width of the rect.
max_time = 4 # Time after which the button should be filled.
# Coefficient to calculate the width of the rect for a given time.
coefficient = max_width / max_time
time = 0
dt = 0
done = False
while not done:
for event in pg.event.get():
if event.type == pg.QUIT:
done = True
mouse_pos = pg.mouse.get_pos()
if button_rect.collidepoint(mouse_pos):
# If mouse is over the button, increase the timer.
if time < max_time: # Stop increasing if max_time is reached.
time += dt
if time >= max_time:
time = max_time
else: # If not colliding, reset the time.
time = 0
width = time * coefficient
screen.fill(BACKGROUND_COLOR)
pg.draw.rect(screen, LIGHT_GRAY, (51, 100, width, 80))
pg.draw.rect(screen, GRAY, button_rect, 2)
txt = FONT.render(str(round(time, 2)), True, GRAY)
screen.blit(txt, (20, 20))
pg.display.flip()
dt = clock.tick(60) / 1000
pg.quit()

Resources