恒美微站 Logo 恒美微站
  • 首页
  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心
  • 联系我们

Python游戏开发中的碰撞检测:从AABB到像素级判定的完整指南

  • 首页
  • 资讯中心
  • /
  • Python游戏开发中的碰撞检测:从AABB到像素级判定的完整指南

相关资讯

weibo-hide-all:批量将公开微博设为仅自己可见 2026/10/14 4:07:59
Qt数据展示控件详解:列表表格树形与联动布局 2026/10/14 4:07:59
从抓包到Python复现:App接口sign签名逆向实战解析 2026/10/14 4:02:58

最新资讯

微信小程序云开发超市库存管理系统源码解析与二次开发指南
CentOS 8.0安装Oracle 12cR2全流程指南:兼容性处理与高频报错排查
告别AI应用廉价感:5个可落地的设计决策
数据提取完整实践复盘:从需求拆解到稳定落地全流程
长活动按声音对齐后片尾漂移,起点通过不代表整段同步
Agent-Reach:多智能体通信与路由编排基础设施实践

今日推荐

IBM Verify Identity Access 修复 22 个漏洞:两个 9.8 严重 RCE 无需认证
小区物业管理系统
小型超市仓库管理系统

本周热门

UE动画修改实战:从资产编辑到重定向与蒙太奇驱动
统计随机数生成器攻击下的KLJN安全密钥交换协议Matlab仿真
政务API安全治理:资产测绘、低代码编排与行标对标实践

本月精选

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证
2026 大模型集体涨价:用 Python 做企业 Token 成本测算与选型避坑(附配置)

Python游戏开发中的碰撞检测:从AABB到像素级判定的完整指南

发布时间:2026/10/14 4:07:59
Python游戏开发中的碰撞检测:从AABB到像素级判定的完整指南 1. 游戏里的“撞墙”是怎么被算出来的两三年前我在一个模拟项目里写过一个马赛克风格的冒险小游戏角色在一个由瓦片地图构成的房间里自由移动。功能不复杂但开发过程中我最怕处理的就是碰撞检测角色动不动卡在墙缝里、跳起来穿过天花板、或者站在两个砖块中间疯狂抖动。后来我把整套碰撞逻辑重写了一遍才慢慢摸清这里面的门道。Python游戏里的碰撞检测说穿了就是“判断两个物体有没有重叠”。但实现方式决定了一个游戏是“手感扎实”还是“像在冰面上滑行”。作为游戏循环里每帧都会执行的核心逻辑碰撞检测的选型和实现直接影响到帧率、操作手感、Bug数量甚至后续如果要接物理引擎还得为它留好接口。这篇文章我按自己的踩坑经历来写尽量把“为什么这么做”讲透不是丢一段案例代码就完事。内容适合刚用Python写完生命游戏的初学者也适合已经在用pygame写小游戏、想把自己的碰撞逻辑理清楚的开发者。先说结论绝大多数2D小游戏用不到高大上的物理引擎老老实实把碰撞检测分成“检测”和“响应”两步用最简单可靠的矩形/圆形判定就能覆盖九成需求。但前提是你得把坐标系、物体尺寸、移动顺序这些细节想明白。2. 三种主流碰撞检测方案的取舍2.1 AABB矩形碰撞最快上手的基础方案AABB就是轴对齐包围盒意思是“这个矩形的边永远和坐标轴平行”。不管你的角色是像素小人还是卡车模型只要它是矩形且不旋转就可以用一个左、右、上、下四个边界来描述。两个AABB是否相交的判定极其简单在x轴上投影看有没有重叠在y轴上投影看有没有重叠两个方向都重叠就是碰撞。def rect_collide(rect1, rect2): return ( rect1.left rect2.right and rect1.right rect2.left and rect1.top rect2.bottom and rect1.bottom rect2.top )这套逻辑的核心思想叫“分离轴定理的二维简化版”只要存在任何一个方向上两个矩形是分开的那它们一定没有重叠。反过来两个方向都重叠那就一定碰撞。我特别想强调的是边界相等的情况。上面这个写法用的是严格不相等表示“恰好挨着不算撞”。如果你的游戏判断人物站在地板上时用的是“角色底部小于等于砖块顶部”那角色就不会被判定掉下去但如果用“小于”每一帧都会因为一像素的浮点误差导致抖动。这个细节在后面的Bug排查里还会展开讲。AABB的优势是计算量小一次判定只需要四次比较性能开销可以忽略。劣势也很明显假如角色的贴图是斜着画的AABB会把整块矩形都算进去实际看起来明明差半个身位却被判定撞上了。解决办法通常是把碰撞盒做小一点比贴图窄几像素也就是“视觉盒”和“碰撞盒”分离。2.2 圆形碰撞手感舒适但要处理半径差圆形碰撞判定更直观两个圆的圆心距离小于半径之和就是碰撞。用距离公式开根号这个操作比较费性能所以标准做法是两边都平方比较距离平方和半径和平方。def circle_collide(c1, c2): dx c1.x - c2.x dy c1.y - c2.y dist_sq dx * dx dy * dy r_sum c1.radius c2.radius return dist_sq r_sum * r_sum圆形碰撞的好处是方向无关旋转、斜着走都不影响判定准确度。很多俯视角游戏里的角色用一个圆当碰撞体手感明显比方块“滑顺”因为圆不会卡在墙角。但代价是圆形不能精确表示狭长物体的形状比如一把剑横着扫过来用圆会有很大的误差区域。我自己的经验是俯视角小游戏角色用圆形墙体和物块用AABB横板平台跳跃角色用AABB因为你需要精确知道脚底踩在哪块砖上。混合使用完全没问题圆对矩形的碰撞也就是先找圆心到矩形最近点再比较距离。def circle_rect_collide(circle, rect): closest_x max(rect.left, min(circle.x, rect.right)) closest_y max(rect.top, min(circle.y, rect.bottom)) dx circle.x - closest_x dy circle.y - closest_y return dx * dx dy * dy circle.radius * circle.radius这段代码是许多小游戏里“角色圆碰撞、地图矩形”的标准答案。本质上是在矩形边界上找离圆心最近的点如果这个点离圆心的距离不超过半径那就是碰撞。2.3 像素级碰撞万不得已才选的方案像素级碰撞是拿两张精灵图的像素透明度逐个比较碰到不透明像素就判定撞上。这个方法能做到“边缘完全贴合”非常适合弹幕游戏的受击判定、或者精细的2D格斗游戏。但性能开销和实现复杂度都是三个方案里最高的。真实项目里几乎没人直接去比像素而是先用AABB做快速排除只有两个框重叠了才进入像素级判定。pygame里提供了Mask类来做这件事用mask.from_surface()生成碰撞掩膜然后overlap()判断。mask1 pygame.mask.from_surface(sprite1) mask2 pygame.mask.from_surface(sprite2) offset (sprite2.left - sprite1.left, sprite2.top - sprite1.top) if mask1.overlap(mask2, offset): handle_collision()我需要提醒的是from_surface()的默认阈值为1但实际用下来很多半透明边缘会被算进去建议把阈值调到128或更高只把比较实的像素当作碰撞体。否则你会看到角色“明明没碰到画出来的边缘却被挡在天上”。这三种方案不是互斥的合理的分层策略是先用AABB粗筛再用圆或Mask精确判定。游戏里的碰撞体不会只有一个而是由多个基础碰撞体组合出来的复杂形状。3. 实操过程从零实现一个稳定的碰撞系统3.1 数据模型与移动顺序是地基很多初学者写碰撞检测的唯一代码就是“移动完再判断是否重叠重叠就退回去”。这能跑但手感极其拉胯。真正稳定的做法是把移动和碰撞分成两套坐标系计算。我建议用三组数据描述一个游戏物体位置坐标、速度向量、碰撞盒尺寸。不要直接用屏幕坐标算而是维护一个“逻辑坐标”渲染时再转换。这样碰撞检测的结果不依赖当前渲染状态也方便你以后加慢动作、回放这类功能。核心思路是把一帧的位移拆成“水平方向移动”和“垂直方向移动”两步分别执行、分别碰撞。这样能解决掉九成平台跳跃类游戏的卡墙问题。# 伪代码分轴移动 old_x, old_y self.x, self.y self.x self.vx * dt resolve_horizontal_collisions() self.y self.vy * dt resolve_vertical_collisions()这一步的意义在于当你水平撞墙时只有x方向被修正y方向不受影响之后y的正常下落不会被“卡在墙角”打断。如果是先移动再统一修正极容易出现撞墙后角色被往后推回一整个对角线位移的情况表现就是“碰到墙边会瞬移”。我自己职业生涯里见到的最常见的碰撞Bug不是判定错了而是移动顺序错了。一个简单的顺序调整抵得上加一整套复杂修正算法。3.2 碰撞响应不是说“退回去”就完了检测到碰撞之后接下来“如何响应”更考验功底。新手做法是直接把角色位置改回碰撞前的位置结果就是对角撞墙时角色原地卡死再也动不了。正确做法是依据碰撞方向做“穿透深度修正”。横版游戏的标准姿势如果角色在水平移动中撞到左墙就把角色的left设为墙的right撞右墙就把right设为墙的left。垂直方向同理撞地就把bottom设为砖块top撞天花板就把top设为砖块bottom。def resolve_collision(self, tile_rect): # 水平方向 if self.vx 0: self.right tile_rect.left self.vx 0 elif self.vx 0: self.left tile_rect.right self.vx 0 # 垂直方向 if self.vy 0: self.bottom tile_rect.top self.vy 0 self.on_ground True elif self.vy 0: self.top tile_rect.bottom self.vy 0注意这里有个小细节把穿透修正和速度置零同时做。如果你只改位置不把速度清零下一帧又会穿回去。这也是我调试时必查的第一个点。更进阶一点的方案是计算“分离向量”找到一个把两个碰撞体推开的极小位移。实现的通用姿势是计算重叠的宽度和高度取较小的一维来分离。这在俯视角游戏里更好用因为俯视角没有“地面”概念任意方向的碰撞响应都一样。3.3 瓦片地图碰撞逐块查还是空间划分瓦片地图是2D横版游戏的标配。碰撞体是很多个小方块最简单的实现是角色每帧遍历所有瓦片判断。地图小的时候无所谓大了就废了比如100x100的地图每帧做一万次矩形判断Python撑不住多久。所以要用空间划分把地图按瓦片网格存好每个瓦片大小为固定值比如32x32角色移动后只检查自己覆盖到的网格范围。用整数除可以算出角色覆盖了哪些行和列start_col max(0, self.left // TILE_SIZE) end_col min(map_width - 1, self.right // TILE_SIZE) start_row max(0, self.top // TILE_SIZE) end_row min(map_height - 1, self.bottom // TILE_SIZE) for row in range(start_row, end_row 1): for col in range(start_col, end_col 1): if map_collision[row][col]: resolve_collision(pygame.Rect(col * TILE_SIZE, row * TILE_SIZE, TILE_SIZE, TILE_SIZE))这个做法的核心价值是把“全地图一万次判定”降成“角色周围的几次判定”。同理如果你在做的是有大量子弹的弹幕游戏子弹之间互相检测时也可以用“将屏幕划分为多个格子只检测同一格或相邻格的子弹”的方式优化。我在实际项目里还测试过一个方案把所有静态碰撞瓦片预先烘焙成几个“合并后的矩形列表”运行时只和这些大矩形碰撞。效果也很明显但实现复杂度偏高普通小游戏用网格遍历就够了。再提一个影响观感的细节瓦片地图碰撞盒应与贴图对齐。很多美术同学画砖块时会画一些花边、挂饰贴图上下会有几个像素的“假想延伸”。如果你直接拿贴图尺寸当碰撞盒会有大量“视线没碰到却被挡住”的尴尬情况。建议在场景编辑阶段预留一层“碰撞图层”只画纯色矩形格子视觉贴图和碰撞盒彻底分离。3.4 地板与屋顶单方向平台与头顶判定平台跳跃类游戏里有一种特殊的碰撞需求单向平台就是角色可以从下方跳上去、或者从侧面穿过去但站在上面不会掉下来。这类平台的碰撞判定和普通砖块完全不同。单向平台的典型实现逻辑只在角色“从上往下落”且“角色的底部之前位于平台顶部的上方”时才触发碰撞。换句话说你只能接住从上面落下来的物体不能阻挡从下面往上顶的物体。if (player.vy 0 and player.bottom platform.top and player.bottom player.vy platform.top FALL_THROUGH_TOLERANCE): player.bottom platform.top player.vy 0 player.on_ground True注意这里比较的是“上一帧的底部”和“当前帧的底部”不是当前帧的位置。如果角色下落速度特别快一帧跨越了整个平台厚度只靠位置判断会直接漏过判定也就是常说的“隧道效应”。解决方式有两种把一帧的运动拆分成多个子步长逐段检测或者记录物体的“前一帧位置”看运动线段是否与平台相交。头顶判定也是同样的思路。很多平台上角色顶到天花板后如果刚好贴着天花板横向移动会因为“头顶的瓦片和侧面的瓦片同时碰撞”而出问题。解决方法是按“先水平、后垂直”的顺序检测并且水平碰撞修正后立即转向垂直碰撞检测确保两个方向互不干扰。我自己的习惯是把“脚底地面”和“头顶天花板”分别用一个薄碰撞矩形表示和主体碰撞盒分开。好处是你可以在游戏里实现“下蹲时缩小碰撞盒、站立时恢复碰撞盒”这类复杂的角色体型变化而不用担心主体碰撞逻辑被牵扯。4. 常见Bug与排查技巧实录4.1 角色在地板上疯狂抖动这个Bug我见过太多次了。现象是角色站在地面上但画面里一直在上下轻微颤动或者左右滑移。根本原因是重力每帧给角色增加向下的速度下一帧碰撞检测发现角色穿透地板又把y坐标拖回去再下一帧又被重力拉下去……形成一个几像素的循环振荡。解决办法有两个方向一个是在“站在地面上”的帧把重力产生的速度清零另一个是设置“地面容忍阈值”允许角色底部和地板顶部有几像素的误差不触发重新判定。if self.on_ground: self.vy 0 # 不累加重力 else: self.vy GRAVITY * dt self.vy min(self.vy, MAX_FALL_SPEED)这里必须注意顺序先更新水平位置和速度再检测水平碰撞然后更新垂直速度和位置再检测垂直碰撞。如果重力累加速度、移动到新位置、碰撞修正这三件事的顺序乱了抖动就很难根治。另外浮点误差也不要小看。用整数坐标存储角色位置是个笨但有效的办法pygame的Rect会自动取整反而能避免很多肉眼可见的微抖动。4.2 高速移动直接穿墙子弹以每帧100像素的速度飞行而墙壁厚度只有32像素常见结果是子弹“嗖”一下穿过了墙完全没触发碰撞。这就是前面提到的隧道效应。处理方式按项目规模分三档第一档是简单粗暴地把速度限制在最小物体厚度以下这种方法只适合偶尔一次的小Demo第二档是把移动拆成多个小步长比如每帧位移100像素那就拆成10次每次移动10像素并检测碰撞质量不错且实现简单第三档是用“扫掠矩形”或者射线检测把移动路径看作一条线段判断线段与碰撞体的相交性。我个人推荐第二档优先。因为拆步长的方案不改变你已有的碰撞判断函数本质上是把“一帧”拆细为“多个子步”遇到复杂场景也好排查。扫掠矩形的数学推导写起来好看但圆扫、矩形扫、旋转扫每种都要单独处理业务代码会越来越复杂。还有一个实际经验很多“穿墙”问题不是速度太快导致的而是“上一帧的位置记录”没保存好。碰撞检测用旧的上一帧坐标去判断路径穿越结果上一帧坐标在移动前就被覆盖了。排查时可以打印最近三帧的坐标快照一眼就能看出问题在哪一帧发生的。4.3 角落卡死与墙角斜滑角色斜着跑向一个凸角时经常出现的情况是身体已经探进角落两个方向都同时碰撞修正逻辑按“先后再左”顺序处理角色直接被卡在原地动弹不得表现为“吸在墙角”。高级感一点的解法是“滑动修正”先计算水平方向的碰撞修正然后在修正后的新位置基础上再做垂直方向检测。因为水平修正已经不让角色嵌入墙壁垂直检测通常就不会再和同一堵墙发生碰撞。如果还是卡说明碰撞体形状或贴图比网格大需要缩小角色的碰撞盒。简易但有效的做法是给角色碰撞体加一个“嘴角圆角”效果也就是让碰撞盒的四角各去掉一小块。真做起来并不复杂把AABB稍微缩小几像素必要时用圆形碰撞替代四个角附近的检测。这里也要提一个我之前犯过的错为了让角色能“刚好挤过”两堵墙之间的缝隙我把碰撞盒调小了结果导致角色半个身子探进墙里还能继续走看起来像是“穿模”。调碰撞盒胖瘦时宁可手感略保守也不能让视觉穿帮。4.4 碰撞体与渲染画面错位碰撞盒是对的但打击判定的位置明显偏离画面里的刀光这种情况多半是因为用了不同来源的坐标一部分碰撞体用世界坐标另一部分用相机偏移后的屏幕坐标。特别是引入摄像机跟随系统之后两个坐标系混用会让Bug变得非常随机每次角色走到屏幕不同位置偏移量都不同。我踩过这个坑后定下一条规矩所有逻辑层统一使用世界坐标渲染层在每帧结束时做一次坐标转换。碰撞检测、AI寻路、拾取判断全部走世界坐标系只有最后绘制时才减去相机位置。screen_x world_x - camera.x screen_y world_y - camera.y如果调试时发现碰撞框和渲染图像错位第一件事就是检查转换函数有没有重复应用比如在渲染函数里减了一次相机偏移在碰撞函数里又减了一次。第二个常见原因是精灵图原点设置不一致有的贴图以左上角为锚点有的以中心为锚点碰撞盒跟着锚点走就偏了。美术资源和代码的坐标约定必须在一开始就锁死。5. 再往下深挖空间索引与物理引擎的边界前面聊了各种碰撞检测的写法和排错但如果你的游戏规模再大一点角色数量上百、互相之间也要检测碰撞那就该考虑空间索引了。最简单的选择是网格空间哈希。把整个地图切割成固定大小的格子每个格子记录“当前存在于本格子的物体ID列表”。每帧更新物体时重新登记归属检测碰撞时只取出同一格子和相邻几个格子的物体来做两两判定。这里需要考虑清楚的问题是网格大小要怎么取经验法则是以世界中物体的平均尺寸作为网格边长。如果物体大小分布特别不均匀就会退化比如一个巨型怪物占了上百个格子每次检测都要和它比对性能损耗直接拉满。更精细的做法是四叉树或BVH树按物体实际分布来划分空间。这些数据结构的手写实现需要一定的算法功底pygame生态里也没有特别通用易用的库。大多数人做到网格哈希这步就已经够了再复杂的直接用现成物理引擎更划算。物理引擎是另一个话题。pygame自带的sprite.Sprite本身不带物理模拟只有碰撞检测的辅助方法。如果你想要连续的物理行为——弹性、摩擦力、关节连接、刚体堆叠——就应该考虑接入现成的物理引擎。Python生态里典型的方案是一个2D物理库它支持刚体、碰撞形状、关节和触发器用起来比自己在原子上硬堆手感要好一个量级。不过我需要提醒一句物理引擎不是银弹。它的坐标系、单位像素/米、时间步长都有自己的约定接入初期会有一段不小的学习成本。而且物理引擎更擅长模拟“受力的物体”并不擅长你手写的“平台跳跃手感”。如果你做的是需要精确跳跃高度、精确滞空时间的平台游戏建议还是保留自己写的碰撞系统如果你要模拟一堆圆球砸碎一堆方块那就果断接引擎。我在一个模拟项目里验证过混用模式玩家角色用自研碰撞逻辑其余物理物体交给引擎模拟角色不会穿模。做法是把引擎的物理世界单独跑在一个“模拟空间”里每帧把角色位置同步成引擎世界中的刚体位置但不参与它的动力学计算。这套方案在性能调优上有额外消耗但换来的是“自己的角色自己做主”的手感优势。真心建议开发者在做选型之前先把自己游戏的核心玩法定义清楚需要的是“精确的操作感”还是“丰富的物理交互”。游戏项目开发中碰撞检测不是孤立的判定代码而是一条贯穿坐标系、数据结构、游戏循环的骨架。把基础算法原理弄懂比盲目堆代码要重要得多。如果你也在做Python小游戏我建议你从最简单的AABB开始先把手感调到不卡墙、不穿墙、不抖动的状态再逐步引入更复杂的形状和优化策略。这条路踏实走完你踩过的坑会变成你最强的技术储备。

关于恒美微站

恒美微站专注于为个体商户、工作室提供极简自助建站服务,让每个人都能轻松拥有专业网站。

快速链接

  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心

服务项目

  • 可视化建站
  • 拖拽编辑
  • 主题定制
  • SEO 优化
  • 网站托管

联系方式

  • 📍 地址:北京市朝阳区建国路 88 号
  • 📞 电话:400-888-8888
  • ✉️ 邮箱:info@hmyw.cn
  • 🕐 时间:周一至周日 9:00-18:00

© 2024 恒美微站 hmyw.cn 版权所有 | 京 ICP 备 12345678 号