Pygame 2D瓦片游戏相机问题:角色移速快于相机致移出窗口
Pygame 2D瓦片游戏跟随相机问题修复
核心问题分析
你的代码存在三个关键问题,直接导致角色移动快于相机并最终移出窗口:
- 重复执行移动逻辑:游戏循环里先调用
player.move(5),之后又调用player.update(),而update方法内部已经包含move(self.speed)(也就是move(5)),等于角色每帧被移动两次,速度翻倍,相机只计算一次偏移,自然跟不上角色移动节奏。 - 玩家绘制未应用相机偏移:
camera_group.draw(self.screen)是Pygame默认的精灵绘制逻辑,直接将玩家绘制在屏幕原生坐标上,而地图是通过相机偏移绘制的,两者位置不匹配,看起来像是角色跑出了相机范围。 - 初始位置被硬编码覆盖: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
相关产品推荐
相关产品推荐

