尝试在 Pygame 简单的 2D 平台游戏中让玩家的屏幕居中

Trying to make screen center to player in Pygame simple 2D Platformer

我正在 Pygame 中创建一个简单的 2D 平台游戏。在这个项目中,我一直在尝试更多地使用 classes 作为良好实践。我目前的问题是我试图让屏幕在播放器上居中,但是我找不到用我的代码当前设置的方式来做到这一点的方法。

此外,我计划稍后添加一种方法,使屏幕跟随玩家时有少量滞后,随着玩家离得越远跟随速度越快。然而,让相机以播放器为中心是我最关心的问题。

当玩家向右移动时,我让所有的“PlatformRects”以相同的速度向左移动。我尝试了不同的速度,将播放器设置为零速度并移动“PlatformRects”几乎可以工作(“播放器”class 的“更新”功能中代码中的相关注释)。我也曾尝试让“PlatformRects”移动取决于玩家的位置,但我也无法让它工作。

如有任何帮助,我将不胜感激。这是我的代码(我删掉了很多不相关的代码,比如动画等等):

import pygame, sys, random
#---Classes----------------------------------------#
class PlatformRect(pygame.sprite.Sprite):
    def __init__(self,x_pos,y_pos,size_x,size_y,path,players):
        super().__init__()
        self.x_pos = x_pos
        self.y_pos = y_pos
        self.size_x = size_x
        self.size_y = size_y
        self.players = players
        self.can_jump1 = None

        if path == "null":
            self.image = pygame.Surface((self.size_x,self.size_y))
            self.rect = self.image.get_rect(topleft = (self.x_pos,self.y_pos))
        else:
            self.image = pygame.transform.scale(pygame.image.load(path).convert_alpha(),(size_x,size_y))
            self.rect = self.image.get_rect(topleft = (self.x_pos,self.y_pos))

    def collision_check(self):
        if pygame.sprite.spritecollide(self,self.players,False): 
            collision_paddle = pygame.sprite.spritecollide(self,self.players,False)[0].rect
            if player.movement_y > 0 and abs(self.rect.top - collision_paddle.bottom) < 10: #load of checks to ensure the correct side is being collided 
                collision_paddle.bottom = self.rect.top 
                player.movement_y = 0
                self.can_jump1 = True
            elif player.movement_y < 0 and abs(self.rect.bottom - collision_paddle.top) < 10 and not abs(self.rect.right - collision_paddle.left) < 10 and not abs(self.rect.left - collision_paddle.right) < 10:
                collision_paddle.top = self.rect.bottom
                player.movement_y *= -0.2
                print("moving bottom")
            elif player.movement_x < 0 and abs(self.rect.right - collision_paddle.left) < 10 and not abs(self.rect.bottom - collision_paddle.top) < 10:
                collision_paddle.left = self.rect.right
                print("moving left")
            elif player.movement_x > 0 and abs(self.rect.left - collision_paddle.right) < 10 and not abs(self.rect.bottom - collision_paddle.top) < 10:
                collision_paddle.right = self.rect.left
                print("moving right")
        else:
            self.can_jump1 = self.rect.top == self.players.sprites()[0].rect.bottom  

    def can_jump_check(self):
        return self.can_jump1

    def update(self):
        self.collision_check()
        self.rect.centerx -= player.movement_x #trying to get rects to move the same distance as the player, if player moves 2 pixels right, rect moves 2 pixels left?
        self.rect.centery -= 0


class Player(pygame.sprite.Sprite):
    def __init__(self,x_pos,y_pos,size_x,size_y,speed_x,acceleration_y): 
        super().__init__()
        self.x_pos = x_pos
        self.y_pos = y_pos
        self.size_x = size_x
        self.size_y = size_y
        self.speed_x = speed_x
        self.shift_pressed = False
        self.acceleration_y = acceleration_y
        self.movement_x = 0 
        self.movement_y = 0
        
        self.image = pygame.Surface((self.size_x,self.size_y))
        self.image.fill("red")
        self.rect = self.image.get_rect(center = (self.x_pos,self.y_pos))

    def screen_constrain(self): 
        if self.rect.bottom >= sresy:
            self.rect.bottom = sresy

    def update(self):
        if abs(self.movement_y) <= 8: 
            self.movement_y += GRAVITY
        self.rect.centery += self.movement_y
        if self.shift_pressed == False:
            self.rect.centerx += self.movement_x #if set to 0, scrolling kindof works, although camera still scrolls even if player is stuck on block
        elif self.shift_pressed == True: 
            self.rect.centerx += 2*self.movement_x
        self.screen_constrain()

class GameManager:
    def __init__(self,player_group,platform_group):
        self.player_group = player_group
        self.platform_group = platform_group
        self.can_jump = True

    def run_game(self):
        #---drawing---#
        self.player_group.draw(screen)
        self.platform_group.draw(screen)

        #---updating---#
        self.player_group.update()
        self.platform_group.update()

    def game_checking(self):
        #---checking---#
        self.can_jump = any(p.can_jump_check() for p in self.platform_group)  
        return self.can_jump
        
#---Setup------------------------------------------#
#---constants-----#
global GRAVITY
GRAVITY = 0.25

#---Gamevariables-----#
can_jump = True
shift_pressed = False
music_playing = False

#---colour---#
bg_colour = (50,50,50)
white_colour = (255,255,255)
black_colour = (0,0,0)

#---res---#
resx = 900 
resy = 700
resx_moved = resx - 50
resy_moved = resy - 50
sresx = 850 #base surface size, to be scaled up to resx_moved and resy_moved
sresy = 650

#---game map---# 
game_map = [[0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
            [0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
            [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
            [0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0],
            [0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0],
            [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0],
            [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1],
            [0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1],
            [0,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1],
            [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
            [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
            [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
            [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
            [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]]

#---start window-----#
pygame.init()
clock = pygame.time.Clock()

screendisplay = pygame.display.set_mode((resx,resy))
screendisplay.fill(bg_colour)
pygame.display.set_caption("PlatformerGame") 

screen = pygame.Surface((sresx,sresy))
screen.fill(white_colour)

#---startgame-----#
player = Player(425,325,50,50,2,30)
player_group = pygame.sprite.GroupSingle() 
player_group.add(player) 

platform_list = []
y_gm = 0
for row in game_map:
    x_gm = 0
    for tile in row:
        if tile == 1:
            platform_list.append(PlatformRect(x_gm * 50, y_gm * 50, 50, 50, "null",player_group)) #sets platform at given positions and size
        x_gm += 1
    y_gm += 1
platform_group = pygame.sprite.Group() 
platform_group.add(platform_list) 

game_manager = GameManager(player_group,platform_group)

#---Loop--------------------------------------------#
while True:
    #---events-----#
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()
            sys.exit()

        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_ESCAPE: #emergency
                pygame.quit()
                sys.exit()

            if event.key == pygame.K_SPACE: 
                if can_jump == True:
                    player.movement_y = 0
                    player.movement_y -= player.acceleration_y * GRAVITY 

            if event.key == pygame.K_LSHIFT: #sprinting
                player.shift_pressed = True

            if event.key == pygame.K_a: 
                player.movement_x -= player.speed_x

            if event.key == pygame.K_d: 
                player.movement_x += player.speed_x

        if event.type == pygame.KEYUP:
            if event.key == pygame.K_SPACE: 
                if player.movement_y < 0: 
                    player.movement_y = 0

            if event.key == pygame.K_a: 
                player.movement_x += player.speed_x

            if event.key == pygame.K_d: 
                player.movement_x -= player.speed_x

            if event.key == pygame.K_LSHIFT: 
                player.shift_pressed = False

    #---background---#
    surf = pygame.transform.scale(screen,(resx_moved,resy_moved)) #changes resolution
    screendisplay.blit(surf,(25,25))
    screen.fill(white_colour) 

    #---running---#
    game_manager.run_game()
    if not game_manager.game_checking() == True and player.rect.bottom < sresy: #checking if can_jump is true
        can_jump = False
    else:
        can_jump = True

    #---updating-----#
    pygame.display.update()
    clock.tick(120)

如果我的代码不清楚,我当然会澄清。

你应该这样移动相机:

# at the beginning: set camera
camera = pygame.math.Vector2((0, 0))

# in the main loop: adjust the camera position to center the player
camera.x = player.x_pos - resx / 2
camera.y = player.y_pos - resy / 2

# in each sprite class: move according to the camera instead of changing the sprite position
pos_on_the_screen = (self.x_pos - camera.x, self.y_pos - camera.y)
  • 摄像机跟随玩家,并自行放置以使玩家位于屏幕中央
  • 每个精灵的位置永远不会改变,不断改变精灵的位置是一个非常糟糕的主意。
  • 根据相机在屏幕上绘制每个精灵

为了减少延迟,您应该仅在每个精灵可见时才显示它:

screen_rect = pygame.Rect((0, 0), (resx, resy))
sprite_rect = pygame.Rect((self.x_pos - camera.x, self.y_pos - camera.y), sprite_image.get_size())
if screen_rect.colliderect(sprite_rect):
    # render if visible

这是我用同样的方法制作的游戏中移动背景的截图:

这里我把镜头移动的更顺畅了。基本上我用这个方法:

speed = 1
distance_x = player.x_pos - camera.x
distance_y = player.y_pos - camera.y
camera.x = (camera.x * speed + distance_x) / (speed + 1)
camera.y = (camera.y * speed + distance_y) / (speed + 1)

然后我根据FPS改speed