少陵野小Tommy 2026-01-26 15:07 采纳率: 62.5%
浏览 6

pygame小人无法跳跃

运行下面这段pygame代码时,小人(man)站在wall上时无法跳跃。如何修改代码解决这个问题?

import pygame
import sys
import pymunk
import pymunk.pygame_util
from pygame.locals import *

pygame.init()

FPS = 60
FramePerSec = pygame.time.Clock()

WHITE = (255, 255, 255)
BLACK = (0, 0, 0)

SCREEN_WIDTH = 1200
SCREEN_HEIGHT = 800

man_speed = 250

DISPLAYSURF = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
DISPLAYSURF.fill(WHITE)
pygame.display.set_caption("Blue Ball")

space = pymunk.Space()
space.gravity = (0, 900)

draw_options = pymunk.pygame_util.DrawOptions(DISPLAYSURF)

class Man(pygame.sprite.Sprite):
    def __init__(self, x, y, space):
        super().__init__()
        self.forms = {
            "normalR": pygame.image.load("images/normalRMan.png").convert_alpha(),
            "normalL": pygame.image.load("images/normalLMan.png").convert_alpha()
        }
        self.current_form = "normalR"
        self.original_image = self.forms[self.current_form]
        self.image = self.original_image
        self.rect = self.image.get_rect()
        self.rect.center = (x, y)

        self.space = space
        width, height = self.rect.width, self.rect.height
        
        self.body = pymunk.Body(1, pymunk.moment_for_box(1, (width, height)))
        self.body.position = (x, y)

        self.shape = pymunk.Poly.create_box(self.body, (width, height))
        self.shape.elasticity = 0.1
        self.shape.friction = 0.2
        self.shape.collision_type = 1

        self.shape.user_data = self

        self.space.add(self.body, self.shape)

        self.jump_strength = 1200

        self.facing_right = True

        self.on_ground = False
        self.coyote_time = 0
        self.coyote_time_max = 8

        self.jump_buffer = 0
        self.jump_buffer_max = 10

        self.head_collision_cooldown = 0
        self.side_collision_left = False
        self.side_collision_right = False
        self.side_collision_cooldown = 0

        self.max_horizontal_speed = 250
        self.max_upward_speed = 500
        self.max_downward_speed = 800

        self.jump_cooldown = 0
        self.min_jump_interval = 10

        self.last_up_key_state = False
        self.jumping = False
        self.wants_to_jump = False

    def update(self, walls_list):
        pressed_keys = pygame.key.get_pressed()

        self.sync_physics_to_sprite()
        
        self.side_collision_left = False
        self.side_collision_right = False

        self.detect_collisions(walls_list)

        if self.on_ground:
            self.coyote_time = self.coyote_time_max
            self.head_collision_cooldown = 0
            self.jumping = False
        elif self.coyote_time > 0:
            self.coyote_time -= 1

        if self.jump_cooldown > 0:
            self.jump_cooldown -= 1

        current_up_key = pressed_keys[K_UP]
        if current_up_key and not self.last_up_key_state:
            self.jump_requested = True
            self.jump_buffer = self.jump_buffer_max
        elif not current_up_key and self.last_up_key_state:
            self.jump_requested = False

        self.last_up_key_state = current_up_key

        if self.jump_buffer > 0:
            self.jump_buffer -= 1

        if self.head_collision_cooldown > 0:
            self.head_collision_cooldown -= 1

        if self.side_collision_cooldown > 0:
            self.side_collision_cooldown -= 1

        can_move_left = not self.side_collision_left or self.body.velocity.x > 0
        can_move_right = not self.side_collision_right or self.body.velocity.x < 0

        target_velocity_x = 0
        
        if pressed_keys[K_LEFT] and can_move_left:
            target_velocity_x = -man_speed
            if self.facing_right:
                self.turn_left()
        elif pressed_keys[K_RIGHT] and can_move_right:
            target_velocity_x = man_speed
            if not self.facing_right:
                self.turn_right()

        if self.head_collision_cooldown == 0 and self.side_collision_cooldown == 0:
            self.body.velocity = (target_velocity_x, self.body.velocity.y)

        can_jump = (self.on_ground or self.coyote_time > 0) and self.jump_cooldown <= 0

        if (self.wants_to_jump or self.jump_buffer > 0) and can_jump:
            self.execute_jump()

        self.keep_in_bounds()

    def sync_physics_to_sprite(self):
        self.rect.center = (int(self.body.position.x), int(self.body.position.y))

        if self.body.velocity.x < -0.1 and self.facing_right:
            self.turn_left()
        elif self.body.velocity.x > 0.1 and not self.facing_right:
            self.turn_right()

    def turn_left(self):
        self.current_form = "normalL"
        self.original_image = self.forms[self.current_form]
        self.image = self.original_image
        self.facing_right = False

    def turn_right(self):
        self.current_form = "normalR"
        self.original_image = self.forms[self.current_form]
        self.image = self.original_image
        self.facing_right = True

    def execute_jump(self):
        self.body.velocity = (self.body.velocity.x, -self.jump_strength)
        self.on_ground = False
        self.coyote_time = 0
        self.jumping = True
        self.wants_to_jump = False
        self.jump_buffer = 0
        self.jump_cooldown = self.min_jump_interval

    def detect_collisions(self, walls_list):
        self.on_ground = False

        for wall in walls_list:
            if not wall.visible:
                continue

            if not self.rect.colliderect(wall.rect):
                continue

            overlap_left = self.rect.right - wall.rect.left
            overlap_right = wall.rect.right - self.rect.left
            overlap_top = self.rect.bottom - wall.rect.top
            overlap_bottom = wall.rect.bottom - self.rect.top

            overlaps = {
                "left": overlap_left,
                "right": overlap_right,
                "top": overlap_top,
                "bottom": overlap_bottom
            }

            min_overlap_direction = min(overlaps, key=overlaps.get)
            min_overlap_value = overlaps[min_overlap_direction]

            if min_overlap_direction == "top" and min_overlap_value <= 20 and self.body.velocity.y >= -5:
                self.on_ground = True
                self.body.position = (self.body.position.x, wall.rect.top - self.rect.height / 2)
                self.body.velocity = (self.body.velocity.x, 0)
                return

            elif min_overlap_direction == "bottom" and min_overlap_value <= 25:
                if self.body.velocity.y < 0:
                    self.head_collision_cooldown = 3
                    self.body.velocity = (self.body.velocity.x, -self.body.velocity.y * 0.2)
                    adjust_y = wall.rect.bottom - self.rect.top + 1
                    self.body.position = (self.body.position.x, self.body.position.y + adjust_y)

            elif min_overlap_direction == "left" and min_overlap_value <= 25:
                if self.body.velocity.x > 0:
                    self.side_collision_left = True
                    self.side_collision_cooldown = 2
                    adjust_x = wall.rect.left - self.rect.right - 1
                    self.body.position = (self.body.position.x + adjust_x, self.body.position.y)
                    self.body.velocity = (0, self.body.velocity.y)

            elif min_overlap_direction == "right" and min_overlap_value <= 25:
                if self.body.velocity.x < 0:
                    self.side_collision_right = True
                    self.side_collision_cooldown = 2
                    adjust_x = wall.rect.right - self.rect.left + 1
                    self.body.position = (self.body.position.x + adjust_x, self.body.position.y)
                    self.body.velocity = (0, self.body.velocity.y)

    def keep_in_bounds(self):
        if self.rect.left < 0:
            self.body.position = (self.rect.width / 2, self.body.position.y)
            self.body.velocity = (0, self.body.velocity.y)
        elif self.rect.right > SCREEN_WIDTH:
            self.body.position = (SCREEN_WIDTH - self.rect.width / 2, self.body.position.y)
            self.body.velocity = (0, self.body.velocity.y)

        if self.rect.top < 0:
            self.body.position = (self.body.position.x, self.rect.height / 2)
            self.body.velocity = (self.body.velocity.x, 0)
            self.head_collision_cooldown = 3

    def set_form(self, form_name):
        self.current_form = form.name
        self.original_image = self.forms[form_name]
        self.image = self.original_image

    def draw(self, surface):
        surface.blit(self.image, self.rect)

class BlueBall(pygame.sprite.Sprite):
    def __init__(self, x, y):
        super().__init__()
        self.image = pygame.image.load("images/BlueBall.png")
        self.rect = self.image.get_rect()
        self.rect.center = (x, y)

    def draw(self, surface):
        surface.blit(self.image, self.rect)

class Wall(pygame.sprite.Sprite):
    def __init__(self, x, y, width, height, space):
        super().__init__()
        self.image = pygame.Surface((width, height))
        self.color = (0, 0, 0)
        self.image.fill(self.color)
        self.rect = self.image.get_rect()
        self.rect.center = (x, y)
        self.visible = True

        self.space = space

        self.body = pymunk.Body(body_type=pymunk.Body.STATIC)
        self.body.position = (x, y)

        self.shape = pymunk.Poly.create_box(self.body, (width, height))
        self.shape.elasticity = 0.05
        self.shape.friction = 0.2
        self.shape.collision_type = 3

        self.shape.user_data = self

        self.space.add(self.body, self.shape)

    def hide(self):
        self.visible = False
        if self.shape in self.space.shapes:
            self.space.remove(self.shape)
        if self.body in self.space.bodies:
            self.space.remove(self.body)

    def draw(self, surface):
        if self.visible:
            surface.blit(self.image, self.rect)

man = Man(160, 160, space)
blueballs = {
    1: BlueBall(300, 300)
}
walls = {
    1: [Wall(600, 770, 1200, 60, space),
        Wall(600, 100, 200, 30, space),
        Wall(900, 300, 200, 30, space),
        Wall(300, 500, 200, 30, space)]
}

level = 1

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

    space.step(1.0 / FPS)
    
    man.update(walls[level])

    DISPLAYSURF.fill(WHITE)
    man.draw(DISPLAYSURF)
    blueballs[1].draw(DISPLAYSURF)
    for wall in walls[level]:
        wall.draw(DISPLAYSURF)
    
    pygame.display.update()
    FramePerSec.tick(FPS)
  • 写回答

2条回答 默认 最新

  • 檀越@新空间 2026-01-26 15:08
    关注

    下午好🌅🌅🌅
    本答案参考通义千问

    您提供的代码中,Man类的update方法似乎没有完整地实现跳跃逻辑。目前,虽然有检测是否在地面上(self.on_ground)和跳跃缓冲(jump_buffer)等变量,但并没有实际触发跳跃动作。这可能是导致小人无法跳跃的原因。

    以下是解决这个问题的详细步骤:

    1. 检查碰撞检测逻辑
      确保detect_collisions(walls_list)方法能够正确检测到小人是否站在“wall”上。如果该方法未能正确设置self.on_groundTrue,则跳跃功能将不会被激活。

    2. 实现跳跃逻辑
      update方法中添加判断条件,当self.on_groundTrueself.coyote_time > 0时,允许跳跃。同时,确保jump_buffer被正确使用以防止连续跳跃。

    3. 调整跳跃速度
      在跳跃时,给小人的身体施加一个垂直方向的速度(例如向上),并限制跳跃频率以避免连续跳跃。

    4. 更新图像方向
      根据小人移动的方向更新图像,使其看起来更自然。

    以下是修改后的代码片段,重点部分已用加粗标出:

    def update(self, walls_list):
        pressed_keys = pygame.key.get_pressed()
    
        self.sync_physics_to_sprite()
        
        self.side_collision_left = False
        self.side_collision_right = False
    
        self.detect_collisions(walls_list)
    
        if self.on_ground:
            self.coyote_time = self.coyote_time_max
            self.head_collision_cooldown = 0
            self.jumping = False
        elif self.coyote_time > 0:
            self.coyote_time -= 1
    
        if self.jump_cooldown > 0:
            self.jump_cooldown -= 1
    
        current_up_key = pressed_keys[K_UP]
        if current_up_key and not self.last_up_key_state:
            self.jump_requested = True
            self.jump_buffer = self.jump_buffer_max
        elif not current_up_key and self.last_up_key_state:
            self.jump_requested = False
    
        self.last_up_key_state = current_up_key
    
        if self.jump_buffer > 0:
            self.jump_buffer -= 1
    
        if self.head_collision_cooldown > 0:
            self.head_collision_cooldown -= 1
    
        if self.side_collision_cooldown > 0:
            self.side_collision_cooldown -= 1
    
        can_move_left = not self.side_collision_left or self.body.velocity.x > 0
        can_move_right = not self.side_collision_right or self.body.velocity.x < 0
    
        target_velocity_x = 0
        
        if pressed_keys[K_LEFT] and can_move_left:
            target_velocity_x = -man_speed
            if self.facing_right:
                self.turn_left()
        elif pressed_keys[K_RIGHT] and can_move_right:
            target_velocity_x = man_speed
            if not self.facing_right:
                self.turn_right()
    
        if self.head_collision_cooldown == 0 and self.side_collision_cooldown == 0:
            self.body.velocity = (target_velocity_x, self.body.velocity.y)
    
        **# 跳跃逻辑**
        if (self.on_ground or self.coyote_time > 0) and self.jump_buffer > 0 and self.jump_cooldown == 0:
            self.body.velocity = (self.body.velocity.x, -self.jump_strength)
            self.jump_cooldown = self.min_jump_interval
    

    说明:

    • 第15行:检查是否在地面上或是否有“coyote time”,即在离开地面后的一段时间内仍然可以跳跃。
    • 第16行:确保jump_buffer大于0,并且跳转冷却时间已经结束。
    • 第17行:对小人的垂直速度施加一个向上的力,模拟跳跃效果。
    • 第18行:设置跳转冷却时间,防止连续跳跃。

    其他建议:

    • 如果detect_collisions方法未正确检测到碰撞,请检查其内部逻辑,确保它能正确识别与“wall”的接触。
    • 确保on_ground变量在碰撞检测中被正确设置为True,否则跳跃功能将失效。

    通过以上修改,您的小人应该能够在“wall”上跳跃了。

    评论

报告相同问题?

问题事件

  • 创建了问题 1月26日