news 2026/9/20 6:24:51

Pygame 弹球模拟器(Bouncing Ball Simulator):基于重力与弹性碰撞的物理仿真入门实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Pygame 弹球模拟器(Bouncing Ball Simulator):基于重力与弹性碰撞的物理仿真入门实战
  • 示例工程

【免费下载链接】python-mini-projects

A collection of simple python mini projects to enhance your python skills

项目地址:https://gitcode.com/gh_mirrors/py/python-mini-projects
点击查看免费下载

python-mini-projects仓库中的projects/Bouncing_ball_simulator是一个用 Pygame 实现的轻量级物理仿真示例:5 个小球在一个 800×600 的容器内受重力加速下落,并与容器底部、左右墙壁及顶部发生弹性碰撞后持续反弹。本文以该项目的 README.md 为主线,结合 ball_bounce.py 源码逐行拆解其物理模型、碰撞检测逻辑与主循环结构,帮助你快速掌握用 Pygame 编写"重力 + 弹性碰撞"仿真程序的核心套路,并了解如何调整参数获得不同的弹跳效果。

项目概览:容器内的重力弹跳仿真

项目描述(来自 README.md)指出,该脚本展示的是若干小球在重力作用下于一个容器内弹跳的仿真过程,小球不仅受重力影响,还会与容器的底部侧壁发生碰撞。

在 ball_bounce.py 的源码注释中,作者进一步说明:

  • 程序模拟了5 个球在重力加速度作用下的弹跳
  • 伴随与容器墙壁的弹性碰撞(elastic collision);
  • 作者将其定位为一个"看起来很有趣"的可视化演示。

从源码结构看,整个程序只有一个类ball和一个主循环,没有引入复杂框架,非常适合作为 Pygame 入门或物理仿真入门的教学示例。项目全部资源文件如下:

文件作用
ball_bounce.py主程序:仿真逻辑、碰撞检测、渲染循环
background-img.jpg容器背景图(1000×666 纯色背景)
ball.png小球图像资源(32×32)
requirements.txt依赖声明:pygame==2.0.2

运行环境与依赖准备

根据 README.md 的 Prerequisites 部分,运行本项目需要满足两个前提:

  1. Python 3(脚本基于 Python 3 编写);
  2. pygame 模块

依赖版本在 requirements.txt 中固定为:

pygame==2.0.2

安装依赖的推荐方式是按 README 给出的命令,直接通过 requirements 文件安装:

pip3 install -r requirements.txt

提示:pip3用于明确指向 Python 3 的包管理器;如果你使用虚拟环境或python3 -m pip,效果等价。固定版本pygame==2.0.2能保证渲染接口行为一致,避免因 pygame 2.x 不同小版本的 API 差异导致运行异常。

快速运行:一条命令启动仿真

README 的 "How to run the script" 部分给出了标准运行流程:

  1. 进入源码所在目录projects/Bouncing_ball_simulator/
  2. 打开终端,执行:
python3 ball_bounce.py

运行后会出现标题为"Ball Bounce Simulation"的 800×600 Pygame 窗口,背景为background-img.jpg,5 个橙色小球(ball.png)从随机位置开始运动,在重力作用下下落、与容器壁碰撞并持续弹跳,直到你关闭窗口(触发pygame.QUIT事件)为止。

需要注意:程序通过相对路径加载图像资源(pygame.image.load('background-img.jpg')pygame.image.load('ball.png')),因此必须保持在projects/Bouncing_ball_simulator/目录下运行,否则会因找不到图片文件而报错。

源码级解析:物理模型与碰撞检测

4.1 窗口初始化与资源加载

import pygame, time, random pygame.init() screen = pygame.display.set_mode((800, 600)) background = pygame.image.load('background-img.jpg') pygame.display.set_caption('Ball Bounce Simulation')
  • pygame.init()初始化所有 pygame 模块;
  • set_mode((800, 600))创建 800×600 像素的仿真容器窗口;
  • load('background-img.jpg')加载容器背景图,主循环中每帧通过screen.blit(background, (0, 0))重绘;
  • set_caption设置窗口标题。

4.2ball类:属性设计与物理常量

class ball: ball_image = pygame.image.load('ball.png') g = 1 def __init__(self): self.velocityX = 4 self.velocityY = 4 self.X = random.randint(0, 768) self.Y = random.randint(0, 350)

类属性与实例属性的职责划分:

属性类型值/取值范围含义
ball_image类属性ball.png所有小球共享同一张图像资源
g类属性1每帧施加的重力加速度(像素/帧²)
velocityX实例属性初始4水平方向速度(像素/帧)
velocityY实例属性初始4垂直方向速度(像素/帧),向下为正
X实例属性随机0~768水平坐标(像素)
Y实例属性随机0~350垂直坐标(像素)

两个值得注意的细节:

  1. 初始位置随机化X0~768随机,Y0~350随机。768 = 800 - 32350均考虑了小球图像尺寸(32×32)与窗口尺寸,确保小球出生时大致处于容器上半部分且不越出左右边界。
  2. 重力常数作为类属性g被定义为类属性而非实例属性,从设计意图看,它代表作用于所有小球的统一重力场,无需为每个球单独存储。

4.3move_ball():逐帧物理更新与碰撞响应

def move_ball(self): # 重力:每帧给垂直速度叠加向下的加速度 self.velocityY += ball.g # 按速度更新位置 self.X += self.velocityX self.Y += self.velocityY # 左右墙壁碰撞:水平速度取反 if self.X < 0 or self.X > 768: self.velocityX *= -1 # 顶部碰撞:仅在向上运动时取反并夹紧 if self.Y < 0 and self.velocityY < 0: self.velocityY *= -1 self.Y = 0 # 底部碰撞:仅在向下运动时取反并夹紧 if self.Y > 568 and self.velocityY > 0: self.velocityY *= -1 self.Y = 568

这段代码是仿真的核心,本质上是离散时间步的欧拉积分(每帧一小步):

  1. 重力积分velocityY += ball.g,每帧垂直速度增加 1 个像素/帧,模拟重力持续加速下落;
  2. 位移更新X += velocityX; Y += velocityY,用当前速度更新位置;
  3. 左右墙壁碰撞:当X < 0X > 768(即小球越出左右边界,768 = 800 - 32考虑球宽)时,velocityX *= -1使水平速度反向——这就是"弹性碰撞"在代码中的体现:速度大小不变、方向反转;
  4. 顶部碰撞:当Y < 0且仍在向上运动(velocityY < 0)时,垂直速度取反并将位置夹紧到Y = 0,避免小球穿出顶部;
  5. 底部碰撞:当Y > 568568 = 600 - 32,考虑球高)且仍在向下运动(velocityY > 0)时,速度取反并夹紧到Y = 568,小球落在容器底部后反弹。

碰撞条件中带方向判断velocityY < 0/velocityY > 0)是避免"边界抖动"的关键:只有球正在朝边界运动时才触发反弹,防止球处于边界且已反向运动时被重复取反、卡在边界附近来回振荡。这是 2D 边界碰撞处理中值得借鉴的实践。

4.4 渲染与主循环

def render_ball(self): screen.blit(ball.ball_image, (self.X, self.Y)) Ball_List = [ball(), ball(), ball(), ball(), ball()] running = True while running: for event in pygame.event.get(): if event.type == pygame.QUIT: running = False time.sleep(0.02) screen.blit(background, (0, 0)) for ball_item in Ball_List: ball_item.render_ball() ball_item.move_ball() pygame.display.update()

主循环的固定节奏为:

  1. 事件处理:轮询pygame.event.get(),收到pygame.QUIT(点击窗口关闭按钮)时退出循环;
  2. 帧率控制time.sleep(0.02),每帧间隔约 20ms,对应约 50 FPS 的刷新节奏,同时也决定了物理仿真的时间步长;
  3. 清屏重绘:先blit背景图覆盖上一帧画面,再遍历Ball_List依次渲染并更新每个小球;
  4. 画面提交pygame.display.update()将绘制结果显示到窗口。

Ball_List一次创建 5 个ball()实例,因此"5 个球"的数量就由这行列表字面量决定——想增加或减少小球数量,直接增删这里的元素即可。

参数调优:改变弹跳效果的关键旋钮

所有物理行为都由少量常量控制,调整它们即可获得截然不同的观感:

参数位置调整方向效果
ball.g类属性调大 → 下落更快、弹跳更急促;调小 → 失重感、漂浮感控制重力强度
velocityX/velocityY__init__调大 → 运动更激烈;设为 0 → 只做垂直弹跳控制初始发射速度
time.sleep(0.02)主循环调小 → 仿真更快;调大 → 慢动作回放控制帧率与时间步长
set_mode((800, 600))初始化改变容器尺寸时,需同步调整边界常量768568容器大小
Ball_List元素个数主循环前增删ball()实例同时弹跳的小球数量

例如,若将窗口改为(1024, 768),则左右边界应相应改为1024 - 32 = 992、底部边界改为768 - 32 = 736,同时建议同步调整Y的随机上限。

从源码结构看可扩展方向

当前实现是"重力 + 墙壁弹性碰撞"的最小可用版本,从源码结构看,以下扩展点都有清晰的落点:

  • 小球之间的碰撞:目前move_ball()只处理球与容器的碰撞,Ball_List中各球彼此穿透。可在主循环中增加两两距离检测,当球心距离小于直径时交换/反转速度分量;
  • 能量损耗(非完全弹性):将velocityY *= -1改为velocityY *= -0.9(恢复系数 < 1),即可模拟每次反弹损失能量的阻尼效果,小球最终会静止在底部;
  • 随机化初始速度__init__中的velocityX/velocityY目前固定为 4,可改为random.randint(...),让每次运行的运动轨迹都不同;
  • 交互控制:在事件循环中监听键盘/鼠标事件,动态调整ball.g或增加新球。

小结

Bouncing_ball_simulator用不到 60 行代码完整演示了 Pygame 物理仿真的标准骨架:初始化窗口 → 加载资源 → 定义运动对象(速度积分 + 边界碰撞) → 主循环(事件、更新、渲染)。其重力积分、带方向判断的边界碰撞、以及用类属性统一管理物理常量的设计,都是可复用到其他 2D 仿真项目(如弹幕、粒子系统、平台跳跃游戏)的基础模式。按 README.md 的指引安装依赖并运行 ball_bounce.py,再结合本文的参数调优表动手修改几个常量,就能直观感受到每个参数对弹跳效果的影响。

  • 示例工程

【免费下载链接】python-mini-projects

A collection of simple python mini projects to enhance your python skills

项目地址:https://gitcode.com/gh_mirrors/py/python-mini-projects
点击查看免费下载

相关推荐

上一篇:突破浏览器性能瓶颈:transformers.js中卷积网络(CNN)的WebGPU加速实现
下一篇:SkyReels-V2 无限长视频生成:从零跑通第一条长视频的新手教程

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/20 6:24:17

BrewUI教程:可视化管理Homebrew,解决Intel Mac安装与卸载残留问题

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/20 6:23:52

npx add-skill 实战:Agent Skill 安装与工程化指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/20 6:19:01

本地AI工作台实战:用WorkBuddy自定义指令与Skill搭建述职报告生成器

上季度述职那天&#xff0c;我走进会议室只带了一台笔记本。汇报到一半的时候&#xff0c;老板突然打断了我的节奏&#xff0c;把 PPT 往前翻了两页&#xff0c;说&#xff1a;“这份总结有感觉&#xff0c;谁帮你写的&#xff1f;”我指了指屏幕上正在后台跑任务的终端——一个…

作者头像 李华
网站建设 2026/9/20 6:18:20

Cursor 跑 Android app 生成:Key 用 TaoToken

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/20 6:14:58

TensorRT部署实战:YOLO转ONNX到推理加速的五大避坑指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华