如何使用Pymunk实现玩家角色吸附在移动平台上?
如何使用Pymunk实现玩家角色吸附在移动平台上?
我看了你的代码和问题演示,你现在遇到的问题是玩家站在移动平台上时,没法跟着平台一起移动,最终会掉下去对吧?这在Pymunk里其实是可以通过优化碰撞回调、完善地面状态管理,以及确保平台的表面速度正确生效来解决的,咱们一步步来调整:
问题根源分析
你已经给移动平台设置了surface_velocity,但当前的问题主要来自两个点:
- 你的
on_ground状态只在碰撞开始时设为True,但没有在玩家离开平台时重置为False,这会导致跳跃逻辑异常,也影响物理互动的持续判断。 - 碰撞回调只用到了
begin,没有利用pre_solve来持续确认玩家的站立状态,Pymunk需要持续的状态确认才能让surface_velocity稳定传递平台速度给玩家。
具体修改方案
- 完善地面状态的碰撞回调
添加separate回调处理玩家和平台分离的场景,同时用pre_solve持续确认玩家的站立状态,确保物理互动始终生效。 - 优化玩家速度的逻辑处理
调整无输入时的玩家速度规则,让站在平台上的玩家能通过Pymunk的摩擦力系统自动跟随平台移动。 - 规范精灵组的使用
把玩家加入all_sprites组,统一管理更新逻辑,符合Pygame的使用习惯。
修改后的完整代码
#Pymunk Platformer: import pygame import pymunk import sys import math pygame.init() WIDTH, HEIGHT = 800, 600 screen = pygame.display.set_mode((WIDTH, HEIGHT)) pygame.display.set_caption('Platformer Game') clock = pygame.time.Clock() space = pymunk.Space() space.gravity = (0, 900) # Gravity pulls objects downwards class Player(pygame.sprite.Sprite): def __init__(self, x, y, width=30, height=30): super().__init__() self.image = pygame.Surface((30, 30), pygame.SRCALPHA).convert_alpha() self.image.fill((255, 0, 0)) # Red square for player self.rect = self.image.get_rect(center=(x, y)) # Pymunk physics body mass = 1 self.width, self.height = width, height inertia = float("inf") # 防止玩家旋转 self.body = pymunk.Body(mass, inertia) self.body.position = x, y self.shape = pymunk.Poly.create_box(self.body, (width, height)) self.shape.collision_type = 1 # Player collision type self.shape.friction = 1.0 # 保持足够摩擦力以传递平台速度 space.add(self.body, self.shape) self.on_ground = False self.angle = 0 self.start_position = (x, y) def update(self): self.rect.center = self.body.position self.angle = math.degrees(self.body.angle) if self.body.position.y > 2000: self.body.position = self.start_position self.body.velocity = (0,0) def jump(self): if self.on_ground: self.body.velocity = (self.body.velocity.x, -300) # Jump velocity self.on_ground = False class Platform(pygame.sprite.Sprite): def __init__(self, x, y, width, height): super().__init__() self.image = pygame.Surface((width, height)) self.image.fill((0, 255, 0)) # Green rectangle for platforms self.rect = self.image.get_rect(topleft=(x, y)) # Static body for platforms self.body = pymunk.Body(body_type=pymunk.Body.STATIC) self.body.position = x + width / 2, y + height / 2 # Center of the platform shape = pymunk.Poly.create_box(self.body, (width, height)) shape.collision_type = 2 # Platform collision type shape.friction = 0.5 space.add(self.body, shape) def update(self): pass # Platforms don't need to update their position class MovingPlatform(pygame.sprite.Sprite): def __init__(self, x, y, width, height, range, speed): super().__init__() self.image = pygame.Surface((width, height)) self.image.fill((0, 255, 0)) # Green rectangle for platforms self.rect = self.image.get_rect(topleft=(x, y)) # Change to KINEMATIC instead of STATIC self.body = pymunk.Body(body_type=pymunk.Body.KINEMATIC) self.body.position = x+width/2, y+height/2 # Center of the platform self.shape = pymunk.Poly.create_box(self.body, (width, height)) self.shape.collision_type = 2 # Platform collision type self.shape.friction = 1.5 # 足够的摩擦力传递平台速度 space.add(self.body, self.shape) self.range = range # How far it moves self.speed = speed # Speed of movement self.direction = 1 # 1 for moving right/up, -1 for left/down self.start_x, self.start_y = x, y def update(self): # Move the platform back and forth if self.body.position.x > self.start_x + self.range and self.direction == 1: self.direction = -1 # Reverse direction at edges if self.body.position.x < self.start_x and self.direction == -1: self.direction = 1 # Reverse direction at edges self.body.position = (self.body.position.x + self.direction * self.speed, self.body.position.y) # 确保表面速度与平台移动速度完全一致 self.shape.surface_velocity = (self.direction * self.speed, 0) self.rect.center = self.body.position # --- 完善碰撞回调函数 --- def player_on_ground_begin(arbiter, space, data): # 碰撞开始时标记玩家在地面 for shape in arbiter.shapes: if shape == player.shape: player.on_ground = True return True def player_on_ground_separate(arbiter, space, data): # 碰撞分离时重置地面状态 for shape in arbiter.shapes: if shape == player.shape: player.on_ground = False return True def player_on_ground_pre_solve(arbiter, space, data): # 持续确认玩家站立状态,确保物理互动稳定生效 player.on_ground = True return True handler = space.add_collision_handler(1, 2) # Player and Platform handler.begin = player_on_ground_begin handler.separate = player_on_ground_separate handler.pre_solve = player_on_ground_pre_solve # Create player and platforms player = Player(350, 200) platforms = pygame.sprite.Group() for _ in range(3): # Create three platforms platforms.add(Platform(100 + _ * 200, HEIGHT - 200, 100, 20)) for i in range(2): platforms.add(Platform(20 + i * 300, HEIGHT - 300, 200, 20)) for i in range(2): ptfm = MovingPlatform(200 + i * 200, HEIGHT - 370, 40, 20, range=100, speed=1) platforms.add(ptfm) all_sprites = pygame.sprite.Group(player, *platforms) # 将玩家加入精灵组统一管理 direction = "right" running = True while running: for event in pygame.event.get(): if event.type == pygame.QUIT: running = False keys = pygame.key.get_pressed() if keys[pygame.K_LEFT]: player.body.velocity = (-150, player.body.velocity.y) # Move left elif keys[pygame.K_RIGHT]: player.body.velocity = (150, player.body.velocity.y) # Move right else: # 无输入时,空中玩家停止左右移动,地面玩家跟随平台 if not player.on_ground: player.body.velocity = (0, player.body.velocity.y) if keys[pygame.K_SPACE]: player.jump() # Update physics space.step(1 / 60.0) # Update sprites all_sprites.update() # Draw screen.fill((0, 0, 0)) all_sprites.draw(screen) rotated_image = pygame.transform.rotate(player.image, player.angle) width, height = rotated_image.get_size() half_width, half_height = int(width/2), int(height/2) x, y = player.rect.center screen.blit(rotated_image, [x - half_width, y - half_height]) pygame.display.flip() clock.tick(60) pygame.quit() sys.exit()
关键修改说明
- 新增碰撞回调函数:通过
separate重置离开平台时的地面状态,pre_solve持续确认站立状态,让Pymunk的摩擦力和表面速度系统稳定工作。 - 优化玩家速度逻辑:无输入时,空中玩家自动停止左右移动,地面玩家则通过物理系统自动跟随平台。
- 统一精灵管理:将玩家加入
all_sprites组,简化更新逻辑。
现在运行修改后的代码,玩家站在移动平台上时就会自动跟随平台移动,不会再被甩下去啦!
备注:内容来源于stack exchange,提问作者Lion In A Box
相关产品推荐
相关产品推荐

