Shuratt
Shuratt

Reputation: 89

Play sound when objects collide

I'm trying to play the ouch sound when the object ball collides with an angryball, but the sound doesn't always play. It starts on the first collision when the two objects are on the same x or y coordinate but after that it doesn't play correctly for future collisions. Maybe it's my fault on how I manage to handle their coordinates to check for the collision?

import pygame
import os
import random

size = width, height = 750, 422
screen = pygame.display.set_mode(size)

img_path = os.path.join(os.getcwd())
background_image = pygame.image.load('background.jpg').convert()
bg_image_rect = background_image.get_rect()
pygame.mixer.pre_init(44100, 16, 2, 4096)

pygame.display.set_caption("BallGame")

class Ball(object):
    def __init__(self):
        self.image = pygame.image.load("ball.png")
        self.image_rect = self.image.get_rect()
        self.image_rect.x
        self.image_rect.y
        self.facing = 'LEFT'

    def handle_keys(self):
        key = pygame.key.get_pressed()
        dist = 5
        if key[pygame.K_DOWN] and self.image_rect.y < 321:
            self.facing = 'DOWN'
            self.image_rect.y += dist
        elif key[pygame.K_UP] and self.image_rect.y > 0:
            self.facing = 'UP'
            self.image_rect.y -= dist
        if key[pygame.K_RIGHT] and self.image_rect.x < 649:
            self.facing = 'RIGHT'
            self.image_rect.x += dist
        elif key[pygame.K_LEFT] and self.image_rect.x > 0:
            self.facing = 'LEFT'
            self.image_rect.x -= dist

    def draw(self, surface):
        if self.facing == "RIGHT":
            surface.blit(pygame.transform.flip(self.image, True, False),(self.image_rect.x,self.image_rect.y))
        elif self.facing == "DOWN":
            surface.blit(pygame.image.load("ball_down.png"),(self.image_rect.x,self.image_rect.y))
        if self.facing == "UP":
            surface.blit(pygame.image.load("ball_up.png"),(self.image_rect.x,self.image_rect.y))
        elif self.facing == "LEFT":
            surface.blit(self.image,(self.image_rect.x,self.image_rect.y))


mob_images = [pygame.image.load("image1.png").convert_alpha(),pygame.image.load("image2.png").convert_alpha(),pygame.image.load("image3.png").convert_alpha(),pygame.image.load("image4.png").convert_alpha(),pygame.image.load("image5.png").convert_alpha()]

class Angryball(pygame.sprite.Sprite):
    def __init__(self, mob_images, pos_x, pos_y):
        super(Angryball, self).__init__()
        self.mob_images = mob_images
        self.image = random.choice(self.mob_images)
        self.rect = self.image.get_rect(x=pos_x, y=pos_y)
        self.facing = 'LEFT'

    def update(self, screen):
        if self.rect.x <= 0:
            self.rect.right = screen.get_rect().width
            self.rect.top = random.randint(0, screen.get_rect().height)
            self.image = random.choice(self.mob_images)
        else:
            self.rect.move_ip(-5, 0)

pygame.init()
screen = pygame.display.set_mode((750, 422))

ball = Ball()
angryball = Angryball(mob_images , 700, random.randrange(400))
sprites = pygame.sprite.Group()
sprites.add(angryball)

clock = pygame.time.Clock()

pygame.mixer.music.load("bg_music.mp3")
pygame.mixer.music.play(-1, 0.0)
ouch = pygame.mixer.Sound("border_sound.wav")

running = True
while running:
    esc_key = pygame.key.get_pressed()
    for event in pygame.event.get():
        if esc_key[pygame.K_ESCAPE]:
            pygame.display.quit()
            pygame.quit()
            running = False

    if ball.image_rect.x == angryball.rect.x:
        ouch.play()
    if ball.image_rect.y == angryball.rect.y:
        ouch.play()


    ball.handle_keys()

    screen.blit(background_image, bg_image_rect)
    screen.blit(background_image, bg_image_rect.move(bg_image_rect.width, 0))
    bg_image_rect.move_ip(-2, 0)
    if bg_image_rect.right <= 0:
        bg_image_rect.x = 0

    sprites.update(screen)
    sprites.draw(screen)

    ball.draw(screen)
    pygame.display.update()

    clock.tick(60)

Upvotes: 1

Views: 1197

Answers (3)

skrx
skrx

Reputation: 20448

You need to replace these lines,

if ball.image_rect.x == angryball.rect.x:
    ouch.play()
if ball.image_rect.y == angryball.rect.y:
    ouch.play()

with this one (to check if the two rects collide):

if ball.image_rect.colliderect(angryball.rect):
    ouch.play()

The problem now is that the sound will be played every frame in which the two objects collide and therefore can become quite loud (it will be played in up to 8 channels simultaneously). Also, if all channels are occupied, the sound playback can get skipped if several objects collide in short succession.

To prevent this, you could give the Angryball a collided attribute (boolean) and set it to True after the first collision. Then reset it to False after some time interval or at the same time when you reset the position.

if ball.image_rect.colliderect(angryball.rect) and not angryball.collided:
    angryball.collided = True
    ouch.play()

Upvotes: 1

Lasutriv
Lasutriv

Reputation: 43

You could also check for collisions at a specific point such as:

ball.rect.collidepoint(angryball.rect.x, angryball.rect.y)

What it does is test if a point is in fact inside a rect of a sprite. I find that it works nicely, it’s simple, and clear to use and understand.

Upvotes: 0

Shuratt
Shuratt

Reputation: 89

As suggested, i replaced the collision detection with

if ball.image_rect.colliderect(angryball.rect):

and it worked

Upvotes: 0

Related Questions