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

Pygame降落伞游戏二次运行触发Segmentation Fault问题求助

排查Pygame降落伞类游戏重启后的Segmentation Fault问题

我开发的Pygame降落伞类游戏,首次获胜后点击屏幕可重新开始,但第二次运行1-2秒后触发Segmentation Fault。该代码用于编程入门课程,其他人实现同款游戏均无问题,恳请帮忙排查原因。附代码如下:

import sys
import os
import pygame
import random
import math

#Force static position of screen
os.environ ['SDL_VIDEO_CENTERED'] = '1'

#constants
BLACK = (0,0,0)
WHITE = (255, 255, 255)
SHIP_WIDTH = 13
SHIP_HEIGHT = 13
SIZE = WIDTH, HEIGHT = 920, 570
TOP_BUFFER = 50
PILL_HEIGHT = 30
PILL_WIDTH = 10
YELLOW = (157, 185, 45)
RED = (185, 45, 45)
BLUE = (45, 115, 185)
GREEN = (5, 94, 16)

#Runs imported module
pygame.init()

class Text:
    def __init__(self, size, text, color, xpos, ypos):
        self.font = pygame.font.SysFont("Britannic Bold", size)
        self.image = self.font.render(text, 1, color)
        self.rect = self.image.get_rect()
        self.rect = self.rect.move(xpos, ypos)

class Ship(pygame.sprite.Sprite):
    def __init__ (self, x, y, side):
        pygame.sprite.Sprite.__init__(self)
        self.density = SHIP_HEIGHT * SHIP_WIDTH
        self.speed = 10
        self.image = pygame.Surface((math.sqrt(self.density), math.sqrt(self.density))).convert()
        self.rect = self.image.get_rect()
        self.rect = self.rect.move(x, y)
        self.type = side
        self.score = Text(30, str(self.density - 169), BLACK, WIDTH / 4 , HEIGHT/ 17)
        self.score_2 = Text(30, str(self.density - 169), BLACK, WIDTH * .75, HEIGHT / 17)

    def update(self, pill_group):
        key = pygame.key.get_pressed()
        if self.type == "left":
            if key[pygame.K_w]:
                self.rect.y -= self.speed
            if key[pygame.K_s]:
                self.rect.y += self.speed
            if key[pygame.K_a]:
                self.rect.x -= self.speed
            if key[pygame.K_d]:
                self.rect.x += self.speed
            if self.type == "left":
                #Boundary Conditions
                if self.rect.right > WIDTH/2:
                    self.rect.right = WIDTH/2
                if self.rect.left < 0:
                    self.rect.left = 0
                if self.rect.top < 50:
                    self.rect.top = 50
                if self.rect.bottom > 570:
                    self.rect.bottom = 570
        elif self.type == "right":
            if key[pygame.K_UP]:
                self.rect.y -= self.speed
            if key[pygame.K_DOWN]:
                self.rect.y += self.speed
            if key[pygame.K_LEFT]:
                self.rect.x -= self.speed
            if key[pygame.K_RIGHT]:
                self.rect.x += self.speed
                # Ship 2 boundary conditions
                if self.rect.right > WIDTH:
                    self.rect.right = WIDTH
                if self.rect.top < TOP_BUFFER:
                    self.rect.top = TOP_BUFFER
                if self.rect.left < WIDTH/2:
                    self.rect.left = WIDTH/2
                if self.rect.bottom > HEIGHT:
                    self.rect.bottom = HEIGHT
        collisions = pygame.sprite.spritecollide(self, pill_group, True)
        for p in collisions:
            self.density += p.density * 50
            print(self.density)
            self.score.image = self.score.font.render(str(self.density - 169), 1, BLACK)
            self.score_2.image = self.score.font.render(str(self.density), 1, BLACK)
            ##if self.density > 500:
            ##self.play == False
        self.rect.width = self.rect.height = math.sqrt(self.density)
        self.image = pygame.transform.scale(self.image, (self.rect.width, self.rect.height))

class Pill(pygame.sprite.Sprite):
    def __init__(self, xpos, density):
        pygame.sprite.Sprite.__init__(self)
        self.density = density
        self.speed = 3
        self.image = pygame.Surface((PILL_WIDTH, PILL_HEIGHT)).convert()
        self.image.fill(self.set_color())
        self.rect = pygame.Rect(xpos, -PILL_HEIGHT, 10, 30)
        self.rect = self.image.get_rect()
        self.rect = self.rect.move(xpos, HEIGHT/15)

    def set_color(self):
        if self.density == 1:
            return YELLOW
        elif self.density == 2:
            return RED
        elif self.density == 3:
            return BLUE
        elif self.density == 4:
            return GREEN

    def update(self):
        self.rect.y += self.speed
        if self.rect.y > HEIGHT:
            self.kill()

def main():
    #Initialize Local Vars
    size = width, height = 920, 570
    fps = 30
    LeftWins = Text(30, "Left Ship Wins!", BLACK, WIDTH / 2.5, HEIGHT / 2)
    RightWins = Text(30, "Right Ship Wins!", BLACK, WIDTH / 2.5, HEIGHT / 2)
    Winner = Text (30, "Click here to play again", BLACK, WIDTH / 2.6, HEIGHT * .75)
    pygame.display.set_caption('Density')
    screen = pygame.display.set_mode(SIZE, pygame.SRCALPHA)
    clock = pygame.time.Clock()
    play = True
    loop_counter = 0
    vertical = pygame.Surface((1, HEIGHT - TOP_BUFFER)).convert()
    horizontal = pygame.Surface((WIDTH, 1)).convert()

    #Create Game Objects
    ship_left = Ship(WIDTH/4 - SHIP_WIDTH/2, HEIGHT - (4 * SHIP_HEIGHT), "left")
    ship_right = Ship((WIDTH * 3) / 4 - SHIP_WIDTH / 2, HEIGHT - (4 * SHIP_HEIGHT), "right")
    #Create Groups
    ship_group = pygame.sprite.Group()
    ship_group.add(ship_left, ship_right)
    pill_group = pygame.sprite.Group()

    #play = True
    outro = True
    while True:
        #Gameplay
        while play:
            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    sys.exit()
            if loop_counter % 10 == 0:
                pill = Pill(random.randrange(0, (WIDTH/2) - PILL_WIDTH), int(random.choice('1111111111111111111122222334')))
                pill2 = Pill(random.randrange((WIDTH/2) + PILL_WIDTH, WIDTH - PILL_WIDTH), int(random.choice('1111111111111111111122222334')))
                pill_group.add(pill)
                pill_group.add(pill2)
            #Update Groups
            ship_group.update(pill_group)
            pill_group.update()
            #Draw/Blit Groups
            screen.fill(

关键排查点与修复方案

Segmentation Fault在Pygame中大多和资源未正确清理、对象状态残留、非法内存访问有关,结合你的代码和场景,主要问题集中在重启逻辑的资源管理上:

1. 重启时未重新初始化游戏对象(核心问题)

你的ship_left、ship_right、精灵组都是在main()函数开头创建的,属于全局生命周期的对象。首次获胜后,这些对象的状态(比如ship.density的数值、精灵组里的残留对象)并没有被重置,第二次运行时直接复用旧对象,导致后续的缩放、碰撞检测操作访问非法内存区域,触发段错误。

修复方法:
把游戏对象和精灵组的创建代码移到外层while True:循环内部,每次重启都生成全新的对象:

def main():
    #Initialize Local Vars
    size = width, height = 920, 570
    fps = 30
    LeftWins = Text(30, "Left Ship Wins!", BLACK, WIDTH / 2.5, HEIGHT / 2)
    RightWins = Text(30, "Right Ship Wins!", BLACK, WIDTH / 2.5, HEIGHT / 2)
    Winner = Text (30, "Click here to play again", BLACK, WIDTH / 2.6, HEIGHT * .75)
    pygame.display.set_caption('Density')
    screen = pygame.display.set_mode(SIZE, pygame.SRCALPHA)
    clock = pygame.time.Clock()
    vertical = pygame.Surface((1, HEIGHT - TOP_BUFFER)).convert()
    horizontal = pygame.Surface((WIDTH, 1)).convert()

    while True:
        # 每次重启都重新初始化游戏状态
        play = True
        loop_counter = 0
        #Create Game Objects
        ship_left = Ship(WIDTH/4 - SHIP_WIDTH/2, HEIGHT - (4 * SHIP_HEIGHT), "left")
        ship_right = Ship((WIDTH * 3) / 4 - SHIP_WIDTH / 2, HEIGHT - (4 * SHIP_HEIGHT), "right")
        #Create Groups
        ship_group = pygame.sprite.Group()
        ship_group.add(ship_left, ship_right)
        pill_group = pygame.sprite.Group()

        #Gameplay
        while play:
            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    sys.exit()
                # 这里补充获胜后的点击重启逻辑,设置play=False并退出循环
            # ...剩余游戏循环代码

2. 重复缩放导致的内存碎片与图像损坏

在Ship.update()中,你每次都基于当前的self.image进行缩放:

self.image = pygame.transform.scale(self.image, (self.rect.width, self.rect.height))

多次缩放会导致图像质量下降,同时可能引发内存碎片,长期运行(尤其是重启后复用对象)会触发段错误。

修复方法:
保存一个原始的Surface,每次缩放都基于原始图像:

class Ship(pygame.sprite.Sprite):
    def __init__ (self, x, y, side):
        pygame.sprite.Sprite.__init__(self)
        self.density = SHIP_HEIGHT * SHIP_WIDTH
        self.speed = 10
        # 保存原始Surface,避免重复缩放损坏图像
        self.original_image = pygame.Surface((math.sqrt(self.density), math.sqrt(self.density))).convert()
        self.image = self.original_image.copy()
        self.rect = self.image.get_rect()
        self.rect.move_ip(x, y)
        # ...其他初始化代码

    def update(self, pill_group):
        # ...其他更新代码
        # 基于原始图像缩放
        self.rect.width = self.rect.height = math.sqrt(self.density)
        self.image = pygame.transform.scale(self.original_image, (self.rect.width, self.rect.height))

3. 精灵组未正确清理

重启前如果没有清空精灵组,旧的精灵对象可能仍被引用,导致内存泄漏或非法访问。在重启前添加清理逻辑:

# 在每次重启循环开始前清空旧组(如果不移到循环内的话)
ship_group.empty()
pill_group.empty()

4. 检查Pygame版本兼容性

如果上述修复后仍有问题,确认你的Pygame版本和课程中其他人使用的版本一致,旧版本可能存在已知的内存管理bug,建议更新到最新稳定版:

pip install --upgrade pygame

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.29 08:59:12