资讯动态

Python游戏开发碰撞检测实战:原理、实现与性能优化

发布时间:2026/10/2 3:13:31 来源:尧图企业网站定制
上周末我调一个用Python写的弹球小游戏差点把电脑屏幕盯出洞来球明明碰到了挡板边缘却直接穿过去落到底部挡板像一个透明的鬼魂。后来排查半天才发现问题出在我自己写的碰撞检测逻辑上——判断条件写反了把“不相交”当成了“相交”。这种经历我相信每个做游戏开发的Python爱好者都遇到过。碰撞检测是游戏开发中绕不开、也最考验基本功的核心机制它决定了玩家是觉得“手感好”还是“这游戏是半成品”。这篇文章我想系统聊聊Python游戏里的碰撞检测怎么实现从最常用的数学判定讲到实际项目里的性能优化再附上我踩过的坑。如果你正准备用Python做自己的第一款小游戏或者已经写过一些Pygame代码但觉得碰撞判定总是不对劲这篇文章应该能帮你省下好几个晚上的排查时间。我会把AABB矩形碰撞、圆形碰撞的原理拆开讲清楚再给一个可直接运行的挡板接球小游戏做实战演示顺便把高频的穿透、误判、性能卡顿问题一并解决。1. 碰撞检测的底层逻辑与方案选型1.1 游戏世界里的“物理接触”到底是什么很多人第一次接触游戏开发时有个误解以为游戏里的人物碰撞检测是靠“看图像有没有重叠”来完成的。比如一个角色图片和一个怪物图片碰到一起程序去比对两张图片的像素透明区域。这个思路理论上可行但实际开发时极少有人直接用原因是像素级比对太吃性能。真实游戏里的做法是“抽象化”。玩家角色在代码里不是一个复杂的角色图片而是一个带坐标的几何体。2D游戏里最常见的是矩形和圆形3D游戏里则会用到立方体、球体、胶囊体等。这些几何体包围住角色的主体部位碰撞检测判断的就是这些几何体之间有没有交集。角色图片再花哨碰撞判定也只看那个看不见的“方框”或“圆圈”有没有碰到另一个“方框”或“圆圈”。这个思路的灵感其实和物理世界很像。你走在路上和别人擦肩而过两个人并没有真的“像素级接触”你的身体轮廓和对方的身体轮廓只要没有重叠区域就不会互相推挤。游戏里的碰撞检测也是在模拟这种“轮廓重叠判断”只不过为了性能把复杂的轮廓简化成了几个基本几何形状的组合。1.2 主流碰撞检测方案怎么选我做过几个小项目后感觉Python游戏开发里真正常用的碰撞检测方案就那么几种每种都有明显的适用场景和性能特点。AABB碰撞检测是我用得最多、也最推荐初学者掌握的一种。AABB的全称是Axis-Aligned Bounding Box也就是“轴对齐包围盒”。这个词听起来高深其实就是指矩形框的长和宽都平行于坐标轴不会发生旋转。它的判定条件只需要四次比较运算速度极快非常适合2D平台游戏里的人物、砖块、奖励道具等物体。缺点也很明显如果物体旋转了矩形框没法跟着转碰撞区域会变得不精确。圆形碰撞检测是另一类经典方案。逻辑上比矩形还简单判断两个圆的圆心距离是否小于半径之和。它不关心物体是否旋转对称性让碰撞结果非常稳定适合做球类、子弹、粒子效果这类近似圆形的物体。还有一种像素级碰撞检测精确但昂贵。它需要逐像素比对待检测对象的非透明区域适用场景很窄一般只用来处理玩家与复杂地形或图标类物体的交互。Python在这种场景下就算用Pygame的Mask模块依然会有明显的性能压力我建议除非有强需求否则别轻易上。我把这三种方案做了一张对比表方便你按需选择方案判断方式优点缺点典型场景AABB矩形比较两个矩形边界是否重叠计算量极小实现简单物体旋转后不准确平台跳跃、格斗游戏角色圆形比较圆心距离和半径之和精度高对旋转不敏感只能近似圆形物体弹幕、足球、台球像素级逐像素比对非透明区域碰撞结果最精确性能开销大像素风格游戏的复杂地形实际项目里我几乎不会只用一种方案。比如一个角色可以先用AABB做粗检测检测通过后再用圆形或像素级做细检测这样既能保证性能又能提升手感也就是游戏开发里常说的“多阶段碰撞过滤”。2. 环境搭建与基础框架2.1 为什么选择Pygame做碰撞检测实验做Python游戏开发绕不开Pygame这个库。它是Python生态里最成熟的2D游戏框架封装了窗口创建、事件处理、图像绘制、声音播放等底层能力。选择Pygame做碰撞检测实验还有一个额外好处它自带Rect类已经实现了矩形对象常用的坐标、宽高属性和碰撞方法非常适合做原理解读。但我要提醒一点如果只是为了学习碰撞检测别一上来就依赖内置的colliderect方法。这个方法确实好用可它是个“黑盒”你只知道结果不知道过程。我在带朋友入门时总是让他们先自己写一遍碰撞判断哪怕代码写得土一点也没关系。手写一遍之后你才能真正理解碰撞检测的本质后续遇到复杂问题才知道从哪儿下手。Pygame的安装也很简单联网环境下在终端里执行一遍pip install pygame即可。装好后可以顺手写一行python -c import pygame; print(pygame.ver)确认版本避免后边调试时发现装的是旧版本。2.2 30行代码搭出可测试的游戏框架不管做什么游戏Pygame的基础结构都是一样的初始化窗口、创建游戏对象、进入主循环、处理事件、更新状态、绘制画面。碰撞检测就发生在“更新状态”这个环节。这里我先给一个最小框架后面所有碰撞测试都可以直接套进去import pygame import sys pygame.init() SCREEN_WIDTH 800 SCREEN_HEIGHT 600 FPS 60 screen pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT)) pygame.display.set_caption(碰撞检测实验) clock pygame.time.Clock() player_rect pygame.Rect(100, 100, 40, 40) obstacle_rect pygame.Rect(300, 200, 80, 80) while True: for event in pygame.event.get(): if event.type pygame.QUIT: pygame.quit() sys.exit() keys pygame.key.get_pressed() if keys[pygame.K_LEFT]: player_rect.x - 5 if keys[pygame.K_RIGHT]: player_rect.x 5 if keys[pygame.K_UP]: player_rect.y - 5 if keys[pygame.K_DOWN]: player_rect.y 5 screen.fill((20, 20, 20)) pygame.draw.rect(screen, (0, 255, 0), player_rect) pygame.draw.rect(screen, (255, 0, 0), obstacle_rect) pygame.display.flip() clock.tick(FPS)这个框架里有几个细节值得说。pygame.Rect的坐标是左上角位置很多新手会把它误当成中心位置这是个高频误区。比如你想让一个物体显示在屏幕正中间写成Rect(WIDTH // 2, HEIGHT // 2, 40, 40)得到的其实是“物体左上角在中间”整个物体会往右下偏移20像素。正确写法应该是Rect(WIDTH // 2 - 20, HEIGHT // 2 - 20, 40, 40)。另一个细节是clock.tick(FPS)。它的作用是让主循环控制在每秒60帧以内避免游戏在性能好的电脑上跑得飞起、在性能差的电脑上慢如蜗牛。帧率不稳定会直接影响碰撞检测的准确性这个问题我留在第5章详细说。3. 核心实现矩形与圆形的碰撞判定3.1 AABB矩形碰撞检测四次条件判断搞定矩形碰撞检测的数学逻辑其实很简单。两个矩形如果相交意味着它们在X轴上的投影区间有重叠同时在Y轴上的投影区间也有重叠。反过来只要它们在任意一个轴上没有重叠整个矩形就不可能相交。我来写个自带的判断函数你不用Pygame的方法也能实现def check_aabb_collision(rect1, rect2): if rect1.x rect1.width rect2.x: return False if rect2.x rect2.width rect1.x: return False if rect1.y rect1.height rect2.y: return False if rect2.y rect2.height rect1.y: return False return True这段代码的逻辑可解读为先判断rect1的最右边是否在rect2左边界的左边再反向判断rect2的最右边是否在rect1左边界的左边同理处理Y轴。如果没有出现任何一种“彻底远离”的情况说明它们必然相交。这里要特别强调的是边界重合问题的处理。假设rect1右边等于rect2左边那这两个矩形其实只是贴在一起并没有真正重叠。按上面代码第一个判断是rect1.x rect1.width rect2.x用的是严格小于号所以恰好在同一位置时条件不成立函数会继续往下判断最后返回True。也就是说我的写法把“边贴边”判定为碰撞。在某些游戏里你可能希望在两者相碰前一像素就触发事件那就可以根据实际手感调整判断条件把改成即可。Pygame内置的colliderect背后大概就是这种逻辑。只不过它还额外做了优化比如先把两个矩形空交集的条件理顺再借用C语言底层加速所以框架自带方法的执行速度通常比自己写快一些。3.2 圆形碰撞检测勾股定理的经典应用圆形碰撞检测的数学原理与应用场景几乎每一个做球类游戏的人都绕不开。两个圆是否碰撞就看两点之间距离是否小于等于半径之和。两点距离的计算用到了勾股定理就是初中数学里三角形直角边和斜边的关系。import math def check_circle_collision(c1_center, c1_radius, c2_center, c2_radius): dx c1_center[0] - c2_center[0] dy c1_center[1] - c2_center[1] distance math.hypot(dx, dy) return distance c1_radius c2_radiusmath.hypot一步就完成了平方和开根号的计算比手写math.sqrt(dx * dx dy * dy)更简洁。但如果在碰撞检测中调用得非常频繁开根号运算依然是个不低的成本。这时可以用平方来比较因为距离的比较和距离平方的比较结果是完全一致的def check_circle_collision_fast(c1_center, c1_radius, c2_center, c2_radius): dx c1_center[0] - c2_center[0] dy c1_center[1] - c2_center[1] dist_sq dx * dx dy * dy radius_sum c1_radius c2_radius return dist_sq radius_sum * radius_sum我实测过这种方式在同时检测几百对物体时性能差距能到一两倍。原因在于CPU执行开根号运算比加减乘整除慢不少而游戏逻辑里碰撞检测可能一帧就要执行几千次。这种“平方替代开根号”的技巧在很多碰撞检测库中被广泛使用你去看一些开源游戏的代码会发现他们判断距离时都在比平方。3.3 矩形与圆形的碰撞判定最容易出错的组合游戏里最常见的其实是矩形和圆形的碰撞比如玩家角色是矩形挡板飞来的球是圆形。这个判定比矩形和矩形、圆形和圆形都要复杂我也是在实际项目里被它折磨过才彻底搞清楚。判断矩形和圆是否碰撞核心思路是找到矩形边框上距离圆心最近的那个点然后计算这个点到圆心的距离如果这个距离小于等于半径就说明碰撞了。def check_rect_circle_collision(rect, center, radius): # 将圆心坐标限制到矩形范围内得到最近点 closest_x max(rect.left, min(center[0], rect.right)) closest_y max(rect.top, min(center[1], rect.bottom)) dx center[0] - closest_x dy center[1] - closest_y return dx * dx dy * dy radius * radiusclosest_x和closest_y的求法包含一个巧妙的逻辑如果圆心本身就在矩形内部那么限制后的点就是圆心本身距离为0必然判定碰撞如果圆心在矩形外部最近点就是矩形边界上离圆心最近的那个角点或边缘点。一句话总结这个函数的关键它把“矩形与圆是否相交”转化成了“点与圆是否足够近”。3.4 碰撞后的响应处理碰撞检测出来之后游戏需要做出响应否则玩家会感觉物体互相穿来穿去没有重量感。最常见的响应有三种位置修正、反弹、消除。位置修正的思路是把撞进另一个物体内的物体推出去。具体做法是计算两个物体重叠了多少然后沿着最小穿透方向把它们分离。我做的挡板接球游戏里用了一种简化方式球碰到挡板后直接把球的Y方向速度改为反方向。这个方案虽然粗糙但对很多小游戏已经足够。反弹处理就复杂一些如果你想模拟真实物理效果需要计算碰撞法线方向然后把速度投影到法线方向做反射。Pygame的Rect类没有直接提供这种功能需要自己写向量运算。我在第6章会稍微展开谈一下向量反射的基本思路。4. 实战案例一个“接球”小游戏4.1 需求设计与角色定义理论说了那么多我们直接做一个能跑的“挡板接球”小游戏。游戏规则很简单玩家用左右方向键控制屏幕底部的挡板反弹不断下落的小球每次成功接住计一分球落到底部游戏结束。这个案例麻雀虽小五脏俱全里边包含矩形碰撞挡板和球都是矩形、屏幕边界反弹、游戏计分、循环结束条件。做完这个小游戏你会对Pygame的整个开发流程有个完整的认识。在设计阶段我先定好所有的常量窗口800像素宽、600像素高挡板是长100宽20的矩形位置在屏幕底部上方40像素处小球是边长20的正方形。初始速度给[5, 5]也就是每帧在X和Y方向上各移动5像素。4.2 完整代码实现下面是我手写并实测可运行的完整代码import pygame import sys pygame.init() SCREEN_WIDTH 800 SCREEN_HEIGHT 600 FPS 60 PADDLE_SPEED 7 BALL_SPEED [5, 5] screen pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT)) pygame.display.set_caption(挡板接球小游戏) clock pygame.time.Clock() paddle pygame.Rect(SCREEN_WIDTH // 2 - 50, SCREEN_HEIGHT - 40, 100, 20) ball pygame.Rect(SCREEN_WIDTH // 2 - 10, SCREEN_HEIGHT // 2 - 10, 20, 20) ball_vx, ball_vy BALL_SPEED score 0 running True while running: for event in pygame.event.get(): if event.type pygame.QUIT: pygame.quit() sys.exit() keys pygame.key.get_pressed() if keys[pygame.K_LEFT] and paddle.left 0: paddle.move_ip(-PADDLE_SPEED, 0) if keys[pygame.K_RIGHT] and paddle.right SCREEN_WIDTH: paddle.move_ip(PADDLE_SPEED, 0) ball.move_ip(ball_vx, ball_vy) if ball.left 0 or ball.right SCREEN_WIDTH: ball_vx -ball_vx if ball.top 0: ball_vy -ball_vy if ball.colliderect(paddle): ball_vy -abs(ball_vy) score 1 print(f得分: {score}) if ball.top SCREEN_HEIGHT: print(f游戏结束最终得分: {score}) running False screen.fill((30, 30, 30)) pygame.draw.rect(screen, (200, 200, 200), paddle) pygame.draw.rect(screen, (255, 255, 255), ball) pygame.display.flip() clock.tick(FPS)你可以直接把这段代码保存成catch_ball.py然后在终端运行。用法是在项目目录下执行python catch_ball.py。4.3 关键环节逐步拆解我在自己写这个游戏时每一步都曾遇到可讲的卡点这里把它们拆出来说清楚。球和挡板的初始化位置值得关注。挡板用Rect(SCREEN_WIDTH // 2 - 50, SCREEN_HEIGHT - 40, 100, 20)创建因为挡板宽度是100减去一半宽度才能居中显示。这个细节我开头提到过是新手最容易犯的坐标错误之一。球的移动用了move_ip方法ip后缀的意思是“in place”也就是直接修改自身坐标而不是返回一个新矩形。很多新手会写ball ball.move(dx, dy)或者直接ball.x ball_vx这两种方式也能用但要注意一致性。反弹逻辑里有个必要的小动作让球碰到挡板后ball_vy -abs(ball_vy)。为什么要加abs因为如果不加绝对值直接取反球从下方斜上方飞过来时可能出现“穿过挡板还继续向上”的情况。通过强制让反弹后的Y速度为正可以保证球永远向上弹。最后判断游戏结束用的是ball.top SCREEN_HEIGHT。为什么不是ball.bottom SCREEN_HEIGHT这里是一个刻意的小陷阱。如果球底部刚越过屏幕底部时结束游戏玩家会觉得球“已经消失了才结束”视觉上不舒服。用ball.top判断则球的整个矩形都离开屏幕时才触发结束观感更自然。4.4 从AABB到精细碰撞给挡板加一点“手感优化”上面的游戏直接用矩形碰撞用法没有问题但手感其实有点生硬球的整个正方形区域碰到挡板的矩形区域就算碰撞哪怕球只是擦过挡板边缘一毫米也算接住。这会让玩家觉得判定太宽松。我在实际改进时会把挡板的碰撞区域收缩一点不让球碰挡板外沿就触发。比如在检测时新建一个缩小后的矩形paddle_visual paddle.inflate(-20, -10) if ball.colliderect(paddle_visual): ball_vy -abs(ball_vy) score 1inflate(-20, -10)会把挡板的宽度和高度分别减少20像素和10像素。这样球必须真正击中挡板中间位置才算有效侧面边缘的“擦边球”不会被判定成功。手感会明显变“紧”对操作精度要求更高但也更接近真实物理反馈。你还可以做更精细的优化按球撞到挡板的水平位置决定反弹角度。比如撞到最左边就往左弹撞到最右边就往右弹这样玩家可以通过移动挡板控制球的去向。实现思路是计算球与挡板中心的相对位置然后映射到一个角度范围再把角度转换成速度向量。这个方案我最后会在进阶部分简单提一下因为它涉及向量分解讲起来会很长。5. 常见问题与排查技巧实录5.1 小球“穿透”挡板的罪魁祸首我从一开始提到的穿墙问题是游戏开发中出现频率最高的问题之一。现象是球以很高的速度移动一帧之前还在挡板上方下一帧已经跑到了挡板下方碰撞检测没能捕捉到中间那帧的重叠。原因是球的速度太快两帧之间的位置变化超过了挡板的厚度。比如挡板只有20像素高球每帧移动30像素那么第一帧球在挡板上方10像素第二帧球就到了挡板下方20像素这个过程中没有任何一帧是真正重叠的。解决思路有三种。第一种是限制球的最大速度让它每帧移动距离不超过最薄碰撞体的厚度简单粗暴但会限制游戏上限。第二种是用连续碰撞检测也就是做“扫掠检测”判断球从上一帧位置到当前位置的线段是否与挡板相交而不是只判断当前帧的点位置。第三种是减小物理步长把一帧拆分为多个小步进行多次碰撞检测。Pygame里有现成的clamp和move方法能辅助第一种思路但连续碰撞检测通常需要自己写线段与矩形的相交判断。我在工具类里会封装一个简单版本def check_line_rect_collision(start_pos, end_pos, rect): # 简化版把线段拆成多个采样点逐点判断是否在矩形内 steps 8 for i in range(1, steps 1): t i / steps x start_pos[0] (end_pos[0] - start_pos[0]) * t y start_pos[1] (end_pos[1] - start_pos[1]) * t if rect.collidepoint(x, y): return True return False这个采样点方案不是严格连续碰撞检测但实用性极高。你把 steps 调得越大检测就越接近完美性能消耗也越大。一般8到16个采样点足够对付绝大多数2D游戏场景。5.2 帧率波动导致判定飘忽碰撞检测中出现“有时穿过去有时撞上”的另一个常见原因是游戏帧率不稳定。在你电脑上跑60帧球一帧移动5像素在另一台电脑上帧率掉到30帧球一帧就移动10像素。同样一段代码在不同设备上的碰撞表现完全不同。解决这个问题的标准方案是固定时间步长。意思是不管真实帧率是多少游戏的物理逻辑都按固定间隔更新。Pygame里可以用clock.tick(60)将最大帧率限制在60但如果性能本身不达标帧率还是会掉。更严谨的写法是用累积时间来做定时更新accumulator 0 UPDATE_INTERVAL 1.0 / 60 while True: dt clock.tick(FPS) / 1000.0 accumulator dt while accumulator UPDATE_INTERVAL: update_game() accumulator - UPDATE_INTERVAL draw_game()这种模式把图像渲染和物理更新分离开物理更新稳定在每秒60次渲染则尽量快。如果你做的只是小游戏用简单方案也不是不行但一旦遇到碰撞手感问题先查帧率是不是罪魁祸首。5.3 高频排查速查表我把这些年遇到的碰撞相关问题和解决方案整理成一张表方便你对照排查问题现象根本原因解决方案球穿过碰撞体物体移动速度超过碰撞体厚度限制速度、连续碰撞检测、减小物理步长碰撞判定过于宽松使用了未经收缩的完整矩形用inflate收缩碰撞矩形碰边缘也算得分AABB矩形覆盖区域大于视觉图形针对碰撞区域单独定义或用多级检测帧率不同手感不同物理更新依赖真实帧率固定时间步长、累积器模式物体互相“黏住”响应处理时没有做位置分离响应阶段增加最小穿透距离修正矩形旋转后碰撞区域错误AABB没法跟随物体旋转改用圆形、凸包或像素级检测5.4 调试碰撞检测的必备可视化技巧排查碰撞检测问题最大的阻碍是“看不见”。你没法用肉眼直接看到程序里碰撞矩形的位置和大小所以只能靠猜测。我的做法是给每个物体绘制一个调试碰撞框pygame.draw.rect(screen, (255, 0, 0), paddle, width2) pygame.draw.rect(screen, (0, 255, 0), ball, width2)当你在屏幕上看到两个红色和绿色的线框时碰撞判定发生的位置就一目了然了。如果球明明碰到了挡板的视觉边缘却显示线框没有相交那说明你的碰撞矩形和视觉位置有偏差。这个可视化技巧看似普通却帮我解决了至少一半的碰撞排查问题。我还会在碰撞发生瞬间打印对象坐标和碰撞结果if ball.colliderect(paddle): print(f碰撞帧: ball({ball.x}, {ball.y}), paddle({paddle.x}, {paddle.y}))有了这些日志你就能知道碰撞发生的具体位置对比自己的预期很快定位是算法问题还是坐标初始化问题。6. 进阶方向与我的调试心得6.1 从“碰到没有”到“碰在哪里”我前面讲的所有检测只回答了“是否碰撞”这个问题。但有经验的游戏开发者会有更进一步的需求碰撞点在哪儿、法线方向是什么、需要把物体分离多少距离、反弹之后速度方向又是什么。比如挡板接球游戏里如果球撞到挡板的不同位置应该有不同的反弹角度就需要计算碰撞点与挡板中心的横向偏移。偏移越靠边缘反弹的横向速度越大。这里要用到向量的分解把入射速度分解为法线方向分量和切线方向分量反转法线方向分量后再合成作为反射速度。def reflect_velocity(velocity, normal): # normal 是碰撞点的法线方向单位向量 dot velocity[0] * normal[0] velocity[1] * normal[1] ref_x velocity[0] - 2 * dot * normal[0] ref_y velocity[1] - 2 * dot * normal[1] return ref_x, ref_y这个公式是向量反射的标准写法适用于任意角度的反弹。如果你想让球的反弹效果更真实建议把这个函数封装好。6.2 什么时候该用物理引擎聊到这里肯定会有人想问那我直接用Pymunk或者Box2D这类物理引擎不更省事吗我的观点是分情况。物理引擎能做到的事情远超手写碰撞检测刚体旋转、摩擦力、弹性系数、碰撞回调、关节约束这些手写起来工作量非常大。如果你做的游戏需要复杂的物理交互比如堆叠方块、荡秋千、投掷物体直接用Pymunk是正确的选择。但如果你只是想实现“玩家碰到敌人就扣血”“子弹碰到墙壁就消失”这种简单逻辑手写碰撞检测反而更可控。物理引擎有它自己的物理模拟方式和调试成本在简单场景里会带来不必要的复杂度。我给一个参考标准需要处理的物体少于100个、形状基本都是矩形或圆形、不需要旋转物理模拟那就手写。否则再考虑引擎。6.3 性能优化的小妙招当游戏里的可碰撞物体数量多起来之后逐对检测的效率会迅速下降。比如有100个物体两两检测就是4950次运算。这个数量级Python勉强能跑但如果到1000个物体就有近50万次运算游戏会明显卡顿。常用的优化方案是空间划分。把游戏地图划分为若干个格子每个格子维护自己的物体列表。检测时只需要查看同一格子以及相邻格子里的物体不需要和全地图的物体逐一比较。我写过一个简单的网格系统class Grid: def __init__(self, cell_size, map_width, map_height): self.cell_size cell_size self.cols map_width // cell_size self.rows map_height // cell_size self.cells [[[] for _ in range(self.rows)] for _ in range(self.cols)] def add_object(self, obj): cell_x obj.x // self.cell_size cell_y obj.y // self.cell_size self.cells[cell_x][cell_y].append(obj) def get_neighbors(self, obj): cell_x obj.x // self.cell_size cell_y obj.y // self.cell_size neighbors [] for dx in (-1, 0, 1): for dy in (-1, 0, 1): nx, ny cell_x dx, cell_y dy if 0 nx self.cols and 0 ny self.rows: neighbors.extend(self.cells[nx][ny]) return neighbors这个实现虽然简单但在物体数量多的场景下能显著减少无效检测。更复杂的四叉树结构适合动态分配物体数量差异大的地图原理和网格类似都是把检测范围局部化。6.4 我踩过最深的坑与一劳永逸的建议最后我想分享一个特别容易忽略的坑碰撞检测和视觉渲染使用了不同的坐标系统。有些游戏为了让角色看起来自然会在绘制角色时做一些偏移有的偏移量是临时的但对碰撞检测用的矩形并没有同步更新偏移。结果就是人眼看着角色撞到了东西程序判定却没撞到或者反了过来。我的习惯是永远为每个游戏对象维护一个“逻辑坐标”和一个“渲染坐标”。逻辑坐标用来做碰撞检测和游戏状态更新渲染坐标只是逻辑坐标加上偏移后的结果。这样碰撞逻辑永远保持纯粹不会受视觉表现干扰。把逻辑和视觉分离是我多年做游戏总结出来的最重要的建议之一这也是为什么我写碰撞检测代码时总是建议用一个独立函数而不是把判定逻辑散落在主循环里。等你把这些基础玩熟了建议再尝试给游戏加入更复杂的碰撞类型和物理反馈比如旋转矩形的碰撞使用分离轴定理、物体之间的弹性碰撞、摩擦力效果等。往前的路还有很多值得挖的东西但核心思想永远是碰撞检测就是对几何图形之间关系的精确判断而这些判断本身并不复杂难的是把它们组织得高效、好维护、还能顺手。希望这篇文章能帮你把这块地基打牢。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑