如何让Pymunk中的盒子从角落以设定强度持续一段时间推升
实现Pymunk盒子的持续角落推升效果
问题需求
需要让物理场景中的盒子从指定角落,以设定强度(如50%最大力度)缓慢向上推升,且推升动作持续设定时长(2-3秒)。此前尝试用单次冲量的方式无法实现需求,因为冲量是瞬间作用,无法达成持续缓慢推升的效果。
解决方案思路
要实现持续推升,不能依赖单次冲量,而是需要在设定时长内,每帧持续施加力或分多次施加小冲量,同时精准控制力的作用位置为盒子的指定角落,结合力度百分比和时长参数实现需求:
- 记录推升的起始时间与持续时长,实时判断当前是否处于推升阶段
- 计算盒子角落的局部坐标,确保力作用在目标位置
- 按设定的力度百分比和方向,每帧稳定施加力(或小冲量)
修改后的完整代码
import pygame import pymunk import pymunk.pygame_util import math pygame.init() WIDTH, HEIGHT = 1280, 720 window = pygame.display.set_mode((WIDTH, HEIGHT)) def draw(space, window, draw_options): window.fill('white') space.debug_draw(draw_options) pygame.display.update() def create_invisible_border(space, width, height, collision_type, friction=0.4, elasticity=0.9, thickness=100): body = pymunk.Body(body_type=pymunk.Body.STATIC) body.position = (width/2, height/2) space.add(body) top_border = pymunk.Segment(body, (-width/2, height/2 + thickness), (width/2, height/2 + thickness), thickness) bottom_border = pymunk.Segment(body, (-width/2, -height/2 - thickness), (width/2, -height/2 - thickness), thickness) left_border = pymunk.Segment(body, (-width/2 - thickness, height/2), (-width/2 - thickness, -height/2), thickness) right_border = pymunk.Segment(body, (width/2 + thickness, height/2), (width/2 + thickness, -height/2), thickness) top_border.collision_type = collision_type bottom_border.collision_type = collision_type left_border.collision_type = collision_type right_border.collision_type = collision_type top_border.friction = friction bottom_border.friction = friction left_border.friction = friction right_border.friction = friction top_border.elasticity = elasticity bottom_border.elasticity = elasticity left_border.elasticity = elasticity right_border.elasticity = elasticity space.add(top_border, bottom_border, left_border, right_border) def create_static_circle(space, x_pos, y_pos, collision_type, radius): body = pymunk.Body(body_type=pymunk.Body.STATIC) body.position = x_pos, y_pos space.add(body) shape = pymunk.Circle(body, radius) shape.collision_type = collision_type space.add(shape) return body def create_object(space, mass, size, x, y, collision_type, friction = 0.4, elasticity = 0.9): moment = pymunk.moment_for_box(mass, size) body = pymunk.Body(mass, moment) body.position = x, y shape = pymunk.Poly.create_box(body, size) shape.elasticity = elasticity shape.friction = friction shape.collision_type = collision_type space.add(body, shape) return body def apply_lift_force(body, box_size, angle_deg, max_strength, percentage): # 计算盒子左下角的局部坐标(可修改为其他角落:右上角为(box_size[0]/2, box_size[1]/2)) corner_local_pos = (-box_size[0]/2, -box_size[1]/2) # 计算实际施加的力度与分量 strength = max_strength * (percentage / 100) angle_rad = math.radians(angle_deg) force_x = strength * math.cos(angle_rad) # Pymunk中y轴向上为负方向,因此取负确保力向上 force_y = -strength * math.sin(angle_rad) # 在指定角落施加力 body.apply_force_at_local_point((force_x, force_y), corner_local_pos) def run(window, width, height): run = True clock = pygame.time.Clock() fps = 60 dt = 1/fps space = pymunk.Space() space.gravity = (0, 981) create_invisible_border(space, width, height, 2) create_static_circle(space, 1050, 160, 3, 25) box_size = (100, 100) box_body = create_object(space, 1, box_size, 300, 300, 1, elasticity = 0.2) # 推升参数配置 lift_active = False lift_start_time = 0 lift_duration = 3000 # 持续3000毫秒(3秒) lift_max_strength = 2000 # 最大力度 lift_percentage = 50 # 50%力度 lift_angle = 135 # 推升角度(135度对应左上方向) draw_options = pymunk.pygame_util.DrawOptions(window) while run: current_time = pygame.time.get_ticks() for event in pygame.event.get(): if event.type == pygame.QUIT: run = False break # 按下任意键触发推升(可自定义触发条件) if event.type == pygame.KEYDOWN: lift_active = True lift_start_time = current_time # 检查是否处于推升时间段内 if lift_active and (current_time - lift_start_time) < lift_duration: apply_lift_force(box_body, box_size, lift_angle, lift_max_strength, lift_percentage) elif (current_time - lift_start_time) >= lift_duration: lift_active = False draw(space, window, draw_options) space.step(dt) clock.tick(fps) pygame.quit() if __name__ == '__main__': run(window, WIDTH, HEIGHT)
关键代码说明
- 推升参数自定义:可通过调整
lift_duration(持续时长,毫秒)、lift_max_strength(最大力度)、lift_percentage(力度百分比)、lift_angle(推升角度)来适配不同需求 - 角落位置控制:
apply_lift_force函数中,corner_local_pos变量定义了力的作用角落,可根据需求修改为盒子的任意角落 - 时长控制逻辑:通过
pygame.time.get_ticks()获取当前时间,对比起始时间判断是否超出推升时长,超出后自动停止施力 - 方向适配:因Pymunk坐标系中y轴向上为负,所以力的y分量取负,确保推升方向向上
内容的提问来源于stack exchange,提问作者john
相关产品推荐
相关产品推荐

