pygame 精灵不随 self.rect.move_ip 移动

pygame sprite not moving with self.rect.move_ip

我正在 pygame 开发一款游戏,我已经在屏幕上找到了基本绘图,但用户输入让我感到困惑。我有一个播放器 class:

class Player(pygame.sprite.Sprite):
  def __init__(self):
    super(Player, self).__init__()
    self.surf = pygame.Surface((75, 25))
    self.surf.fill((255, 255, 255))
    self.rect = self.surf.get_rect()

  def update(self, pressed_keys):
    if pressed_keys[K_UP]:
        self.rect.move_ip(0, -5)
    if pressed_keys[K_DOWN]:
        self.rect.move_ip(0, 5)
    if pressed_keys[K_LEFT]:
        self.rect.move_ip(-5, 0)
    if pressed_keys[K_RIGHT]:
        self.rect.move_ip(5, 0)
    pygame.event.pump()

但是每当我按下按键时,什么也没有发生。这是绘制内容的脚本结尾:

while running:
    for event in pygame.event.get():
        if event.type == KEYDOWN:
            if event.key == K_ESCAPE:
                running = False
        elif event.type == QUIT:
            running = False
    
    pressed_keys = pygame.key.get_pressed()

    player.update(pressed_keys)

    screen.fill((0, 0, 0))

    surf = pygame.Surface((50, 50))

    surf.fill((0, 0, 0))
    rect = surf.get_rect()

    screen.blit(player.surf, (SCREEN_WIDTH/2, SCREEN_HEIGHT/2))

    pygame.display.flip()

我不知道发生了什么,但我找不到任何相关信息来修复它。一切都画对了,但没有别的。

您必须 blit rect 属性中存储的位置的玩家:

screen.blit(player.surf, (SCREEN_WIDTH/2, SCREEN_HEIGHT/2))

screen.blit(player.surf, player.rect)

问题:在屏幕上闪烁时出现逻辑错误

在你的游戏循环中:

  • player.update移动 播放器按键
  • blit 正在绘制始终固定在中心的玩家 (SCREEN_WIDTH/2, SCREEN_HEIGHT/2) - 尽管玩家的位置移动了

因此你的播放器卡在视觉上在屏幕中心,尽管你移动了它并且它的位置(在rect中)被箭头键改变了。

调试

添加一些调试 print 语句

  • blit 之前监控玩家位置是否在更新后移动:.
  • blit 之后监视其 return 绘制矩形的值
print("player's rect after update:", player.rect)
drawn_rect = screen.blit(player.surf, (SCREEN_WIDTH/2, SCREEN_HEIGHT/2))
print("rect drawn by blit:", drawn_rect)

研究

假设您 blit 的意图是:

  • 最初绘制时让玩家在屏幕上居中
  • 使用 blit
  • 在屏幕上绘制玩家

让我们查阅 PyGame 文档以获得一些支持。

Surface 对象居中

阅读 Surface 文档,了解 如何将 get_rect() 居中:

You can pass keyword argument values to this function. These named values will be applied to the attributes of the Rect before it is returned. An example would be mysurf.get_rect(center=(100, 100)) to create a rectangle for the Surface centered at a given position.

使用blit

来自 blit(source, dest, area=None, special_flags=0)

上的文档

Draws a source Surface onto this Surface. The draw can be positioned with the dest argument. The dest argument can either be a pair of coordinates representing the position of the upper left corner of the blit or a Rect

设计修复

Spawn初始位置的玩家,例如centered(在屏幕上),创建过程中。

然后你的游戏循环有 single responsibilitydraw player screen

玩家:居中、移动、抽签

class Player(pygame.sprite.Sprite):
     def __init__(self):
         super(Player, self).__init__()
         self.surf = pygame.Surface((75, 25))
         self.surf.fill((255, 255, 255))
         self.rect = self.centered()  # spawn it centerd

    def update(self, pressed_keys):
         if pressed_keys[K_UP]:
             self.move(0, -5)
         if pressed_keys[K_DOWN]:
             self.move(0, 5)
         if pressed_keys[K_LEFT]:
             self.move(-5, 0)
         if pressed_keys[K_RIGHT]:
             self.move(5, 0)
         pygame.event.pump()

## added some methods

    def centered():
        return self.surf.get_rect(center=(SCREEN_WIDTH/2, SCREEN_HEIGHT/2)) 

    def move(x, y):
        self.rect.move_ip(x,y)

    def draw_on(surface):  # visitor-pattern
        surface.blit(self.surf, self.rect)

注意:在 Player

中添加了一些功能
  • centered 定位在恒定屏幕尺寸的中心
  • move 以向量 (x,y)
  • 移动玩家
  • draw_on 在给定的表面上绘制玩家 sprite/surface,就像您的 screen

绘图

然后在你的游戏循环中替换:

    screen.blit(player.surf, (SCREEN_WIDTH/2, SCREEN_HEIGHT/2))

通过新命令:

    # new player is centered, moved player is located anywhere
    player.draw_on(screen)

注意:_visitor-pattern- 将语句更改为形式为 <subject>.<verb>(<object>) 的人类可读句子,并且比以前少了一个参数(内部化)。

奖金:建议

可以改进面向对象的设计以增强可读性、简化测试和简化使用:

  1. 通过命令动作等功能控制玩家
  2. 选择一种简单的游戏语言(领域术语)
  3. 并用它来命名你的抽象(对象、函数、变量)

示例:Playerscreenmovecentereddraw_to_on(名词, 类似形容词或动词, 用介词表示关系)

通常,设计改进可以更轻松地识别逻辑错误或在调试过程中分析控制流 - 希望如此。