【问题标题】:Pygame - Platformer jump when grounded is unresponsivePygame - 接地时平台跳跃无响应
【发布时间】:2021-05-19 02:42:05
【问题描述】:

我最近在 pygame 中制作平台游戏(Python 版本:3.8.5,Pygame 版本:2.0.1)。

我实现了一个跳转功能,只要按下 "a" 键或 up 键并且播放器接地

问题

但这太迟钝了:

  • 在跳跃发生之前,我必须按 2 到 3 次键。

代码

下面是整个项目的代码(虽然有点乱):

import os
import sys
import pygame
from pygame.locals import *

# utility functions

# for drawing the collision box
def draw_collision_box(screen, rect):
    pygame.draw.rect(screen, (0, 255, 0), rect, 1)

# getting collisions
def get_collisions(rect, tiles):
    collisions = []

    for tile in tiles:
        if rect.colliderect(tile):
            collisions.append(tile)
    
    return collisions

# moving the player
def move(rect, movement, tiles):
    collision_types = {"top": False, "bottom": False, "right": False, "left": False}

    rect.x += movement[0]
    collisions = get_collisions(rect, tiles)

    for collision in collisions:
        if movement[0] > 0:
            rect.right = collision.left
            collision_types["right"] = True
        elif movement[0] < 0:
            rect.left = collision.right
            collision_types["left"] = True

    rect.y += movement[1]
    collisions = get_collisions(rect, tiles)

    for collision in collisions:
        if movement[1] > 0:
            rect.bottom = collision.top
            collision_types["bottom"] = True
        elif movement[1] < 0:
            rect.top = collision.bottom
            collision_types["top"] = True

    return rect, collision_types

# constants
FPS = 60
TILE_SIZE = 32
MAX_GRAVITY_SCL = 3

# initialize pygame
pygame.init()

# initializing the game window
WINDOW_SIZE = (400, 400)
screen = pygame.display.set_mode(WINDOW_SIZE)
pygame.display.set_caption("Platformer")

# clock
clock = pygame.time.Clock()

# player
player_image = pygame.image.load(os.path.join(
    "assets", "platformer", "player.png"))  # loading the player image
player_image = pygame.transform.scale(player_image, (16, 32))  # resizing the player image
player_image.set_colorkey((255, 255, 255))  # making the bg of the player transparent by setting the color key 

moving_right = False  # checking if the player is moving right
moving_left = False  # checking if the player is moving left

player_xspeed = 4  # player speed on horizontal axis

player_ymomentum = 0  # initial momentum on vertical axis
gravitational_acc = 0.5  # constant gravitation acceleration
jump_force = 10  # jump force of the player
grounded = False   # checking if the player is grounded

player_rect = pygame.Rect(
    50, 50, player_image.get_width(), player_image.get_height())  # player rect for managing collisions

# tiles
grass_image = pygame.image.load(os.path.join(
    "assets", "platformer", "grass.png"))  # loading the grass image
dirt_image = pygame.image.load(os.path.join(
    "assets", "platformer", "dirt.png"))  # loading the dirt image

# resizing the tile images
grass_image = pygame.transform.scale(grass_image, (TILE_SIZE, TILE_SIZE))
dirt_image = pygame.transform.scale(dirt_image, (TILE_SIZE, TILE_SIZE))

# game map
game_map = [["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", "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", "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", "2", "2", "2", "0", "0", "2", "2", "2", "0", "0", "0", "0", "0", "0", "0", "0", "0"],
            ["0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "2", "2", "2", "0", "0", "0", "0", "0"],
            ["0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "1", "1", "1", "0", "0", "0", "0", "0"],
            ["2", "2", "2", "2", "2", "2", "2", "2", "2", "2", "2", "2", "1", "1", "1", "2", "2", "2", "2", "2"],
            ["1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1"],
            ["1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1"]]

# game loop
while True:
    # frames per second
    clock.tick(FPS)

    # filling the background with a solid color so that images doesn't make on top of each other
    screen.fill((146, 244, 255))

    # rendering images
    screen.blit(player_image, (player_rect.x, player_rect.y))
    
    # rendering the map
    tiles = []

    for y in range(len(game_map)):
        for x in range(len(game_map[y])):
            if game_map[y][x] == "1":
                screen.blit(dirt_image, (x * TILE_SIZE, y * TILE_SIZE))
            if game_map[y][x] == "2":
                screen.blit(grass_image, (x * TILE_SIZE, y * TILE_SIZE))
            if game_map[y][x] != "0":
                tiles.append(pygame.Rect(x * TILE_SIZE, y * TILE_SIZE, TILE_SIZE, TILE_SIZE))
    
    # drawing the collision boxes for the tiles
    # for tile in tiles:
    #     draw_collision_box(screen, tile)

    # updating the game
    movement = [0, 0]

    if moving_right == True:
        movement[0] += player_xspeed
    if moving_left == True:
        movement[0] -= player_xspeed

    # gravity for the player
    player_ymomentum += gravitational_acc
    player_ymomentum = min(player_ymomentum, MAX_GRAVITY_SCL)
    movement[1] += player_ymomentum

    # updating player's position
    player_rect, collision_types = move(player_rect, movement, tiles)
    if collision_types["top"] or collision_types["bottom"]:
        player_ymomentum = 0

    grounded = collision_types["bottom"]

    # drawing the collision rect of the player
    # draw_collision_box(screen, player_rect)

    # listening for events
    for event in pygame.event.get():
        if event.type == QUIT:  # quit event
            pygame.quit()  # stop pygame
            sys.exit()  # quit the program

        if event.type == KEYDOWN:  # key input event
            if (event.key == K_RIGHT or event.key == K_d):
                moving_right = True
            if (event.key == K_LEFT or event.key == K_a):
                moving_left = True
            if (event.key == K_UP or event.key == K_w) and grounded:
                player_ymomentum = -jump_force

        if event.type == KEYUP:  # keyup input event
            if (event.key == K_RIGHT or event.key == K_d):
                moving_right = False
            if (event.key == K_LEFT or event.key == K_a):
                moving_left = False

    # updating the display
    pygame.display.update()

有人可以帮我吗?

【问题讨论】:

    标签: python pygame event-handling keypress responsiveness


    【解决方案1】:

    通过阅读您的代码,会发生以下情况:

    在这行代码中

    player_ymomentum += gravitational_acc
    

    player_ymomentum 设置为 0.5;因此movement[1] 也设置为 0.5。

    move 函数中,运行这行代码:

    rect.y += movement[1]
    

    由于movement[1] Rect.y 是/必须是整数值,rect.y 实际上并没有改变。

    由于rect.y 没有更改,因此在接下来对get_collisions 的调用中未检测到冲突。

    条件if movement[1] &gt; 0True,但是由于没有检测到碰撞,collision_types["bottom"]永远不会被设置为True;因此grounded 不会是True,你不能跳。


    在下一帧中,一行

    player_ymomentum += gravitational_acc
    

    再次运行。这一次,player_ymomentum 设置为 1;因此movement[1] 也设置为 1。

    move 函数中,行

    rect.y += movement[1]
    

    这次实际上会移动矩形。会检测到碰撞,collision_types["bottom"]会被设置成Truegrounded会被设置成True,让你跳转。

    player_ymomentum 将被重置为 0,这个循环又开始了。

    因此,每当您尝试站立时跳跃时,grounded 实际上是 False 的几率为 50/50,因此按跳跃键将不起作用。


    要解决此问题,请删除此部分:

    if collision_types["top"] or collision_types["bottom"]:
        player_ymomentum = 0
    

    player_ymomentum 将向MAX_GRAVITY_SCL 发展,同时仍然站在地上,但这应该不是问题。

    或者在尝试检测玩家与地面之间的碰撞时使用更大的Rect,如下所示:

    ...
    rect.y += movement[1]
    collisions = get_collisions(rect.inflate(1, 1), tiles)
    
    for collision in collisions:
    ...
    

    【讨论】:

    • 嘿懒惰谢谢,它没有帮助我,因为我用不同的方式修复了它,但我知道 Rect.y 必须是一个 int 值。非常感谢您对 BTW 的帮助
    【解决方案2】:

    我通过创建另一个名为 check_grounded 的函数解决了这个问题。

    发现问题

    问题在于在每一帧中,grounded 变量被赋值为 True,然后是 False。查看游戏循环中的摘录:

        # updating player's position
        player_rect, collision_types = move(player_rect, movement, tiles)
        if collision_types["top"] or collision_types["bottom"]:
            player_ymomentum = 0
    
        grounded = collision_types["bottom"]  # here we assign True or False
    

    所以玩家必须在正确的时刻跳跃 - 当grounded == True.

    解决方案

    为了解决这个问题,我做了另一个函数,只是为了地面检测,它工作正常:

    # check if grounded
    def check_grounded(rect, tiles):
        rect.y += 1
        collisions = get_collisions(rect, tiles)
        rect.y -= 1
        return len(collisions) != 0
    
    grounded = check_grounded(player_rect, tiles)
    

    【讨论】:

    • 用你的话帮助阅读问题解决方案。我敢打赌,通过这种重构,代码变得更易于理解:提取方法遵循设计原则:单级抽象 (SLA)。我稍微改进了格式。
    猜你喜欢
    • 2023-03-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多