如何在Pygame中存储对象历史坐标?解决小行星位置同步异常
问题分析与解决方案
嘿,我一眼就看出问题所在啦——你所有的小行星都在共享全局的x和index变量!每次生成新小行星时,这些全局变量会被新的随机值覆盖,旧小行星绘制的时候就会用新的x坐标,自然就“瞬移”到新位置了。咱们来一步步修复这个问题:
核心修复点
1. 让Enemy类拥有自己的x和index属性
修改Enemy的构造函数,把随机生成的x和index作为实例属性保存,而不是依赖全局变量:
class Enemy: def __init__(self, x, y, width, height, index): self.x = x # 把x设为实例属性 self.index = index # 把index设为实例属性 self.width = width self.height = height self.vel = 1.5 self.y = y # 初始化hitbox时用self.x self.hitbox = (self.x, self.y, self.width, self.height)
2. 修改Enemy.draw方法,使用实例自身的属性
把方法里所有的x和index都替换成self.x和self.index,确保每个小行星用自己的坐标和图片索引:
def draw(self, win): win.blit(asteroids[self.index], (self.x, self.y)) if self.index == 0: self.hitbox = (self.x + 68, self.y + 68, self.width - 10, self.height - 14) elif self.index == 1: self.hitbox = (self.x + 38, self.y + 47, self.width + 20, self.height - 5) elif self.index == 2: self.hitbox = (self.x + 18, self.y + 12, self.width + 32, self.height + 30) elif self.index == 3: self.hitbox = (self.x + 20, self.y + 32, self.width + 16, self.height + 5) else: self.hitbox = (self.x + 4, self.y + 7, self.width - 24, self.height - 31) pygame.draw.rect(win, (255, 0, 0), self.hitbox, 2)
3. 生成小行星时传递正确的参数
在定时器事件里,生成Enemy实例时,把随机生成的x和index传递给构造函数,而不是赋值给全局变量:
elif event.type == my_event_id: x = random.randrange(screen_width - 64 * 2) index = random.choice(number) # 把x、index作为参数传给Enemy asteroids_on_screen.append(Enemy(x, rock.y, rock.width, rock.height, index))
完整修正后的代码
import pygame import random pygame.init() screen_width = 500 screen_height = 500 win = pygame.display.set_mode((screen_width, screen_height)) walk_left = [pygame.image.load('sprite_5.png'), pygame.image.load('sprite_6.png')] walk_right = [pygame.image.load('sprite_3.png'), pygame.image.load('sprite_4.png')] standing = [pygame.image.load('sprite_0.png'), pygame.image.load('sprite_1.png'), pygame.image.load('sprite_2.png')] class Player: def __init__(self, x, y, width, height): self.x = x self.y = y self.width = width self.height = height self.vel = 4 self.left = False self.right = False self.standing = True self.walk_count = 0 self.hitbox = (self.x + 2, self.y + 26, 123, 45) def draw(self, win): if self.walk_count + 1 >= 12: self.walk_count = 0 if not self.standing: if self.left: win.blit(walk_left[self.walk_count // 6], (self.x, self.y)) self.walk_count += 1 elif self.right: win.blit(walk_right[self.walk_count // 6], (self.x, self.y)) self.walk_count += 1 else: win.blit(standing[self.walk_count // 4], (self.x, self.y)) self.walk_count += 1 self.hitbox = (self.x + 2, self.y + 31, 123, 40) pygame.draw.rect(win, (255, 0, 0), self.hitbox, 2) def move(): if keys[pygame.K_LEFT] and man.x > man.vel or keys[pygame.K_a] and man.x > man.vel: man.x -= man.vel man.left = True man.right = False man.standing = False elif keys[pygame.K_RIGHT] and man.x < 500 - man.width - man.vel: man.x += man.vel man.left = False man.right = True man.standing = False else: man.standing = True class Enemy: def __init__(self, x, y, width, height, index): self.x = x self.index = index self.width = width self.height = height self.vel = 1.5 self.y = y self.hitbox = (self.x, self.y, self.width, self.height) def draw(self, win): win.blit(asteroids[self.index], (self.x, self.y)) if self.index == 0: self.hitbox = (self.x + 68, self.y + 68, self.width - 10, self.height - 14) elif self.index == 1: self.hitbox = (self.x + 38, self.y + 47, self.width + 20, self.height - 5) elif self.index == 2: self.hitbox = (self.x + 18, self.y + 12, self.width + 32, self.height + 30) elif self.index == 3: self.hitbox = (self.x + 20, self.y + 32, self.width + 16, self.height + 5) else: self.hitbox = (self.x + 4, self.y + 7, self.width - 24, self.height - 31) pygame.draw.rect(win, (255, 0, 0), self.hitbox, 2) my_event_id = pygame.USEREVENT + 1 pygame.time.set_timer(my_event_id, 1000) class Projectile: def __init__(self, x, y, width, height, color): self.x = x self.y = y self.width = width self.height = height self.color = color self.vel = 5 def draw(self, win): pygame.draw.rect(win, self.color, (self.x, self.y, self.height, self.width)) class Unit: def __init__(self): self.last = pygame.time.get_ticks() self.cooldown = 200 def fire(self): now = pygame.time.get_ticks() if now - self.last >= self.cooldown: self.last = now spawn_bullet() def spawn_bullet(): if keys[pygame.K_SPACE]: bullets.append(Projectile((man.x + 126 // 2), (man.y + 5), 7, 3, (255, 0, 0))) def re_draw(): win.fill((0, 0, 0)) man.draw(win) for bullet in bullets: bullet.draw(win) for a in asteroids_on_screen: a.draw(win) pygame.display.update() asteroids = [pygame.image.load('rock0.png'), pygame.image.load('rock1.png'), pygame.image.load('rock2.png'), pygame.image.load('rock3.png'), pygame.image.load('rock4.png')] number = [0, 1, 2, 3, 4] delay = Unit() man = Player(186, 400, 128, 128) bullets = [] asteroids_on_screen = [] rock = Enemy(-140, 64, 64, 0) # 适配新的构造函数参数 run = True clock = pygame.time.Clock() while run: last = pygame.time.get_ticks() for event in pygame.event.get(): if event.type == pygame.QUIT: run = False elif event.type == my_event_id: x = random.randrange(screen_width - 64 * 2) index = random.choice(number) asteroids_on_screen.append(Enemy(x, rock.y, rock.width, rock.height, index)) for a in asteroids_on_screen: if -141 < a.y < 500: a.y += a.vel else: asteroids_on_screen.pop(asteroids_on_screen.index(a)) for bullet in bullets: if 0 < bullet.y < 500: bullet.y -= bullet.vel else: bullets.pop(bullets.index(bullet)) keys = pygame.key.get_pressed() move() delay.fire() clock.tick(60) re_draw() pygame.quit()
额外说明
- 我修改了
rock = Enemy(-140, 64, 64, 0)这一行,因为Enemy的构造函数现在需要5个参数,这里给个临时的0作为index就行,反正这个实例只是用来获取初始y、宽高的。 - 现在每个小行星都有自己独立的
x坐标和图片索引,新生成的小行星不会影响旧的,它们会各自沿着自己的直线下落啦!
内容的提问来源于stack exchange,提问作者Isaac
相关产品推荐
相关产品推荐

