You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Pygame弹跳球未启用能量损耗公式却出现反弹高度逐降问题排查

Python实现弹跳小球无能量损耗设置下反弹高度逐次降低问题

问题背景

  • 练习目标:跟随Java教程学习物理动画实现时,手动将示例代码改写为Python版本,通过手动复现理解运动逻辑原理,而非直接照搬代码
  • 预期逻辑:实现小球从y=25坐标下落、触底反弹效果,核心使用两个运动学公式:v = u + a*t、s = s + v*t + 1/2*a*t²;无能量损耗时小球应每次回弹至初始高度,添加能量损耗系数后可模拟真实弹跳效果
  • 异常现象:注释掉能量损耗计算代码、仅保留碰撞后速度反向dy = -dy逻辑时,小球反弹高度依然逐次降低,无法回到初始下落位置

问题复现代码

import pygame

# pygame window
WIDTH, HEIGHT = 800, 600
WINDOW = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Ball Game!")
FPS = 60

# physics variables
GRAVITY = 15
ENERGY_LOSS = 0.65
dt = 0.2

# player position variables
x = 400
y = 25
dx = 0
dy = 0
radius = 20

#colours
WHITE = (255, 255, 255)
GREEN = (0, 255, 0)

def draw_window():
    WINDOW.fill(WHITE)
    pygame.draw.circle(WINDOW, GREEN, (x, y), radius)
    pygame.display.update()


def main():
    clock = pygame.time.Clock()
    global x
    global y
    global dx
    global dy
    run = True

    while run:
        clock.tick(FPS)

        if y > HEIGHT-radius-1:
            y = HEIGHT - radius - 1
            # dy = dy * ENERGY_LOSS # Why is there energy loss without the formula!?!?!
            dy = -dy
        else:
            # velocity- acceleration formula
            dy += GRAVITY * dt
            # position formula
            y += dy * dt + (0.5 * GRAVITY * dt ** 2)

        draw_window()

if __name__ == '__main__':
    main()

根因定位

代码共有3处逻辑错误,共同导致了无能量损耗设置下的能量耗散:

  1. 运动学公式使用顺序错误
    匀变速位移公式s = v0*t + 0.5*a*t²要求使用帧初速度v0计算位移,之后再用v1 = v0 + a*t更新帧末速度。但代码中先更新了速度(把v0改成了v1),再代入位移公式计算,相当于位移被多算了a*t²的量,重力加速度效果被错误放大,运动计算本身就不符合匀变速规律。
  2. 物理更新与碰撞检测顺序错误
    代码在每帧开头先判断上一帧的最终位置是否触底,只有未触底时才执行本帧的速度、位置更新。碰撞发生时(即上一帧更新后小球已经穿到地面以下),本帧仅做位置重置和速度反向,完全跳过了物理量更新,且没有处理一帧内的穿模位移:小球在本帧内穿过地面后本该在碰撞点反弹、用剩余帧时间向上运动,但代码直接将位置拉回地面、整帧位移清零,每一次碰撞都会凭空损耗一部分能量。
  3. 物理时间步设置不合理
    代码设置FPS=60(即每帧真实时长约1/60≈0.016s),但硬编码dt=0.2作为物理时间步,是真实帧时长的12倍,会导致单帧内小球位移过大、穿模深度高,进一步放大碰撞时的能量损耗误差。

修复方案

针对以上问题做对应修改即可:

  • 换用稳定性更好、实现更简单的半隐式欧拉法做物理计算:先更新速度,再直接用新速度更新位置,不需要额外加0.5*a*t²项,避免公式顺序错误
  • 调整逻辑顺序:每帧先执行物理更新(速度、位置计算),再做碰撞检测,触底时重置位置、反向速度即可
  • 将物理时间步调整为和帧率匹配的值,减少单帧穿模误差
    修复后的核心循环代码如下:
# 修正dt为和60帧率匹配的时间步
dt = 1/FPS

def main():
    clock = pygame.time.Clock()
    global x, y, dx, dy
    run = True
    while run:
        clock.tick(FPS)
        # 先更新物理量
        dy += GRAVITY * dt
        y += dy * dt
        # 再做碰撞检测
        if y > HEIGHT - radius - 1:
            y = HEIGHT - radius - 1
            dy = -dy
            # 需要模拟能量损耗时打开下一行即可
            # dy *= ENERGY_LOSS
        draw_window()

修改后无能量损耗设置下,小球会稳定回弹到初始高度,不会出现逐次降低的现象。

内容的提问来源于stack exchange,提问作者RJrules64

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.30 23:06:28