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")
# [...]
Related
This question already has answers here:
How to detect collisions between two rectangular objects or images in pygame
(1 answer)
How do I detect collision in pygame?
(5 answers)
Closed 2 years ago.
I have been trying to create a 2D platformer in pygame, and I have encountered a problem with the collision of the player sprite with the platform. I hope to implement full collision for the platforms, so that the player sprite will stop when it hits the platform from any direction. Currently, the sprite stops when it hits the top of the platforms, but if it collides with the bottom or sides of a platform it instantly jumps to the top of the platform. I have tried various things to solve this, but to no avail. Any help would be appreciated.
I have seperated my code into 3 files.
This is my main file:
import pygame
from settings import *
from sprites import *
#Game Class
class Game:
def __init__(self):
pygame.init()
pygame.mixer.init()
self.gameDisplay = pygame.display.set_mode((displayWidth, displayHeight))
self.clock = pygame.time.Clock()
self.gameRunning = True
#Stars the game
def new(self):
self.allSprites = pygame.sprite.Group()
self.platforms = pygame.sprite.Group()
self.player = Player(self)
self.allSprites.add(self.player)
floor = Platform(0, 680, displayWidth, 40)
plat2 = Platform( 500, 400, 100, 40)
self.allSprites.add(floor, plat2)
self.platforms.add(floor, plat2)
self.run()
#Game Loop
def run(self):
self.gameRunning = True
while self.gameRunning == True:
self.clock.tick(FPS)
self.events()
self.update()
self.draw()
#Updates the screen
def update(self):
self.allSprites.update()
hits = pygame.sprite.spritecollide(self.player, self.platforms, False)
if hits:
self.player.pos.y = hits[0].rect.top
self.player.spd.y = 0
self.player.rect.midbottom = self.player.pos
if self.player.rect.left >= displayHeight - 200:
self.player.pos.x -= abs(self.player.spd.x)
for plat in self.platforms:
plat.rect.x -= abs(self.player.spd.x)
if self.player.rect.right <= displayHeight / 4:
self.player.pos.x += abs(self.player.spd.x)
for plat in self.platforms:
plat.rect.x += abs(self.player.spd.x)
#Events loop
def events(self):
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_UP:
self.player.jump()
#Draws things to the screen
def draw(self):
self.gameDisplay.fill(LIGHT_BLUE)
self.allSprites.draw(self.gameDisplay)
pygame.display.update()
def runGame():
game = Game()
game.new()
runGame()
Sprites file:
import pygame
from settings import *
vec = pygame.math.Vector2
#Player Class
class Player(pygame.sprite.Sprite):
def __init__(self, game):
pygame.sprite.Sprite.__init__(self)
self.game = game
self.image = pygame.Surface([40, 40])
self.image.fill(DARK_BLUE)
self.rect = self.image.get_rect()
self.rect.center = (displayWidth / 2, displayHeight / 2)
self.pos = vec(displayWidth / 2, displayHeight / 2)
self.spd = vec(0,0)
self.acc = vec(0,0)
def update(self):
self.acc = vec(0, playerGrav)
keysPressed = pygame.key.get_pressed()
if keysPressed[pygame.K_LEFT]:
self.acc.x = - playerAcc
if keysPressed[pygame.K_RIGHT]:
self.acc.x = playerAcc
self.acc.x += self.spd.x * playerFric
self.spd += self.acc
self.pos += self.spd + 0.5 * self.acc
self.rect.midbottom = self.pos
def jump(self):
self.rect.y +=1
hits = pygame.sprite.spritecollide(self, self.game.platforms, False)
self.rect.y -= 1
if hits:
self.spd.y = -20
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
Settings File:
import pygame
pygame.font.init()
#Setup
FPS = 60
displayWidth = 1280
displayHeight = 720
pygame.display.set_caption("2D Platformer")
gameDisplay = pygame.display.set_mode((displayWidth, displayHeight))
clock = pygame.time.Clock()
#Player
playerAcc = 0.5
playerFric = -0.1
playerGrav = 0.5
#Colour Pallette
BLACK = (0, 0, 0 )
WHITE = (255, 255, 255)
RED = (200, 0, 0 )
GREEN = (0, 200, 0 )
BLUE = (0, 0, 200)
LIGHT_BLUE = (0, 191, 255)
DARK_BLUE = (0, 50, 150)
#Score
score = 0
This is the code that is responsible for collision:
hits = pygame.sprite.spritecollide(self.player, self.platforms, False)
if hits:
self.player.pos.y = hits[0].rect.top
self.player.spd.y = 0
self.player.rect.midbottom = self.player.pos
I understand that the reason why the sprite jumps to the top of the platform is due to these lines of code:
if hits:
self.player.pos.y = hits[0].rect.top
However, I don't know how to implement full collision.
I am currently trying to develop a small, basic game using Pygame. However, I am not able to draw my sprite object onto the screen. My code is below:
import pygame
# window
WIDTH = 400
HEIGHT = 400
FPS = 60
# colors
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)
YELLOW = (255, 255, 0)
PINK = (255, 0, 191)
BROWN = (102, 51, 0)
GRAY = (102, 102, 51)
# player
class Player(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface((50,50))
self.image.fill(GREEN)
# variables needed for movement
self.rect = self.image.get_rect()
self.rect.centerx = 200
self.rect.bottom = 200
self.speedx = 0
self.speedy = 0
def update(self):
# move sprite
self.speedx = 0
keystate = pygame.key.get_pressed()
if keystate[pygame.K_w]:
self.speedx = -5
if keystate[pygame.K_d]:
self.speedx = 5
self.rect.x += self.speedx
# boundary collision
if self.rect.x < 0:
self.rect.x = 0
elif self.rect.x < 400:
self.rect.x = 400
# initialisation
pygame.init()
# sound
pygame.mixer.init()
# draw window
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Shoot")
clock = pygame.time.Clock()
# sprite group
all_sprites = pygame.sprite.Group()
# create player
player = Player()
# add sprites to sprite group
all_sprites.add(player)
# game loop
running = True
while running:
# keeps speed constant
clock.tick(FPS)
# allows for exit
for event in pygame.event.get():
# check before closing window
if event.type == pygame.QUIT:
running = False
# update sprites
all_sprites.update()
# draw screen
#screen.fill(BLACK)
all_sprites.draw(screen)
#player.draw(screen)
# used to draw
pygame.display.flip()
pygame.quit()
As you can see in the code, I have a player object which I create from the Player class. I then add this to an all_sprites group. This should then be drawn on the screen. However, nothing is drawn
The issue is that the rectangle of the Player object is out of the window, because of if self.rect.x < 400: self.rect.x = 400 in:
class Player(pygame.sprite.Sprite):
# [...]
def update(self):
# [...]
if self.rect.x < 0:
self.rect.x = 0
elif self.rect.x < 400:
self.rect.x = 400
It has to be self.rect.x > 400:
class Player(pygame.sprite.Sprite):
# [...]
def update(self):
# [...]
if self.rect.x < 0:
self.rect.x = 0
elif self.rect.x > 400: # <----
self.rect.x = 400
This question already has an answer here:
pygame.sprite.groupcollide() does not work when trying to implement collision in pygame [duplicate]
(1 answer)
Closed 2 years ago.
I made an enemy class and a player class for a pygame game and I'm trying to figure out how to detect collisions between them. I've tried a lot of different methods, and they don't work, because they need you to use a Rect object and I use a Surface object. Can you tell me what code to add to detect collisions between to Surface Objects? Here is my code:
import pygame
from pygame.locals import *
from sys import exit
import time
import random
pygame.init()
SCREEN_SIZE = (800, 600)
screen = pygame.display.set_mode(SCREEN_SIZE, 0, 32)
background = pygame.Surface(screen.get_size())
background = background.convert()
background.fill((250, 250, 250))
screen.blit(background, (0, 0))
class Box(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self._1 = random.randint(10, 50)
self.image = pygame.Surface((self._1, self._1))
self.image.fill((random.randint(1, 255), random.randint(1, 255), random.randint(1, 255)))
self.rect = self.image.get_rect()
self.dx = random.randint(5, 15)
self.dy = random.randint(5, 15)
self.direction = self.dx, self.dy
def update(self):
pass
class Player(Box):
def __init__(self):
Box.__init__(self)
self.x = 400
self.y = 300
self.image = pygame.Surface((30, 30))
self.image.fill((0, 255, 0))
screen.fill((255, 255, 255))
self.rect.centerx = 400
self.rect.centery = 300
def update(self):
self.rect.centerx = self.x
self.rect.centery = self.y
class Enemy(Box):
def __init__(self):
Box.__init__(self)
self.image = pygame.Surface((30, 30))
self.image.fill((0, 255, 0))
self.image.fill((255, 0, 0))
def update(self):
self.dx, self.dy = self.direction
self.rect.centerx += self.dx
self.rect.centery += self.dy
if self.rect[0] >= 800 or self.rect[0] + self._1 <= 0:
self.direction = -self.dx, self.dy
if self.rect[1] >= 600 or self._1 + self.rect[1] <= 0:
self.direction = self.dx, -self.dy
allSprites = pygame.sprite.Group()
player = Player()
enemy1 = Enemy()
enemy2 = Enemy()
enemy3 = Enemy()
allSprites.add(enemy1)
allSprites.add(enemy2)
allSprites.add(enemy3)
allSprites.add(player)
while True:
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
exit()
if event.type == KEYDOWN:
if event.key == K_LEFT:
player.x -= 10
if event.key == K_RIGHT:
player.x += 10
if event.key == K_UP:
player.y -= 10
if event.key == K_DOWN:
player.y += 10
screen.fill((255, 255, 255))
allSprites.clear(screen, background)
allSprites.update()
allSprites.draw(screen)
pygame.display.flip()
time.sleep(50.0 / 1000.0)
Add the enemies to a pygame.sprite.Group (enemies) and detect if the Sprite player collides with an element from the Group enemies by pygame.sprite.spritecollide(). e.g.:
player = Player()
enemy1 = Enemy()
enemy2 = Enemy()
enemy3 = Enemy()
allSprites = pygame.sprite.Group()
allSprites.add([enemy1, enemy2, enemy3, player])
enemies = pygame.sprite.Group()
enemies.add([enemy1, enemy2, enemy3])
while True:
# [...]
allSprites.update()
if pygame.sprite.spritecollide(player, enemies, False):
print("collide")
# [...]
But note the .rect attributes of the objects Player and Enemy have just a size of 1x1. Ensure that the rectangles have the proper size:
class Player(Box):
def __init__(self):
Box.__init__(self)
self.x = 400
self.y = 300
self.image = pygame.Surface((30, 30))
self.image.fill((0, 255, 0))
screen.fill((255, 255, 255))
self.rect = self.image.get_rect(center=(400, 300)) # <---
class Enemy(Box):
def __init__(self):
Box.__init__(self)
self.image = pygame.Surface((30, 30))
self.rect = self.image.get_rect() # <---
self.image.fill((0, 255, 0))
self.image.fill((255, 0, 0))
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.
I'm making a Flappy Bird clone in PyGame and I'm trying to add a moving background image. I'm using a 800x600 image right now, and for some reason, when I blit the image, the performance suffers terribly. Is there something I'm doing wrong that is taking up a lot of resources?
import pygame, sys
import random
from pygame import *
pygame.init()
red = (255, 0, 0)
white = (255, 255, 255)
black = (0, 0, 0)
blue = (0, 0, 255)
green = (0, 255, 0)
screen = pygame.display.set_mode((800, 600))
pygame.display.set_caption("flap it up")
clock = pygame.time.Clock()
MAX_VEL = 5
GAP = 175
WALLSPEED = -2
WALLEVENT = pygame.USEREVENT+1
SCOREEVENT = pygame.USEREVENT+2
pygame.time.set_timer(WALLEVENT, 5000)
pygame.time.set_timer(SCOREEVENT, 2500)
myfont = pygame.font.SysFont("monospace", 18)
class Player(object):
def __init__(self):
self.rect = pygame.Rect(384, 284, 32, 32)
self.xvel = 0
self.yvel = MAX_VEL
self.xcel = 0
self.ycel = 1
def move(self):
self.rect.top += self.yvel
if self.yvel < MAX_VEL:
self.yvel += self.ycel
else:
self.yvel = MAX_VEL
def jump(self):
self.yvel = -15
def draw(self):
pygame.draw.rect(screen, red, self.rect)
class Wall(object):
def __init__(self, y):
self.rect1 = pygame.Rect(800, y, 32, 600-y)
self.rect2 = pygame.Rect(800, 0, 32, y-GAP)
self.xvel = WALLSPEED
def move(self, walls):
self.rect1.x += self.xvel
self.rect2.x += self.xvel
if self.rect1.x < -31:
walls.remove(self)
def draw(self):
pygame.draw.rect(screen, green, self.rect1)
pygame.draw.rect(screen, green, self.rect2)
class BG(object):
def __init__(self, x):
self.x = x
self.xvel = WALLSPEED
self.img = pygame.image.load("bg.jpg")
def move(self, bg):
self.x += self.xvel
if self.x < -799:
bg.remove(self)
bg.append(BG(self.x+1600))
screen.blit(self.img, (self.x, 0))
def lose():
pygame.quit()
quit()
def main(player, walls, bg, score, playing):
while playing:
for event in pygame.event.get():
if event.type == KEYDOWN:
player.jump()
if event.type == WALLEVENT:
numbo = random.randrange(GAP, 600, 25)
walls.append(Wall(numbo))
if event.type == SCOREEVENT:
score += 1
for b in bg:
b.move(bg)
label = myfont.render("Score: " + str(score), 1, (0, 0, 0))
screen.blit(label, (30, 20))
player.move()
player.draw()
for w in walls:
w.move(walls)
w.draw()
if player.rect.colliderect(w.rect1) or player.rect.colliderect(w.rect2):
lose()
playing = False
clock.tick(60)
pygame.display.update()
player = Player()
walls = []
bg = []
score = 0
walls.append(Wall(300))
bg.append(BG(0))
bg.append(BG(800))
playing = True
main(player, walls, bg, score, playing)
Typically, this should not be a problem. You should try to set the FPS manually and see if there is a difference
# Create a clock
clock = pygame.time.Clock()
# Set FPS (frames per second)
clock.tick(50)