owltheprogrammer
owltheprogrammer

Reputation: 19

Pygame bug, music will not play properly as it just loops the beginning part

I'm working on a basic shooter game in Pygame (I'm following a tutorial.) I decided to add background music to keep things nice. Code (quite long!):

import pygame
from pygame import image as sprite

pygame.init()

dow = pygame.display.set_mode((1000, 700))

pygame.display.set_caption("PY.Touhou")

clock = pygame.time.Clock()
rightSprites = [sprite.load('ness-right1.png'),
                pygame.image.load('ness-right2.png'),
                pygame.image.load('ness-right3.png'),
                pygame.image.load('ness-right4.png'),
                pygame.image.load('ness-right5.png'),
                pygame.image.load('ness-right6.png'),
                pygame.image.load('ness-right7.png'),
                pygame.image.load('ness-right8.png')
                ]
leftSprites = [
    sprite.load('ness-left1.png'),
    sprite.load('ness-left2.png'),
    sprite.load('ness-left3.png'),
    sprite.load('ness-left4.png'),
    sprite.load('ness-left5.png'),
    sprite.load('ness-left6.png'),
    sprite.load('ness-left7.png'),
    sprite.load('ness-left8.png')
]

scene = pygame.image.load('bg.png')
idle = pygame.image.load('ness-idle.png')


class Player(object):
    def __init__(self, x_pos, y_pos, w, h):
        self.x_pos = x_pos
        self.y_pos = y_pos
        self.w = w
        self.h = h
        self.velocity = 15
        self.JUMPbool = False
        self.atleft = False
        self.atright = False
        self.steps = 0
        self.jumpheight = 12
        self.hitbox = (self.x_pos + 50, self.y_pos, 73, 227)
        self.still = True

    def move(self):
        if self.steps + 1 >= 24:
            self.steps = 0

        if not self.still:

            if self.atleft:
                dow.blit(leftSprites[self.steps // 4], (self.x_pos, self.y_pos))
                self.steps += 1

            elif self.atright:
                dow.blit(rightSprites[self.steps // 4], (self.x_pos, self.y_pos))
                self.steps += 1

        else:
            if self.atright:
                dow.blit(rightSprites[1], (self.x_pos, self.y_pos))
            else:
                dow.blit(leftSprites[1], (self.x_pos, self.y_pos))
        self.hitbox = (self.x_pos + 50, self.y_pos, 73, 227)
        # pygame.draw.rect(dow, (0, 0, 255), self.hitbox, 2)


class IceBullet(object):
    def __init__(self, x_pos, y_pos, radius, color, direction):
        self.x_pos = x_pos
        self.y_pos = y_pos
        self.radius = radius
        self.color = color
        self.direction = direction
        self.velocity = 8 * direction

    def summon(self):
        pygame.draw.circle(dow, self.color, (self.x_pos, self.y_pos), self.radius)


class EnemyCirno(object):
    erightSprites = [sprite.load('e-right%s.png' % pic) for pic in range(1, 9)]
    eleftSprites = [sprite.load('e-left%s.png' % pic) for pic in range(1, 9)]

    def __init__(self, x_pos, y_pos, w, h, ending):
        self.x_pos = x_pos
        self.y_pos = y_pos
        self.w = w
        self.h = h
        self.ending = ending
        self.path = [self.x_pos, self.ending]
        self.hitbox = (self.x_pos + 50, self.y_pos, 73, 227)
        self.steps = 0
        self.health = 20
        self.isAlive = True
        self.velocity = 4

    def summon(self):
        self.move()
        if self.isAlive:
            if self.steps + 1 >= 24:
                self.steps = 0
            if self.velocity > 0:
                dow.blit(self.erightSprites[self.steps // 4], (self.x_pos, self.y_pos))
                self.steps += 1
            else:
                dow.blit(self.eleftSprites[self.steps // 4], (self.x_pos, self.y_pos))
                self.steps += 1
            pygame.draw.rect(dow, (255, 10, 0), (100, 110, 400, 20))
            pygame.draw.rect(dow, (5, 255, 10), (100, 110, 200 - (20 * (10 - self.health)), 20))
            enemy_health = italicStyle.render('Enemy Health', 1, (255, 0, 0))
            dow.blit(enemy_health, (100, 150))
            self.hitbox = (self.x_pos + 50, self.y_pos, 73, 227)
        # pygame.draw.rect(dow, (0, 0, 0), self.hitbox, 2)

    def move(self):
        if self.velocity > 0:
            if self.x_pos + self.velocity < self.path[1]:
                self.x_pos += self.velocity
            else:
                self.velocity = self.velocity * -1
                self.steps = 0
        else:
            if self.x_pos - self.velocity > self.path[0]:
                self.x_pos += self.velocity
            else:
                self.velocity = self.velocity * -1
                self.steps = 0

    def hit(self):
        if self.health > 1:
            self.health -= 0.50
            hit = pygame.mixer.Sound('hit.wav')
            hit.play()
        else:
            self.isAlive = False
            dead = pygame.mixer.Sound('death_sound.wav')
            dead.play()


# Main starting loop.
letterStyle = pygame.font.SysFont('Bookman Old Style', 50)
italicStyle = pygame.font.SysFont('Bookman Old Style', 30, False, True)
cirno = Player(300, 470, 160, 233)
score = 0
evilCirno = EnemyCirno(50, 470, 160, 233, 800)
maxShots = 0
bullets = []
running = True
while running is True:
    clock.tick(24)
    pygame.time.delay(10)
    music = pygame.mixer.music.load('main_theme.wav') # And here is where the bug begins!
    pygame.mixer.music.play()
    for events in pygame.event.get():
        if events.type == pygame.QUIT:
            running = False
    key = pygame.key.get_pressed()


    def update_screen():
        dow.blit(scene, (0, 0))
        textdisplay = letterStyle.render('Score: ' + str(score), 1, (0, 255, 255))
        won = italicStyle.render("Enemy defeated!", 1, (10, 255, 19))
        dow.blit(textdisplay, (50, 30))
        if not evilCirno.isAlive:
            dow.blit(won, (100, 150))
        cirno.move()
        evilCirno.summon()
        for shot in bullets:
            shot.summon()

        pygame.display.update()


    if maxShots > 0:
        maxShots += 1
    if maxShots > 5:
        maxShots = 0
    for shot in bullets:
        if evilCirno.isAlive:
            if shot.y_pos - shot.radius < evilCirno.hitbox[1] + evilCirno.hitbox[3] and shot.y_pos + shot.radius > \
                    evilCirno.hitbox[1]:
                if shot.x_pos + shot.radius > evilCirno.hitbox[0] and shot.x_pos - shot.radius < evilCirno.hitbox[0] + \
                        evilCirno.hitbox[2]:
                    evilCirno.hit()
                    score += 50
                    bullets.pop(bullets.index(shot))

        if 1000 > shot.x_pos > 0:
            shot.x_pos += shot.velocity
        else:
            bullets.pop(bullets.index(shot))

    if key[pygame.K_z] and maxShots == 0:
        shoot = pygame.mixer.Sound('bullet.wav')
        shoot.play()
        if cirno.atleft:
            facing = -1
        else:
            facing = 1
        if len(bullets) < 5:
            bullets.append(
                IceBullet(round(cirno.x_pos + cirno.w // 2), round(cirno.y_pos + cirno.h // 1.2), 20, (0, 0, 255),
                          facing))

        maxShots = 1

    if key[pygame.K_LEFT] and cirno.x_pos > cirno.velocity:  # Will stop ASAP when reached
        cirno.x_pos -= cirno.velocity
        cirno.atleft = True
        cirno.atright = False
        cirno.still = False

    elif key[
        pygame.K_RIGHT] and cirno.x_pos < 1000 - cirno.velocity - cirno.w:  # If it goes past this, it will stop ASAP, as well
        cirno.x_pos += cirno.velocity
        cirno.atright = True
        cirno.atleft = False
        cirno.still = False
    else:
        cirno.steps = 0
        cirno.still = True
    if not cirno.JUMPbool:
        if key[pygame.K_UP]:
            cirno.JUMPbool = True
            cirno.steps = 0

    else:
        if cirno.jumpheight >= -12:  # What to do if pygame.K_SPACE is pressed down.

            cirno.y_pos -= (cirno.jumpheight * abs(cirno.jumpheight)) * 0.5
            cirno.jumpheight -= 1
        else:
            cirno.jumpheight = 12
            cirno.JUMPbool = False

    update_screen()

pygame.quit()

So far, the music actually does play, as well as the sounds. Though the problem is that it just loops the beginning part of the music over and over (which is only the first few seconds), so what you get is just something that sounds like a broken MP3 player. (for reference the music is 1:45. Even weirder is that I've seen people use it with no issues.) Is there any way to fix this bug? Thanks for your help :)

Upvotes: 1

Views: 422

Answers (1)

Kingsley
Kingsley

Reputation: 14906

Do you see the bug here?

while running is True:
    [...]
    music = pygame.mixer.music.load('main_theme.wav')
    pygame.mixer.music.play()

It's re-loading and re-playing replaying the music inside your code main loop. That is every single updated frame of game-play,

Load and play music outside your main loop:

music = pygame.mixer.music.load('main_theme.wav')
pygame.mixer.music.play()

while running is True:
    [...]

Your code can then call the mixer.get_busy() function too see if the music has stopped, and do something about it.

You probably also want to investigate PyGame Sound mixer channels, so your sound effects mix in with the background music properly.

Upvotes: 1

Related Questions