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

如何在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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.14 08:00:24