运行下面这段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)