尝试在 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
我正在 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