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
相关产品推荐
相关产品推荐

