pygame/python wont detect collision between sprites - python
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
Related
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
Move Character with Vector
I am teaching myself pygame and am looking at making my character able to rotate and then move in the direction they are facing. I can do the rotation but cannot get the character to move in the direction the image is then facing. The code is on Trinket HERE class Bob(pygame.sprite.Sprite): def __init__(self, color , height , width): super().__init__() self.image = pygame.Surface([width , height]) self.image.fill(BLACK) self.image.set_colorkey(BLACK) #Loading the image for the character self.img = pygame.image.load("char.jfif") #creating a copy of the image self.img_orig = self.img.copy() #defining the starting angle of the character image self.angle = 0 self.velocity = 5 self.rect = self.img_orig.get_rect() def moveLeft(self): self.angle += 1 self.img = pygame.transform.rotate(self.img_orig, self.angle) def moveRight(self): self.rect.x += self.velocity if self.rect.x > 485: self.rect.x = 485 while run: for event in pygame.event.get(): if event.type == pygame.QUIT: run = False keys = pygame.key.get_pressed() if keys[pygame.K_UP]: pSprite.moveForward() if keys[pygame.K_DOWN]: pSprite.moveDown() if keys[pygame.K_LEFT]: pSprite.moveLeft() if keys[pygame.K_RIGHT]: pSprite.moveRight() #---- Game Logic Here #--- Drawing Code Here #Reset the screen to blank screen.fill(BLUE) #Draw Play Area #Draw Sprites screen.blit(pSprite.img,(pSprite.rect.x, pSprite.rect.y))
You can use pygame's Vector2 class instead of calculating the position of your sprite yourself. I also suggest to let the sprite itself handle its movement instead of doing so in the main loop and using a clock for constant framerates and easy control of the speed of your sprites. You also probably want to use an image format with alpha channel (like PNG). Here's a simple example: import pygame class Actor(pygame.sprite.Sprite): def __init__(self, pos, *grps): super().__init__(*grps) self.image = pygame.image.load('char.png').convert_alpha() self.image_org = self.image.copy() self.rect = self.image.get_rect(center=pos) self.pos = pygame.Vector2(pos) self.direction = pygame.Vector2((0, -1)) def update(self, events, dt): pressed = pygame.key.get_pressed() # if a is pressed, rotate left with 360 degress per second if pressed[pygame.K_a]: self.direction.rotate_ip(dt * -360) # if d is pressed, rotate right with 360 degress per second if pressed[pygame.K_d]: self.direction.rotate_ip(dt * 360) # check if should move forward or backward movement = 0 if pressed[pygame.K_w]: movement = 1 if pressed[pygame.K_s]: movement = -1 movement_v = self.direction * movement if movement_v.length() > 0: movement_v.normalize_ip() # move 100px per second in the direction we're facing self.pos += movement_v * dt * 100 # rotate the image self.image = pygame.transform.rotate(self.image_org, self.direction.angle_to((0, -1))) self.rect = self.image.get_rect(center=self.pos) def main(): pygame.init() screen = pygame.display.set_mode((600, 480)) sprites = pygame.sprite.Group() Actor((100, 100), sprites) clock, dt = pygame.time.Clock(), 0 while True: events = pygame.event.get() for e in events: if e.type == pygame.QUIT: return screen.fill('grey') sprites.draw(screen) sprites.update(events, dt) pygame.display.flip() dt = clock.tick(60) / 1000 main() char.png
Rotate the player around its center (see How do I rotate an image around its center using PyGame?): self.angle += 1 self.img = pygame.transform.rotate(self.img_orig, self.angle) self.rect = self.img.get_rect(center = self.rect.center) Use an attribute x and y to store the position of the player with floating point accuracy. class Bob(pygame.sprite.Sprite): def __init__(self, color , height , width): # [...] self.x, self.y = self.rect.center Compute the direction of the player dependent on the angle with the trgonometric function math.sin and math.cos. Change the position attributes and update the rect attribute: self.x += self.velocity * math.cos(math.radians(self.angle + 90)) self.y -= self.velocity * math.sin(math.radians(self.angle + 90)) self.rect.center = round(self.x), round(self.y) The y-axis needs to be reversed (-dy) as the y-axis is generally pointing up, but in the PyGame coordinate system the y-axis is pointing down. In addition, a correction angle must be deducted (+ 90). Since the "angle" is 0 ° when the sprite is looking up, you need to add 90 ° to the angle for the calculation of the direction vector. See also te in pygame while moving with the keys](How to turn the sprite in pygame while moving with the keys. Class Bob: import pygame import math BLACK = (0,0,0) class Bob(pygame.sprite.Sprite): def __init__(self, color , height , width): super().__init__() self.image = pygame.Surface([width , height]) self.image.fill(BLACK) self.image.set_colorkey(BLACK) #Loading the image for the character self.img = pygame.image.load("char.jfif") #creating a copy of the image self.img_orig = self.img.copy() #defining the starting angle of the character image self.angle = 0 self.velocity = 5 self.rect = self.img_orig.get_rect() self.x, self.y = self.rect.center def rotate(self, change_angle): self.angle += change_angle self.img = pygame.transform.rotate(self.img_orig, self.angle) self.rect = self.img.get_rect(center = self.rect.center) def move(self, distance): self.x += distance * math.cos(math.radians(self.angle + 90)) self.y -= distance * math.sin(math.radians(self.angle + 90)) self.rect.center = round(self.x), round(self.y) def moveLeft(self): self.rotate(1) def moveRight(self): self.rotate(-1) def moveForward(self): self.move(self.velocity) def moveDown(self): self.move(-self.velocity) When setting the starting position of the player, you need to set the x, y and rect attribute: pSprite = Bob(WHITE , 25,25) pSprite.rect.x = 50 pSprite.rect.y = 50 pSprite.x, pSprite.y = pSprite.rect.center
Bullet not moving upwards because of OOP
I have a simple shooter game using pygame. I'm having some problems making the bullet's y coordinate slowly increasing up. I know this is something to do with the way I've programmed the Player class even though a bullet Rect is in it. I think I have to change the update function inside it. This is my code: import pygame, random, sys, time pygame.init() #Constants WIDTH = 800 HEIGHT = 500 BLACK = (0, 0, 0) WHITE = (255, 255, 255) # Background Colour RED = (255, 0, 0) GREEN = (0, 255, 0) window = pygame.display.set_mode((WIDTH, HEIGHT)) pygame.display.set_caption("Pygame Shooter Game") clock = pygame.time.Clock() fps = 60 run = True class Player(): def __init__(self, width, colour, x, y): self.width = width self.colour = colour self.x = x self.y = y self.vel = 5 self.shoot = False self.player = pygame.Rect(self.x, self.y, self.width, self.width) self.cartridge = pygame.Rect(0, 0, self.width/2, self.width/2) self.bullet = pygame.Rect(0, 0, 10, 20) self.shoot = False def draw(self, win): self.win = win pygame.draw.rect(self.win, self.colour, self.player) # Draw player(rect) pygame.draw.rect(self.win, GREEN, self.cartridge) #Draw cartridge if self.shoot: pygame.draw.rect(self.win, BLACK, self.bullet) def move(self): keys = pygame.key.get_pressed() if keys[pygame.K_LEFT] and self.x > 0: self.x -= self.vel # We don't do elif cuz we want them to be able to move diagonally if keys[pygame.K_RIGHT] and self.x < WIDTH-self.width: self.x += self.vel if keys[pygame.K_UP] and self.y > 0: self.y -= self.vel if keys[pygame.K_DOWN] and self.y < HEIGHT-self.width: self.y += self.vel if keys[pygame.K_SPACE]: self.shoot = True def update(self): self.player = pygame.Rect(self.x, self.y, self.width, self.width) self.cartridge.midbottom = self.player.midtop self.bullet.midbottom = self.cartridge.midtop if self.shoot: while self.bullet.y > 0: self.bullet.y -= 1 def main(win): run = True player = Player(50, RED, WIDTH/2, HEIGHT/2) while run: win.fill(WHITE) clock.tick(fps) for event in pygame.event.get(): if event.type == pygame.QUIT: run = False player.move() player.update() player.draw(win) pygame.display.update() pygame.quit() sys.exit() main(window) Also, how can I make the create classes for each individual Cartridge and Bullet, to make the whole code more efficient?
update is invoked continuously in the main application loop. Therefore, no additional animation loops are required for the update. Change the loop to a selection (change while to if): while self.bullet.y > 0: if self.bullet.y > 0: self.bullet.y -= 1 The starting position of the bullet must be set when the bullet is shot, rather than continuously when the bullet is updated: class Player(): # [...] def move(self): # [...] if keys[pygame.K_SPACE]: self.shoot = True self.bullet.midbottom = self.cartridge.midtop # <--- INSERT def update(self): self.player = pygame.Rect(self.x, self.y, self.width, self.width) self.cartridge.midbottom = self.player.midtop # self.bullet.midbottom = self.cartridge.midtop <--- DELETE if self.shoot: if self.bullet.y > 0: # <--- if (not while) self.bullet.y -= 1 See also: How can i shoot a bullet with space bar? How do I stop more than 1 bullet firing at once?
pygame sprite collision with background elements [duplicate]
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): # [...]
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