This question already has an answer here:
Why is my collision test always returning 'true' and why is the position of the rectangle of the image always wrong (0, 0)?
(1 answer)
Closed 2 years ago.
Alright so I am trying to just code a simple pong game in python using pygame module and I keep running into an attribute error. I know similar questions have been asked before but I can't figure out the answer from those questions. I'd appreciate any help at all :).
so, the error i am getting in my code is
File "D:/Dev/Documents/Projects/Pong/pong.py", line 81, in <module>
ball.x += ball_speed_x
AttributeError: 'pygame.Surface' object has no attribute 'x'
my code is
import pygame, sys
pygame.init()
clock = pygame.time.Clock()
screen_width = 1280
screen_height = 960
screen = pygame.display.set_mode((screen_width,screen_height))
pygame.display.set_caption('game')
background = pygame.image.load('bg.png')
white = (255, 255, 255)
green = (51, 255, 51)
ball = pygame.image.load('ball.png')
paddle1 = pygame.Rect(screen_width - int(20), screen_height/int(2) - int(70), int(10), int(140))
paddle2 = pygame.Rect(int(10), screen_height/int(2) - int(70), int(10), int(140))
basicfont = pygame.font.SysFont(None, 48)
text = basicfont.render('Pong Game', True, green)
textrect = text.get_rect()
textrect.centerx = screen.get_rect().centerx
ball_speed_x = 7
ball_speed_y = 7
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
screen.fill(white)
screen.blit(background, (0, 0))
screen.blit(text, textrect)
pygame.draw.rect(screen, white, paddle1)
pygame.draw.rect(screen, white, paddle2)
pygame.draw.aaline(screen, green, (screen_width/2, 0), (screen_width/2, screen_height))
pygame.display.flip()
ball.x += ball_speed_x
ball.y += ball_speed_y
if ball.top <= 0 or ball.bottom >= screen_height:
ball_speed_y *= -1
if ball.left <= 0 or ball.right >= screen_width:
ball_speed_x *= -1
clock.tick(60)
Again, any help would be greatly appreciated and I am aware of other similar questions, I'm just new to this language and can't seem to figure this out. Thanks.
A pygame.Surface has no position. A Surface just contains the image.
If you want to draw the ball surface in the window, then you have to blit() the ball at a certain position.
You have to create a pygame.Surface and a pygame.Rect objecct:
ball = pygame.image.load('ball.png')
ball_rect = ball.get_rect(center = (screen_width // 2, screen_height // 2))
Then you can change the location of ball_rect and blit the ball to the screen Surface:
screen.blit(ball, ball_rect)
See the example:
import pygame, sys
pygame.init()
clock = pygame.time.Clock()
screen_width = 1280
screen_height = 960
screen = pygame.display.set_mode((screen_width,screen_height))
pygame.display.set_caption('game')
background = pygame.image.load('bg.png')
white = (255, 255, 255)
green = (51, 255, 51)
ball = pygame.image.load('ball.png')
ball_rect = ball.get_rect(center = (screen_width // 2, screen_height // 2))
paddle1 = pygame.Rect(screen_width - int(20), screen_height/int(2) - int(70), int(10), int(140))
paddle2 = pygame.Rect(int(10), screen_height/int(2) - int(70), int(10), int(140))
basicfont = pygame.font.SysFont(None, 48)
text = basicfont.render('Pong Game', True, green)
textrect = text.get_rect()
textrect.centerx = screen.get_rect().centerx
ball_speed_x = 7
ball_speed_y = 7
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
screen.fill(white)
screen.blit(background, (0, 0))
screen.blit(text, textrect)
pygame.draw.rect(screen, white, paddle1)
pygame.draw.rect(screen, white, paddle2)
pygame.draw.aaline(screen, green, (screen_width/2, 0), (screen_width/2, screen_height))
screen.blit(ball, ball_rect)
pygame.display.flip()
ball_rect.x += ball_speed_x
ball_rect.y += ball_speed_y
if ball_rect.top <= 0 or ball_rect.bottom >= screen_height:
ball_speed_y *= -1
if ball_rect.left <= 0 or ball_rect.right >= screen_width:
ball_speed_x *= -1
clock.tick(60)
Related
So I'm working on a pong copy using pygame. So far everything was working fine with pygame and everything was rendering. I wanted to add the player score to the screen, and I can't get it up. I'm not actually familiar with pygame that much so I pretty muched copied the text code from the docs and other sources. However they would not render, but when I opened a standalone, python file, it worked fine. Here is my main.py file where I render text and a link to the demo using replit: https://replit.com/#Glitchez/Pong-demo?v=1
import pygame
from paddles import *
from controls import *
from ball import *
pygame.font.init()
# SCREEN INIT
pygame.init()
win = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Pong")
# VARIABLES
ball_radius = 7
paddle_speed = 5
paddle_length = 100
FPS = 60
# DEFINE OBJECTS
paddle1 = Paddle(paddle_length, 15, 100, paddle_speed, True, WHITE)
paddle2 = Paddle(paddle_length, 970, 100, paddle_speed, False, WHITE)
ball = Ball(WIDTH // 2, HEIGHT // 2, ball_radius)
font = pygame.font.SysFont(None, 24)
# COLLISION MANAGEMENT
def handle_collision(ball, paddle1, paddle2):
if ball.y + ball.radius >= HEIGHT:
ball.y_vel *= -1
elif ball.y - ball.radius <= 0:
ball.y_vel *= -1
if ball.x_vel < 0:
if paddle1.y <= ball.y <= paddle1.y + paddle1.height:
if ball.x - ball.radius <= paddle1.x + paddle1.width:
ball.x_vel *= -1
middle_y = paddle1.y + paddle1.height / 2
difference_in_y = middle_y - ball.y
reduction_factor = (paddle1.height / 2) / ball.MAX_VEL
y_vel = difference_in_y / reduction_factor
ball.y_vel = -1 * y_vel
else:
if paddle2.y <= ball.y <= paddle2.y + paddle2.height:
if ball.x + ball.radius >= paddle2.x:
ball.x_vel *= -1
middle_y = paddle2.y + paddle2.height / 2
difference_in_y = middle_y - ball.y
reduction_factor = (paddle2.height / 2) / ball.MAX_VEL
y_vel = difference_in_y / reduction_factor
ball.y_vel = -1 * y_vel
# HANDLE ALL GRAPHICS
def graphics(screen):
screen.fill(BLACK)
paddle1.draw(screen)
paddle2.draw(screen)
ball.draw(screen)
# DRAWS DOTTED LINE
for i in range(10, HEIGHT, HEIGHT // 20):
if i % 2 == 1:
continue
pygame.draw.rect(screen, WHITE, (WIDTH // 2 - 5, i, 8, HEIGHT // 40))
# GAME LOOP
def main():
run = True
clock = pygame.time.Clock()
P1_SCORE, P2_SCORE = 0, 0
#ISSUE HERE!!!
my_font = pygame.font.SysFont('Comic Sans MS', 30)
text_surface = my_font.render(str(P1_SCORE), False, (255, 255, 255))
while run:
clock.tick(FPS)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
win.blit(text_surface, (250,250))
paddle1.move()
paddle2.move()
ball.move()
handle_collision(ball, paddle1, paddle2)
P1_SCORE, P2_SCORE = ball.check_win(P1_SCORE, P2_SCORE)
graphics(win)
pygame.display.update()
print(f"player 1: {P1_SCORE}, player 2: {P2_SCORE}")
pygame.quit()
if __name__ == '__main__':
main()
The code is rendering the score correctly, blit()ing it to the window perfectly... but then erasing everything when the screen is painted in graphics().
So, you have a function graphics(), move the score-drawing into there.
# HANDLE ALL GRAPHICS
def graphics(screen, font, p1_score, p2_score):
screen.fill(BLACK)
paddle1.draw(screen)
paddle2.draw(screen)
ball.draw(screen)
# DRAWS DOTTED LINE
for i in range(10, HEIGHT, HEIGHT // 20):
if i % 2 == 1:
continue
pygame.draw.rect(screen, WHITE, (WIDTH // 2 - 5, i, 8, HEIGHT // 40))
# DRAWS THE SCORE
# PLAYER 1 (LEFT)
text_surface = my_font.render(str(p1_score), False, (255, 255, 255))
screen.blit(text_surface, (250,250))
# PLAYER 2 (RIGHT)
text_surface = my_font.render(str(p2_score), False, (255, 255, 255))
screen.blit(text_surface, (WIDTH-WIDTH//4,250)) # 25% from the right-side
# GAME LOOP
def main():
run = True
clock = pygame.time.Clock()
P1_SCORE, P2_SCORE = 0, 0
my_font = pygame.font.SysFont('Comic Sans MS', 30)
while run:
clock.tick(FPS)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
paddle1.move()
paddle2.move()
ball.move()
handle_collision(ball, paddle1, paddle2)
P1_SCORE, P2_SCORE = ball.check_win(P1_SCORE, P2_SCORE)
graphics(win, my_font, P1_SCORE, P2_SCORE)
pygame.display.update()
print(f"player 1: {P1_SCORE}, player 2: {P2_SCORE}")
pygame.quit()
NOTE: This is not tested code, errors and omissions should be expected.
If the text-rendering becomes slow, you could also pre-render all the scores to an array of surfaces on start-up. Then just blit the one that matches the score.
This question already has answers here:
How do I detect collision in pygame?
(5 answers)
Closed 1 year ago.
My problem: I am trying to create a vertical ball drop game, and I am testing for collision, which does work. But how would I reset my ball when it hits the hoop? Instead of it detecting and then hitting the bottom of the screen which results in a Game over screen because you lose. Any help is appreciated.
import time, random
from pygame.locals import *
import pygame, sys
pygame.init()
FPS = 60
FramePerSec = pygame.time.Clock()
BLUE = (0, 0, 255)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
SCREEN_WIDTH = 400
SCREEN_HEIGHT = 600
SCREEN_BOTTOM = 0
SPEED = 5
SCORE = 0
font = pygame.font.SysFont("Verdana", 60)
font_small = pygame.font.SysFont("Verdana", 20)
game_over = font.render("Game Over", True, BLACK)
background = pygame.image.load("background.jpg")
DISPLAYSURF = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
DISPLAYSURF.fill(WHITE)
pygame.display.set_caption("Ball Drop")
class Ball(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
self.image = pygame.image.load("Ball.png")
self.rect = self.image.get_rect()
self.rect.center = (random.randint(40, SCREEN_WIDTH - 40), 0)
def move(self):
global SCORE
self.rect.move_ip(0, SPEED)
if (self.rect.bottom > 600):
self.rect.top = 0
self.rect.center = (random.randint(30, 380), 0)
# Need to check PNG for hit detection
class Basket(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
self.image = pygame.image.load("Basket.png")
self.rect = self.image.get_rect()
self.rect.center = (160, 520)
def move(self):
pressed_keys = pygame.key.get_pressed()
if(self.rect.x >= (SCREEN_WIDTH - 145)):
self.rect.x -= 5;
elif(self.rect.x <= -5):
self.rect.x += 5;
else:
if pressed_keys[pygame.K_a]:
self.rect.move_ip(-SPEED, 0)
if pressed_keys[pygame.K_d]:
self.rect.move_ip(SPEED, 0)
class Wall(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
self.image = pygame.image.load("wall.png")
self.rect = self.image.get_rect()
self.rect.center = (0, 670)
B2 = Basket()
B1 = Ball()
W1 = Wall()
balls = pygame.sprite.Group()
balls.add(B1)
# Need to fix wall sprite group
walls = pygame.sprite.Group()
walls.add(W1)
all_sprites = pygame.sprite.Group()
all_sprites.add(B2)
all_sprites.add(B1)
INC_SPEED = pygame.USEREVENT + 1
pygame.time.set_timer(INC_SPEED, 1000)
while True:
for event in pygame.event.get():
if event.type == INC_SPEED:
SPEED += 0.3
if event.type == QUIT:
pygame.quit()
sys.exit()
DISPLAYSURF.blit(background, (0, 0))
scores = font_small.render(str(SCORE), True, BLACK)
DISPLAYSURF.blit(scores, (10, 10))
for entity in all_sprites:
DISPLAYSURF.blit(entity.image, entity.rect)
entity.move()
# NEed to fix collison and Counting stats
if pygame.sprite.spritecollideany(W1, balls):
DISPLAYSURF.fill(RED)
DISPLAYSURF.blit(game_over, (30, 250))
pygame.display.update()
for entity in all_sprites:
entity.kill()
time.sleep(2)
pygame.quit()
sys.exit()
if pygame.sprite.spritecollideany(B2, balls):
print("Hit")
SCORE += 1
pygame.display.update()
pygame.display.update()
FramePerSec.tick(FPS)
pygame.sprite.spritecollideany() returns the hit Sprite (ball) object. Change the position of this ball:
while True:
# [...]
ball_hit = pygame.sprite.spritecollideany(B2, balls)
if ball_hit:
ball_hit.rect.center = (random.randint(30, 380), 0)
SCORE += 1
print("Hit")
# [...]
I was working on a code to simulate multiple balls moving inside a box using pygame. I wrote the code for a single ball that collides with the walls of the container. I want to increase the number of balls as I want. I looked for help here and got a tutorial here: https://github.com/petercollingridge/code-for-blog/blob/master/pygame%20physics%20simulation/particle_tutorial_3.py
Here he uses class. But for now, I don't want to use a class to simulate this. Is there any other way? Please help me. I am new to python. This is my code:
import pygame
import time
import random
pygame.init()
display_width = 860
display_height = 540
black = (0, 0, 0)
white = (255, 255, 255)
red = (255, 0, 0)
green = (0, 255, 0)
blue = (0, 0, 255)
display_surface = pygame.display.set_mode((display_width, display_height))
pygame.display.set_caption("Simulation")
clock = pygame.time.Clock()
def draw_ball(ball_color, ballx, bally, ballr):
pygame.draw.ellipse(display_surface, ball_color, [int(ballx), int(bally), int(2 *ballr),int(2 * ballr)])
ball_radius = 7
ball_x_vel = 5
ball_y_vel = 5
ball_x_position=0.0
ball_y_position=0.0
number_of_balls=5
ball_x_position =ball_x_position+ random.randrange(0,display_width-2*ball_radius)
ball_y_position = ball_y_position+random.randrange(0,display_height-2*ball_radius)
display_exit = False
while not display_exit:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
quit()
if ball_x_position > display_width - 2 * ball_radius or ball_x_position < 0:
ball_x_vel *= -1
if ball_y_position > display_height - 2 * ball_radius or ball_y_position < 0:
ball_y_vel *= -1
ball_x_position += ball_x_vel
ball_y_position += ball_y_vel
display_surface.fill(white)
draw_ball(red, ball_x_position, ball_y_position, ball_radius)
pygame.display.update()
clock.tick(100)
pygame.quit()
quit()
I think I should use some kind of for loop to create multiple balls for random initial positions and then follow the same boundary conditions.
Edit: After the suggestions:
import pygame
import random
pygame.init()
display_width = 860
display_height = 540
black = (0, 0, 0)
white = (255, 255, 255)
red = (255, 0, 0)
display_surface = pygame.display.set_mode((display_width, display_height))
pygame.display.set_caption("Simulation")
clock = pygame.time.Clock()
def draw_ball(ball_color, ball_specs):
pygame.draw.ellipse(display_surface, ball_color, ball_specs)
ball_x_position = 0
ball_y_position = 0
ball_diameter = 10
number_of_balls = 10
def ball_show(number_of_balls):
for n in range(number_of_balls):
ball_x_position = random.randrange(0, display_width)
ball_y_position = random.randrange(0, display_height)
ball = [ball_x_position, ball_y_position, ball_diameter, ball_diameter]
draw_ball(red, ball)
display_exit = False
while not display_exit:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
quit()
display_surface.fill(white)
ball_show(number_of_balls)
pygame.display.update()
clock.tick(100)
pygame.quit()
quit()
im currently learning to program in python (first language) and im trying to create a clicker game, however, while doing the click function i got this error:
line 73, in <module>
if player.collidepoint(event.pos):
AttributeError: 'Player' object has no attribute 'collidepoint'
it seems that my "player" object (the clickable object) doesnt have a rect ? but after looking it up for hours i could not find an answer
this is my game code
import pygame
from os import path
img_dir = path.join(path.dirname(__file__), "img")
width = 500
height = 600
fps = 30
# Cores
white = (255, 255, 255)
black = (0, 0, 0)
red = (255, 0 ,0)
green = (0, 255, 0)
blue = (0, 0, 255)
yellow = (255, 255, 0)
# Iniciar o game
pygame.init()
pygame.mixer.init()
screen = pygame.display.set_mode((width, height))
pygame.display.set_caption("Trojan Clicker")
clock = pygame.time.Clock()
font_name = pygame.font.match_font("arial")
def draw_text(surf, text, size, x , y):
font = pygame.font.Font(font_name, size)
text_surface = font.render(text, True, white)
text_rect = text_surface.get_rect()
text_rect.midtop = (x, y)
surf.blit(text_surface, text_rect)
class Player(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface((75, 75))
self.image.fill(red)
self.rect = self.image.get_rect()
self.screen_rect = screen.get_rect()
self.rect.centerx = width / 2
self.rect.bottom = height / 2
self.speedx = 0
def update(self):
self.speedx = 0
all_sprites = pygame.sprite.Group()
player = Player()
all_sprites.add(player)
clicks = 0
# Loop
running = True
while running:
# Fps
clock.tick(fps)
# Eventos
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.MOUSEBUTTONDOWN:
# 1 is the left mouse button, 2 is middle, 3 is right.
if event.button == 1:
# `event.pos` is the mouse position.
if player.collidepoint(event.pos):
# Increment the number.
number += 1
# Updates
all_sprites.update()
# Draw / render X
screen.fill(black)
all_sprites.draw(screen)
draw_text(screen, str(clicks), 18, width / 2, 10)
# Depois de desenhar tudo, "flip" o display
pygame.display.flip()
pygame.quit()
Some comments are in portuguese btw, sorry about that
Thanks in advance for everyone who helps
Change the line
if player.collidepoint(event.pos):
to
if player.rect.collidepoint(event.pos):
For my computing project I need to make some sort of app or game. I decided I would make space invaders. I have managed to get the basics working, the sprites would spawn I could move the player, the enemies fell from the top of the screen and the background and music worked properly, but the collision is not working properly. When the corners touch or the midpoint between two corners touch then it works fine, the program closes. But if the corners/midpoints don't touch then the sprites pass right through each other. Any help would be much appreciated. Here is the code :
import pygame
import random
import sys
# --- constants --- (UPPER_CASE names)
WHITE = (255, 255, 255)
BLACK = ( 0, 0, 0)
RED = (255, 0, 0)
GREEN = ( 0, 255, 0)
BLUE = ( 0, 0, 255)
ORANGE = (255, 255, 0)
YELLOW = ( 0, 255, 255)
DISPLAY_WIDTH = 720
DISPLAY_HEIGHT = 720
FPS = 60
# --- classes --- (CamelCase names)
class Player(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load("images\\user1.gif").convert()
self.image = pygame.transform.scale(self.image, (50, 50))
self.rect = self.image.get_rect()
self.speed_x = 0
def update(self):
self.speed_x = 0
keystate = pygame.key.get_pressed()
if keystate[pygame.K_LEFT]:
self.speed_x = -7
if keystate[pygame.K_RIGHT]:
self.speed_x = 7
self.rect.x += self.speed_x
if self.rect.right > DISPLAY_WIDTH:
self.rect.right = DISPLAY_WIDTH
if self.rect.left < 0:
self.rect.left = 0
class Mob(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load("images\\enemy1.gif").convert()
self.image = pygame.transform.scale(self.image, (50, 50))
self.rect = self.image.get_rect()
self.speed_x = 0
self.rect.x = random.randrange(0, DISPLAY_WIDTH - self.rect.width)
self.rect.y = random.randrange(-100, -40)
self.speedy = random.randrange(3, 11)
def update(self):
self.rect.y +=self.speedy
if self.rect.top > DISPLAY_HEIGHT + 10:
self.rect.x = random.randrange(0, DISPLAY_WIDTH - self.rect.width)
self.rect.y = random.randrange(-100, -40)
self.speedy = random.randrange(3, 11)
# --- functions --- (lower_case names)
# empty
# --- main --- (lower_case names)
# - init -
pygame.init()
pygame.mixer.init()
display = pygame.display.set_mode((DISPLAY_WIDTH, DISPLAY_HEIGHT))
display_rect = display.get_rect()
# - objects -
all_sprites = pygame.sprite.Group()
mobs = pygame.sprite.Group()
player = Player()
player.rect.center = ((DISPLAY_WIDTH / 2), DISPLAY_HEIGHT/1.2)
all_sprites.add(player)
for z in range(8):
mob = Mob()
mobs.add(mob)
all_sprites.add(mob)
background = pygame.image.load("images\\background.jpg")
background = pygame.transform.scale(background, (DISPLAY_WIDTH, DISPLAY_HEIGHT))
# - other -
pygame.mixer.music.load("audio\\soundtrack.mp3")
pygame.mixer.music.play(-1)
pygame.mixer.music.set_volume(0.4)
# - mainloop -
crashed = False
clock = pygame.time.Clock()
while not crashed:
# - events -
for event in pygame.event.get():
if event.type == pygame.QUIT:
crashed = True
print(event)
# - checks for a hit -
col = pygame.sprite.collide_rect(mob, player)
if col:
sys.exit()
# - updates (without draws) -
all_sprites.update()
# - draws (without updates) -
display.blit(background, (0, 0))
all_sprites.draw(display)
pygame.display.update()
# - FPS -
clock.tick(FPS)
# - end -
pygame.quit()
Any help would be great, thanks.
The function pygame.sprite.collide_rect is not intended for direct use. It's intended to be passed into the 'main' collide functions
pygame.sprite.spritecollide()
pygame.sprite.groupcollide()
pygame.sprite.spritecollideany()
In your case you want pygame.sprite.spritecollideany:
col = pygame.sprite.spritecollideany(player, mobs)
if col:
sys.exit()
This function will return one in mobs that collided with player.