pygame sprite collision with background elements [duplicate] - python

This question already has answers here:
Pygame mask collision
(1 answer)
How can I made a collision mask?
(1 answer)
Closed 2 years ago.
I've been looking for days to find a solution but any of the other threads could help me.
I've been trying to make the sprite move over the background image. The background have transparent streets that should be the possible paths for the sprite.
I need to detect when the sprite collide with the other part of the background image that is not transparent. i tried perfect collision method but i don't think it's the right solution for my case because the background.rect doesn't make any sense.
I also tried the overlap method but it always return true.
The pictures are 32 bit depth and i'm calling convert_alpha()
class sprites(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.x = 200
self.y = 300
self.img= player_sprite
self.rect = player_sprite.get_rect()
self.mask = pygame.mask.from_surface(player_sprite)
def position(self):
dx = mouse_x-self.x
dy = self.y-mouse_y
d = float((dx**2+dy**2)**0.5)
displ_x = dx/d
displ_y = dy/d
self.x += displ_x
self.y += displ_y
if type(self.mask.overlap(object_background.mask,(0,0))):
self.x -= displ_x
self.y -= displ_y
class object_background_class(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.img = object_background_img
self.rect = object_background_img.get_rect()
self.mask = pygame.mask.from_surface(object_background_img.convert_alpha())
object_background = object_background_class()
player = sprites()
player.position() changes each time the coordinates of the sprite accordind to the mouse(x,y) and check if with the new x,y of the player make it collides with the background
game_state = False
while not game_state:
for e in pygame.event.get():
if e == pygame.QUIT:
game_state = True
if e.type == pygame.KEYDOWN and e.type == pygame.K_ESCAPE:
game_state = True
if e.type == pygame.KEYDOWN:
if e.key == 27:
game_state = True
mouse_x, mouse_y = pygame.mouse.get_pos()
player.position()
DISPLAYSURFACE.blit(color_background, (0, 0))
DISPLAYSURFACE.blit(player.img, (player.x, player.y))
DISPLAYSURFACE.blit(object_background.img, (0, 0))
pygame.display.flip()

The game screen is one big coordinate plane, just say if x, y coords of player go over or under whatever x,y coord threshold than do something

I also tried the overlap method but it always return true.
Of course. pygame.sprite.collide_mask() use the .rect and .mask attribute of the sprite object for the collision detection.
You have to update self.rect after moving the player and changing the self.x and self.y attribute of the player:
class sprites(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.x = 200
self.y = 300
self.img= player_sprite
self.rect = player_sprite.get_rect()
self.mask = pygame.mask.from_surface(player_sprite)
def position(self):
dx = mouse_x-self.x
dy = self.y-mouse_y
d = float((dx**2+dy**2)**0.5)
displ_x = dx/d
displ_y = dy/d
self.x += displ_x
self.y += displ_y
if type(self.mask.overlap(object_background.mask,(0,0))):
self.x -= displ_x
self.y -= displ_y
self.rect.topleft = (round(self.x), round(self.y)) # <--- THIS IS MISSING
Now you can use collide_mask:
if pygame.sprite.collide_mask(player, object_background):
# [...]

Related

Pygame mask collisions not detected [duplicate]

I have made a putt-putt game and now I want to add a slanted wall type. Because of this, I need to use masks for the collision (until now I have just used rects). I have spent hours learning about masks and trying to figure out why my code won't work. There are no errors, the collision just isn't detected.
I have simplified my code down to something much smaller just as a way for me to test it efficiently. From everything I've seen this seems like it should work, but it doesnt. Here it is:
import pygame
# Pygame init stuff
pygame.init()
wind_width = 1200
wind_height = 700
gameDisplay = pygame.display.set_mode((wind_width, wind_height))
pygame.display.set_caption("Mini Golf!")
pygame.display.update()
gameExit = False
clock = pygame.time.Clock()
# Class setups
class Ball:
def __init__(self, x, y):
self.x = x
self.y = y
self.image = pygame.image.load("sball.png")
self.rect = self.image.get_rect()
self.mask = pygame.mask.from_surface(self.image)
def render(self):
self.rect.topleft = (self.x, self.y)
gameDisplay.blit(self.image, self.rect)
class Slant:
def __init__(self, x, y):
self.x = x
self.y = y
self.image = pygame.image.load("posslant.png")
self.rect = self.image.get_rect()
self.mask = pygame.mask.from_surface(self.image)
def render(self):
self.rect.topleft = (self.x, self.y)
gameDisplay.blit(self.image, self.rect)
# Creating objects
ball = Ball(250, 250)
slant = Slant(270, 250)
# Game loop
gameExit = False
while not(gameExit):
# Moves ball
for event in pygame.event.get():
if event.type == pygame.QUIT:
gameExit = True
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_UP:
ball.y -= 1
elif event.key == pygame.K_DOWN:
ball.y += 1
elif event.key == pygame.K_LEFT:
ball.x -= 1
elif event.key == pygame.K_RIGHT:
ball.x += 1
# Collision detection
offset_x, offset_y = (slant.rect.x - ball.rect.x), (slant.rect.y - ball.rect.y)
if slant.mask.overlap(ball.mask, (offset_x, offset_y)):
print("hit")
# Draws everything
gameDisplay.fill((0, 0, 0))
ball.render()
slant.render()
pygame.display.update()
clock.tick(100)
The offset parameter of the method overlap() is the relative position of the othermask in relation to the pygame.mask.Mask object.
So the offset is calculated by subtracting the coordinates of slant from the coordinates of ball:
offset_x, offset_y = (slant.rect.x - ball.rect.x), (slant.rect.y - ball.rect.y)
offset = (ball.rect.x - slant.rect.x), (ball.rect.y - slant.rect.y)
if slant.mask.overlap(ball.mask, offset):
print("hit")
When you create the mask images, then I recommend to ensure that the image has per pixel alpha format by calling .convert_alpha():
class Ball:
def __init__(self, x, y):
self.x = x
self.y = y
self.image = pygame.image.load("sball.png")
self.rect = self.image.get_rect()
self.mask = pygame.mask.from_surface(self.image.convert_alpha()) # <---
class Slant:
def __init__(self, x, y):
self.x = x
self.y = y
self.image = pygame.image.load("posslant.png")
self.rect = self.image.get_rect()
self.mask = pygame.mask.from_surface(self.image.image.convert_alpha()) # <---
Minimal example: repl.it/#Rabbid76/PyGame-SurfaceMaskIntersect
See also: Mask

Pygame sprite rotation changes its collision [duplicate]

I have made a putt-putt game and now I want to add a slanted wall type. Because of this, I need to use masks for the collision (until now I have just used rects). I have spent hours learning about masks and trying to figure out why my code won't work. There are no errors, the collision just isn't detected.
I have simplified my code down to something much smaller just as a way for me to test it efficiently. From everything I've seen this seems like it should work, but it doesnt. Here it is:
import pygame
# Pygame init stuff
pygame.init()
wind_width = 1200
wind_height = 700
gameDisplay = pygame.display.set_mode((wind_width, wind_height))
pygame.display.set_caption("Mini Golf!")
pygame.display.update()
gameExit = False
clock = pygame.time.Clock()
# Class setups
class Ball:
def __init__(self, x, y):
self.x = x
self.y = y
self.image = pygame.image.load("sball.png")
self.rect = self.image.get_rect()
self.mask = pygame.mask.from_surface(self.image)
def render(self):
self.rect.topleft = (self.x, self.y)
gameDisplay.blit(self.image, self.rect)
class Slant:
def __init__(self, x, y):
self.x = x
self.y = y
self.image = pygame.image.load("posslant.png")
self.rect = self.image.get_rect()
self.mask = pygame.mask.from_surface(self.image)
def render(self):
self.rect.topleft = (self.x, self.y)
gameDisplay.blit(self.image, self.rect)
# Creating objects
ball = Ball(250, 250)
slant = Slant(270, 250)
# Game loop
gameExit = False
while not(gameExit):
# Moves ball
for event in pygame.event.get():
if event.type == pygame.QUIT:
gameExit = True
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_UP:
ball.y -= 1
elif event.key == pygame.K_DOWN:
ball.y += 1
elif event.key == pygame.K_LEFT:
ball.x -= 1
elif event.key == pygame.K_RIGHT:
ball.x += 1
# Collision detection
offset_x, offset_y = (slant.rect.x - ball.rect.x), (slant.rect.y - ball.rect.y)
if slant.mask.overlap(ball.mask, (offset_x, offset_y)):
print("hit")
# Draws everything
gameDisplay.fill((0, 0, 0))
ball.render()
slant.render()
pygame.display.update()
clock.tick(100)
The offset parameter of the method overlap() is the relative position of the othermask in relation to the pygame.mask.Mask object.
So the offset is calculated by subtracting the coordinates of slant from the coordinates of ball:
offset_x, offset_y = (slant.rect.x - ball.rect.x), (slant.rect.y - ball.rect.y)
offset = (ball.rect.x - slant.rect.x), (ball.rect.y - slant.rect.y)
if slant.mask.overlap(ball.mask, offset):
print("hit")
When you create the mask images, then I recommend to ensure that the image has per pixel alpha format by calling .convert_alpha():
class Ball:
def __init__(self, x, y):
self.x = x
self.y = y
self.image = pygame.image.load("sball.png")
self.rect = self.image.get_rect()
self.mask = pygame.mask.from_surface(self.image.convert_alpha()) # <---
class Slant:
def __init__(self, x, y):
self.x = x
self.y = y
self.image = pygame.image.load("posslant.png")
self.rect = self.image.get_rect()
self.mask = pygame.mask.from_surface(self.image.image.convert_alpha()) # <---
Minimal example: repl.it/#Rabbid76/PyGame-SurfaceMaskIntersect
See also: Mask

how do i add a detect collision in pygame [duplicate]

This question already has answers here:
How do I detect collision in pygame?
(5 answers)
Closed 1 year ago.
i found this unfinished file in my files and now i need to finish it, only problem is idk really how do i detect collision with the bullet and the player thing and/or the bullet and the enemy thing, when the bullets collide with the enemy it still dies, i just don't remember how.
here's the code ig help thanks
import pygame, os, random,sys
pygame.init()
FPS=60
SCREEN = pygame.display.set_mode((400,500))
pygame.display.set_caption('caption')
x=50
y=450
vel = 3
width = 20
height = 20
class Player(pygame.sprite.Sprite):
def __init__(self,x,y,width,height):
super().__init__()
self.x = x
self.y = y
self.vel = 4
self.image = pygame.Surface((width, height))
self.image.fill((0,0,0))
self.rect = self.image.get_rect(topleft = (x, y))
class B(pygame.sprite.Sprite):
def __init__(self,x,y,radius, color):
super().__init__()
self.x = x
self.y = y
self.color = color
self.radius = radius
self.vel = vel
self.image = pygame.Surface((self.radius * 2, self.radius * 2), pygame.SRCALPHA)
pygame.draw.circle(self.image, self.color, (self.radius, self.radius), self.radius)
self.rect = self.image.get_rect(center = (self.x, self.y))
class Enemy(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
self.image = pygame.Surface((20,20))
self.image.fill((255, 0, 0))
y = random.randrange (0, 480)
x = 400
self.rect = self.image.get_rect(topleft = (x, y))
self.speed = random.randrange(1,2)
player = Player(x, y, width, height)
enemies = pygame.sprite.Group()
bullets = pygame.sprite.Group()
all_sprites = pygame.sprite.Group()
all_sprites.add(player)
score = 0
# main loop
running = True
clock = pygame.time.Clock()
while running:
clock.tick(FPS)
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_SPACE and len(bullets) < 8:
bullet = B(player.rect.centerx, player.rect.centery, 2, (0,0,0))
bullets.add(bullet)
all_sprites.add(bullet)
if len(enemies) < 2:
e = Enemy()
enemies.add(e)
all_sprites.add(e)
for bullet in bullets:
if bullet.rect.right < 500:
bullet.rect.x += bullet.vel
else:
bullet.kill()
for enemy in enemies:
if enemy.rect.right > 0:
enemy.rect.x -= enemy.speed
else:
enemy.kill()
print ("L")
pygame.quit()
sys.exit()
pygame.sprite.groupcollide(bullets, enemies, True, True)
keys = pygame.key.get_pressed()
if keys[pygame.K_UP] and player.rect.top > player.vel:
player.rect.y -= player.vel
if keys[pygame.K_DOWN] and player.rect.bottom < 500 - player.vel:
player.rect.y += player.vel
SCREEN.fill((190, 232, 220))
all_sprites.draw(SCREEN)
pygame.display.update()
pygame.quit()
Collision detection depends on your needs.
Bounding boxes are simple and can detect if the x, y edges of each object are not within one another. This is fine for fast moving games and things where you don't necessarily require point precision.
Distance vectors are also simple and perfect solution for circle collisions. They calculate the difference in distance between two objects according to a radius prescribed with the hypotenuse of distX^2 + distY^2.
Compound bounding objects are harder to calculate, especially for concave areas on objects of arbitrary shape. There are too many notable and novel approaches to collision detection beyond these to remark on here.
They're also increasingly complex depending on things like variability (if they're deformable objects, if they have particle seams, etc and 2d vs 3d object collision can be vastly different worlds as well. There are tons of articles, but I'll post one with implementation here

pygame/python wont detect collision between sprites

im trying to detect a collision between pacman and the boxes, but its not detecting any collision, any help or advice? at the moment ive tried creating a list of instances but that hasnt worked, i dont know what else to do. also its telling me to add more detail but i dont really know what i can add to be honest, sorry
import pygame
import os
import sys
#intialise the game
pygame.init()
screen = pygame.display.set_mode((448, 576))
done = False
y = 320
x = 216
#sets up clock and loads pacman image
clock = pygame.time.Clock()
PACMANSPRITE = pygame.image.load("pacman.png").convert_alpha()
#gets pacman intro music, sets music to lower volume then plays it
pygame.mixer.music.load('pacman_beginning.WAV')
pygame.mixer.music.set_volume(0.01)
pygame.mixer.music.play(0)
#box class, used for boxes to border pacmans map
class boxcollisions(pygame.sprite.Sprite):
def __init__(self, x, y):
self.y = y
self.x = x
self.rect = pygame.Rect(self.x, self.y, 15, 15)
self.color = (0, 128, 255)
def draw(self, screen):
pygame.draw.rect(screen, self.color, self.rect)
#pacmans class
class pacman(pygame.sprite.Sprite):
def __init__(self, image, x, y):
self.image = image
self.y=y
self.x=x
self.rect = self.image.get_rect()
self.rect.left = self.x
self.rect.top = self.y
self.rect.width=16
self.rect.height=16
# move pacman
def movement(self):
pressed= pygame.key.get_pressed()
if pressed[pygame.K_UP]:
self.y -= 2
if pressed[pygame.K_DOWN]:
self.y += 2
if pressed[pygame.K_LEFT]:
self.x -= 2
if pressed[pygame.K_RIGHT]:
self.x += 2
def draw(self, surface):
""" Draw on surface """
# blit yourself at your current position
surface.blit(self.image, (self.x, self.y))
#instances the pacman class
sprite = pacman(PACMANSPRITE, x ,y)
#main game loop
while not done:
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
pygame.quit()
sys.exit()
screen.fill((100,0,0))
#co-ordinates for boxes to set up map boundaries
boundaries=[
[],
[],
[],
[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28],
[1,14,15,28], #5
[1,3,4,5,6,8,9,10,11,12,14,15,17,18,19,20,21,23,24,25,26,28],
[1,3,4,5,6,8,9,10,11,12,14,15,17,18,19,20,21,23,24,25,26,28],
[1,3,4,5,6,8,9,10,11,12,14,15,17,18,19,20,21,23,24,25,26,28],
[1,28],
[1,3,4,5,6,8,9,11,12,13,14,15,16,17,18,20,21,23,24,25,26,28], #10
[1,3,4,5,6,8,9,11,12,13,14,15,16,17,18,20,21,23,24,25,26,28],
[1,8,9,14,15,20,21,28],
[1,2,3,4,5,6,8,9,10,11,12,14,15,17,18,19,20,21,23,24,25,26,27,28],
[1,2,3,4,5,6,8,9,10,11,12,14,15,17,18,19,20,21,23,24,25,26,27,28],
[6,8,9,20,21,23], #15
[6,8,9,11,12,13,14,15,16,17,18,20,21,23],
[1,2,3,4,5,6,8,9,11,12,13,14,15,16,17,18,20,21,23,24,25,26,27,28],
[1,11,12,13,14,15,16,17,18,28],
[1,2,3,4,5,6,8,9,11,12,13,14,15,16,17,18,20,21,23,24,25,26,27,28],
[6,8,9,11,12,13,14,15,16,17,18,20,21,23,24,25,26,27,28], #20
[6,8,9,20,21,23],
[6,8,9,11,12,13,14,15,16,17,18,20,21,23],
[1,2,3,4,5,6,8,9,11,12,13,14,15,16,17,18,20,21,23,24,25,26,27,28],
[1,14,15,28],
[1,3,4,5,6,8,9,10,11,12,14,15,17,18,19,20,21,23,24,25,26,28], #25
[1,3,4,5,6,8,9,10,11,12,14,15,17,18,19,20,21,23,24,25,26,28],
[1,5,6,23,24,28],
[1,2,3,5,6,8,9,11,12,13,14,15,16,17,18,20,21,23,24,26,27,28],
[1,2,3,5,6,8,9,11,12,13,14,15,16,17,18,20,21,23,24,26,27,28],
[1,8,9,14,15,20,21,28], # 30
[1,3,4,5,6,7,8,9,10,11,12,14,15,17,18,19,20,21,22,23,24,25,26,28],
[1,3,4,5,6,7,8,9,10,11,12,14,15,17,18,19,20,21,22,23,24,25,26,28],
[1,28],
[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28],
]
#builds the boxes
bx=0
by=-16
for row in boundaries:
#y co ordinate
by=by+16
for n in row:
#x co ordinate
n=n-1
bx=n*16
box = boxcollisions(bx, by)
box.draw(screen)
#moves pacman
sprite.movement()
sprite.draw(screen)
#tests for collision
print(pygame.sprite.collide_rect(sprite, box))
pygame.display.flip()
clock.tick(60)
1 - You need update the top and left position at moviment method. look:
# move pacman
def movement(self):
pressed= pygame.key.get_pressed()
if pressed[pygame.K_UP]:
self.y -= 2
if pressed[pygame.K_DOWN]:
self.y += 2
if pressed[pygame.K_LEFT]:
self.x -= 2
if pressed[pygame.K_RIGHT]:
self.x += 2
self.rect.left = self.x
self.rect.top = self.y
2 - You have to verificate the collision into loop, for verification with all boxes
for row in boundaries:
#y co ordinate
by=by+16
for n in row:
#x co ordinate
n=n-1
bx=n*16
box = boxcollisions(bx, by)
box_list.append(box)
box.draw(screen)
if pygame.sprite.collide_rect(sprite, box):
print("collided")
Use rect.collidelist to test if pacman is colliding with a wall sprites in your wall sprites list. It will return -1 as long as no collision is detected

Building a keyboard controlled game in pygame. I'm getting an indentation error but I dont know why

This is my game so far. I was able to display the Player and make it move with the keyboard. On a separate program I displayed the Enemy class randomly within the range. When I combined the two programs I started getting a bunch of indentation errors. If I fix one, another one pops ups. Please help!
import pygame
import os
import random
black = (0,0,0)
white = (255,255,255)
red = (255, 0, 0)
green = (0, 100, 0)
# This class represents the bar at the bottom that the player controls
class Player(object):
def __init__(self):
self.image = pygame.image.load("player_one.png").convert()
self.image.set_colorkey(white)
self.width = 15
self.height = 15
self.x = 940
self.y = 240
def handle_keys(self):
key = pygame.key.get_pressed()
if key[pygame.K_DOWN]:
if self.y < 470:
self.y += self.height
elif key[pygame.K_UP]:
if self.y > 0:
self.y -= self.height
if key[pygame.K_RIGHT]:
if self.x < 940:
self.x += self.width
elif key[pygame.K_LEFT]:
if self.x > 0:
self.x -= self.width
def draw(self, surface):
surface.blit(self.image, (self.x, self.y))
class Enemy(object):
def __init__(self):
self.image = pygame.image.load(image).convert()
self.image.set_colorkey(white)
image_rect = image.get_rect()
self.rect = self.image.get_rect()
self.rect.centerx = x
self.rect.centery = y
def draw(self, screen):
surface.blit(self.image, self.rect)
def update(self):
self.rect.topleft = random.randint(60, 220+1), random.randint( 0, 475+1)
class Game():
def __init__(self):
pygame.init()
pygame.display.set_caption('Best Football Game Ever!')
self.screen = pygame.display.set_mode((1000, 500))
self.multi_enemies = []
for i in range(1, 4):
enemy = Enemy("enemy_"+str(i)+".png")
enemy.update()
self.multi_enemies.append(enemy)
def run(self):
clock = pygame.time.Clock()
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
running = False
for enemy in self.multi_enemies:
enemy.update()
#---updates----
# place for updates
# --- draws ---
for enemy in self.multi_enemies:
enemy.draw(self.screen)
for x in range(60,940,35):
pygame.draw.line(screen, white, [x, 0], [x, 500], 1)
player.handle_keys()
self.screen.fill(green)
pygame.display.flip()
clock.tick(20)
pygame.quit()
Game().run()
You are mixing tabs with spaces for indentation. The only way this "works" is if you have your tabstop set to 8
Since nearly everyone uses 4 spaces for indentation, a tab looks like two levels of indentation, but Python only counts it as one
Here tabs are highlighted in yellow:
Edit-Select All
then go to
Format- Untabify
That should fix your problem

Categories