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

Pygame 2D瓦片游戏相机问题:角色移速快于相机致移出窗口

Pygame 2D瓦片游戏跟随相机问题修复

核心问题分析

你的代码存在三个关键问题,直接导致角色移动快于相机并最终移出窗口:

  1. 重复执行移动逻辑:游戏循环里先调用player.move(5),之后又调用player.update(),而update方法内部已经包含move(self.speed)(也就是move(5)),等于角色每帧被移动两次,速度翻倍,相机只计算一次偏移,自然跟不上角色移动节奏。
  2. 玩家绘制未应用相机偏移:camera_group.draw(self.screen)是Pygame默认的精灵绘制逻辑,直接将玩家绘制在屏幕原生坐标上,而地图是通过相机偏移绘制的,两者位置不匹配,看起来像是角色跑出了相机范围。
  3. 初始位置被硬编码覆盖:Player类的__init__里硬写了self.rect = self.image.get_rect(center=(640, 360)),覆盖了你传入的pos参数,导致角色初始位置不是预期的(100,200)。

修正方案

1. 清理游戏循环的重复调用与更新顺序

删掉游戏循环中多余的self.player.move(5),只保留self.camera_group.update()(它会自动调用所有精灵的update方法),确保每帧只执行一次移动逻辑:

def game(self):
    while True:
        self.clock.tick(self.FPS)
        self.screen.fill('black')
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                sys.exit()

        # 先更新所有精灵的状态(包括玩家移动)
        self.camera_group.update()
        # 再执行带相机偏移的绘制
        self.camera_group.custom_draw(self.player)
        
        pygame.display.update()

2. 修改Camera类,统一应用相机偏移

重写Camera类的绘制逻辑,让地图和所有精灵都使用相同的相机偏移,替换原来的默认draw方法:

class Camera(pygame.sprite.Group):
    def __init__(self):
        super().__init__()
        self.display_surface = pygame.display.get_surface()
        self.offset = pygame.math.Vector2()
        self.half_w = self.display_surface.get_size()[0] // 2
        self.half_h = self.display_surface.get_size()[1] // 2

        self.map = pygame.image.load('./map/level_data/level.png').convert_alpha()
        self.rect = self.map.get_rect(topleft=(0, 0))

    def custom_draw(self, player):
        # 计算相机偏移量
        self.offset.x = player.rect.centerx - self.half_w
        self.offset.y = player.rect.centery - self.half_h

        # 绘制带偏移的地图
        ground_offset = self.rect.topleft - self.offset
        self.display_surface.blit(self.map, ground_offset)

        # 绘制带偏移的所有精灵(包括玩家)
        for sprite in self.sprites():
            offset_pos = sprite.rect.topleft - self.offset
            self.display_surface.blit(sprite.image, offset_pos)

这样就不需要再调用self.camera_group.draw(self.screen),所有绘制都在custom_draw里完成,确保地图和玩家的位置完全匹配相机逻辑。

3. 修复Player类的初始位置问题

把Player类__init__里的硬编码center改成使用传入的pos参数:

def __init__(self, pos, group):
    super().__init__(group)
    self.image = pygame.image.load('./chatacters/players/player_one.png').convert_alpha()
    self.rect = self.image.get_rect(center=pos)  # 使用传入的初始位置
    self.direction = pygame.math.Vector2()
    self.speed = 5

完整修正后的代码

import pygame, sys

class Player(pygame.sprite.Sprite):
    def __init__(self, pos, group):
        super().__init__(group)
        self.image = pygame.image.load('./chatacters/players/player_one.png').convert_alpha()
        self.rect = self.image.get_rect(center=pos)
        self.direction = pygame.math.Vector2()
        self.speed = 5

    def input(self):
        keys = pygame.key.get_pressed()

        if keys[pygame.K_UP]:
            self.direction.y = -1
        elif keys[pygame.K_DOWN]:
            self.direction.y = 1
        else:
            self.direction.y = 0

        if keys[pygame.K_LEFT]:
            self.direction.x = -1
        elif keys[pygame.K_RIGHT]:
            self.direction.x = 1
        else:
            self.direction.x = 0

    def move(self, speed):
        if self.direction.magnitude() != 0:
            self.direction = self.direction.normalize()

        self.rect.center += self.direction * speed

    def update(self):
        self.input()
        self.move(self.speed)


class Camera(pygame.sprite.Group):
    def __init__(self):
        super().__init__()
        self.display_surface = pygame.display.get_surface()
        self.offset = pygame.math.Vector2()
        self.half_w = self.display_surface.get_size()[0] // 2
        self.half_h = self.display_surface.get_size()[1] // 2

        self.map = pygame.image.load('./map/level_data/level.png').convert_alpha()
        self.rect = self.map.get_rect(topleft=(0, 0))

    def custom_draw(self, player):
        self.offset.x = player.rect.centerx - self.half_w
        self.offset.y = player.rect.centery - self.half_h

        # 绘制地图
        ground_offset = self.rect.topleft - self.offset
        self.display_surface.blit(self.map, ground_offset)

        # 绘制所有精灵
        for sprite in self.sprites():
            offset_pos = sprite.rect.topleft - self.offset
            self.display_surface.blit(sprite.image, offset_pos)


class Game():
    def __init__(self):
        self.WIDTH = 1280
        self.HEIGHT = 720
        self.FPS = 60

        pygame.init()
        pygame.display.set_caption('BetterFortnite')

        self.screen = pygame.display.set_mode((self.WIDTH, self.HEIGHT))
        self.clock = pygame.time.Clock()
        self.camera_group = Camera()
        self.player = Player((100, 200), self.camera_group)

    def game(self):
        while True:
            self.clock.tick(self.FPS)
            self.screen.fill('black')
            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    pygame.quit()
                    sys.exit()

            self.camera_group.update()
            self.camera_group.custom_draw(self.player)

            pygame.display.update()


if __name__ in '__main__':
    game = Game()
    game.game()

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.24 23:06:27