You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Pygame制作街霸风格游戏时音效失真问题求助

音效失真问题解决方法

你的音效失真核心原因是重复叠加播放,同时缺少对pygame音频系统的合理初始化配置,具体问题和修复方案如下:

问题根源

  1. 局部flag无效:attack和draw_healthbar方法里的flag是局部变量,每帧调用方法时都会重新初始化为0,导致按住攻击键或角色生命值归零时,音效每秒被调用60次(和帧率一致),大量叠加播放造成失真。
  2. 音频初始化参数缺失:未指定pygame mixer的采样率、声道数等参数,可能导致音效解码时出现格式不匹配的问题。

修复步骤

1. 初始化pygame音频系统并配置参数

在pygame.init()后添加音频初始化代码,匹配常见音效格式:

pygame.mixer.init(frequency=44100, size=-16, channels=2, buffer=4096)

2. 将局部播放控制变量改为实例变量

在Fighter类的__init__方法中添加用于控制音效播放的标记:

self.punch_played = False
self.ko_played = False

3. 修改攻击音效播放逻辑

在attack方法中使用实例变量控制音效仅播放一次,直到按键抬起:

def attack(self, type_):
    if type_ == "punch":
        if self.dir_ == "right":
            if not self.punch_played:
                self.punch.play()
                self.punch_played = True
            self.attack_.center = (self.base.topright[0] + 35, self.base.centery - 10)
            pygame.draw.rect(self.win, self.color, self.attack_)
        elif self.dir_ == "left":
            if not self.punch_played:
                self.punch.play()
                self.punch_played = True
            self.attack_.center = (self.base.topleft[0] - 35, self.base.centery - 10)
            pygame.draw.rect(self.win, self.color, self.attack_)

同时在Game类的按键抬起事件中重置攻击音效标记:

if event.type == pygame.KEYUP:
    if event.key == pygame.K_e:
        self.fighter.attack_.y = 1000
        self.attackFlag1 = 0
        self.fighter.punch_played = False  # 重置攻击音效标记
if event.type == pygame.KEYUP:
    if event.key == pygame.K_RSHIFT:
        self.fighter2.attack_.y = 1000
        self.attackFlag2 = 0
        self.fighter2.punch_played = False  # 重置攻击音效标记

4. 修改KO音效播放逻辑

在draw_healthbar方法中使用实例变量控制KO音效仅播放一次:

def draw_healthbar(self):
    if self.health <= 0:
        if not self.ko_played:
            self.KO.play()
            self.ko_played = True
        # 重置生命值同时重置KO标记,方便下次阵亡播放
        self.health = 100
        self.ko_played = False
    self.healthbar.width = self.health * 3
    pygame.draw.rect(self.win, colors["red"], self.background)
    pygame.draw.rect(self.win, colors["green"], self.healthbar)

完整修改后的代码

import pygame

pygame.init()
# 初始化音频系统,配置参数匹配音效格式
pygame.mixer.init(frequency=44100, size=-16, channels=2, buffer=4096)

colors = {
    "white": (255, 255, 255),
    "black": (0, 0, 0),
    "blue": (71, 52, 237),
    "green": (53, 189, 71),
    "brown": (79, 21, 21),
    "red": (184, 39, 39),
    "purple": (145, 27, 209)
}

class Fighter:
    def __init__(self, win, x, y, win_width, win_height, color, healthx, healthy):
        self.win = win
        self.gravity = 0
        self.win_width = win_width
        self.win_height = win_height
        self.color = color
        self.health = 100
        self.healthx = healthx
        self.healthy = healthy
        self.dir_ = "right"
        self.x = x
        self.y = y

        self.base = pygame.Rect(0, 0, 70, 100)
        self.base.centerx = self.x
        self.base.centery = self.y

        self.attack_ = pygame.Rect(0, 0, 20, 20)
        self.healthbar = pygame.Rect(0, 0, self.health * 3, 20)
        self.healthbar.center = (self.healthx, self.healthy)

        self.background = pygame.Rect(0, 0, 300, 20)
        self.background.center = self.healthbar.center

        self.punch = pygame.mixer.Sound("sounds/attack.wav")
        self.punch.set_volume(0.3)

        self.KO = pygame.mixer.Sound("sounds/KO.mp3")
        self.KO.set_volume(0.3)

        # 添加音效播放控制标记
        self.punch_played = False
        self.ko_played = False

    def render(self):
        pygame.draw.ellipse(self.win, self.color, self.base)
        self.x = self.base.centerx
        self.y = self.base.centery

    def move(self, x, y):
        self.base.centerx = x
        self.base.centery = y

    def fall(self):
        self.base.y += self.gravity
        self.gravity += 1

        if self.base.bottom >= self.win_height - 50:
            self.gravity = 0
            self.base.bottom = self.win_height - 50

    def draw_healthbar(self):
        if self.health <= 0:
            if not self.ko_played:
                self.KO.play()
                self.ko_played = True
            # 重置生命值,同时重置KO标记
            self.health = 100
            self.ko_played = False
        
        self.healthbar.width = self.health * 3
        pygame.draw.rect(self.win, colors["red"], self.background)
        pygame.draw.rect(self.win, colors["green"], self.healthbar)

    def attack(self, type_):
        if type_ == "punch":
            if self.dir_ == "right":
                if not self.punch_played:
                    self.punch.play()
                    self.punch_played = True
                self.attack_.center = (self.base.topright[0] + 35, self.base.centery - 10)
                pygame.draw.rect(self.win, self.color, self.attack_)

            elif self.dir_ == "left":
                if not self.punch_played:
                    self.punch.play()
                    self.punch_played = True
                self.attack_.center = (self.base.topleft[0] - 35, self.base.centery - 10)
                pygame.draw.rect(self.win, self.color, self.attack_)


class Background:
    def __init__(self, win, win_width, win_height):
        self.win = win
        self.win_width = win_width
        self.win_height = win_height

        self.sky = (self.win_width, self.win_height - 100)
        self.ground = (self.win_width, 100)
        self.platform = (220, 30)

        self.platformTop = (220, 5)
        self.platformBottom = (220, 5)

        self.sky_base = pygame.Rect(0, 0, *self.sky)
        self.ground_base = pygame.Rect(0, 0, *self.ground)
        self.ground_base.bottom = self.win_height

        self.platform_base = pygame.Rect(0, 0, *self.platform)
        self.platform_base.center = (self.win_width / 2, self.win_height / 2 + 15)

        self.platform_top = pygame.Rect(0, 0, *self.platformTop)
        self.platform_top.center = (self.platform_base.centerx, self.platform_base.top + self.platformBottom[1])

        self.platform_bottom = pygame.Rect(0, 0, *self.platformBottom)
        self.platform_bottom.center = (self.platform_base.centerx, self.platform_base.bottom - self.platformBottom[1])

    def load(self):
        pygame.draw.rect(self.win, colors["blue"], self.sky_base)
        pygame.draw.rect(self.win, colors["green"], self.ground_base)
        pygame.draw.rect(self.win, colors["brown"], self.platform_base)


class Game:
    def __init__(self):
        self.width = 900
        self.height = 500
        self.running = True
        self.attackFlag1 = 0
        self.attackFlag2 = 0
        self.fps = 60

        self.win = pygame.display.set_mode((self.width, self.height))
        self.clock = pygame.time.Clock()
        self.fighter = Fighter(self.win, 200, 450, self.width, self.height, colors["red"], 200, 50)
        self.fighter2 = Fighter(self.win, 700, 450, self.width, self.height, colors["purple"], 700, 50)
        self.background = Background(self.win, self.width, self.height)

        pygame.display.set_caption("street fighter")

    def fighter_1(self, keys):
        if keys[pygame.K_a]:
            if self.fighter.base.left <= 0:
                self.fighter.x += 10

            self.fighter.dir_ = "left"

            x = self.fighter.x
            self.fighter.move(x - 10, self.fighter.y)

        if keys[pygame.K_d]:
            if self.fighter.base.right >= self.width:
                self.fighter.x -= 10

            self.fighter.dir_ = "right"

            x = self.fighter.x
            self.fighter.move(x + 10, self.fighter.y)

        if keys[pygame.K_w] and self.fighter.base.bottom == 450:
            self.fighter.gravity = -20

        if keys[pygame.K_s] and self.fighter.base.bottom != 450:
                self.fighter.gravity += 10

        if self.background.platform_top.colliderect(self.fighter.base):
            if keys[pygame.K_w]:
                self.fighter.gravity = -20

            else:
                self.fighter.gravity = 0
                self.fighter.base.bottom = self.background.platform_top.bottom

        if self.background.platform_bottom.colliderect(self.fighter.base):
            self.fighter.gravity += 5

        if keys[pygame.K_e]:
            self.fighter.attack("punch")

        if self.fighter.base.colliderect(self.fighter2.attack_):
            if self.attackFlag2 == 0:
                self.fighter.health -= 5
                self.attackFlag2 += 1


    def fighter_2(self, keys):
        if keys[pygame.K_LEFT]:
            if self.fighter2.base.left <= 0:
                self.fighter2.x += 10

            self.fighter2.dir_ = "left"

            x = self.fighter2.x
            self.fighter2.move(x - 10, self.fighter2.y)

        if keys[pygame.K_RIGHT]:
            if self.fighter2.base.right >= self.width:
                self.fighter2.x -= 10

            self.fighter2.dir_ = "right"

            x = self.fighter2.x
            self.fighter2.move(x + 10, self.fighter2.y)

        if keys[pygame.K_UP] and self.fighter2.base.bottom == 450:
            self.fighter2.gravity = -20

        if keys[pygame.K_DOWN] and self.fighter2.base.bottom != 450:
                self.fighter2.gravity += 10

        if self.background.platform_top.colliderect(self.fighter2.base):
            if keys[pygame.K_UP]:
                self.fighter2.gravity = -20

            else:
                self.fighter2.gravity = 0
                self.fighter2.base.bottom = self.background.platform_top.bottom

        if self.background.platform_bottom.colliderect(self.fighter2.base):
            self.fighter2.gravity += 5

        if self.fighter2.base.colliderect(self.fighter.attack_):
            if self.attackFlag1 == 0:
                self.fighter2.health -= 50
                self.attackFlag1 += 1

        if keys[pygame.K_RSHIFT]:
            self.fighter2.attack("punch")


    def run(self):
        while self.running:
            self.clock.tick(self.fps)
            self.background.load()

            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    self.running = False

                if event.type == pygame.KEYUP:
                    if event.key == pygame.K_e:
                        self.fighter.attack_.y = 1000
                        self.attackFlag1 = 0
                        self.fighter.punch_played = False  # 重置攻击音效标记

                if event.type == pygame.KEYUP:
                    if event.key == pygame.K_RSHIFT:
                        self.fighter2.attack_.y = 1000
                        self.attackFlag2 = 0
                        self.fighter2.punch_played = False  # 重置攻击音效标记

            keys = pygame.key.get_pressed()

            self.fighter_1(keys)
            self.fighter.fall()
            self.fighter.draw_healthbar()
            self.fighter.render()

            self.fighter_2(keys)
            self.fighter2.fall()
            self.fighter2.draw_healthbar()
            self.fighter2.render()

            pygame.display.update()

        pygame.quit()

if __name__ == '__main__':
    Game().run()

额外建议

  • 确保音效文件本身无损坏,格式为pygame支持的WAV/MP3等格式。
  • 如果仍有失真,可以调整pygame.mixer.init中的buffer参数(比如改为2048或8192),找到适合的值。

内容的提问来源于stack exchange,提问作者just_a_kid_coder_123

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.24 14:36:20