Godot 粒子碰撞层指南:用分层与掩码把子弹互碰关掉,只留你需要的碰撞
【免费下载链接】godot-demo-projectsDemonstration and Template Projects项目地址: https://gitcode.com/GitHub_Trending/go/godot-demo-projects
做粒子效果时最常踩的物理坑不是速度、不是数量,而是粒子之间会不会互相碰。子弹互相弹开、火花彼此穿身,画面一下就乱了。Godot 的粒子碰撞层配置专门管这件事:用碰撞层(Collision Layer)和碰撞掩码(Collision Mask)两样东西,就能精确决定哪几类物理对象会碰、哪几类绝不碰。官方示例仓库 godot-demo-projects 里的 2d/bullet_shower 就是个现成范本:500 发子弹照样能碰到玩家、被边界挡住,子弹之间却从不互相碰撞。下面按这个 bullet_shower 子弹雨示例走一遍——先弄清碰撞层和掩码各管什么,再给每类粒子分层,然后用一行代码关掉同类粒子的互碰,最后看不同配置下画面到底差在哪。
为什么 500 发子弹默认会互相打架
先说这个 demo 的实际场景:屏幕右侧源源不断涌进左侧的圆形刚体子弹,每帧 500 发;玩家是个跟着鼠标走的圆,被碰到就换成哭脸。
问题出在"默认"两个字上。Godot 的 2D 物理引擎每一帧都要判定"谁碰了谁",而且是按对象两两组合来判的。500 颗子弹如果彼此都开着碰撞,等于每帧都有上百万对组合要过一遍检测,帧率肉眼可见地往下掉。比掉帧更直观的代价是画面:子弹会互相顶住、弹开、堆叠,原本笔直下落的雨瞬间变成乱窜的弹片。
所以动手配置之前,先回答一个具体问题:我到底需要哪几类碰撞?这个 demo 的答案很干脆——子弹要能碰到玩家,子弹出了左边界要能绕回右边,子弹和子弹之间谁也不用管。想清楚这三条,后面的层和掩码基本就是照单填数。
👀 碰撞层和碰撞掩码,一个说"我是谁",一个说"我查谁"
在 Godot 里,每个 2D 物理对象都挂着两条 32 位的开关,就是 Godot 碰撞层和碰撞掩码:
- 碰撞层(Layer):第 n 位打开,表示"我属于第 n 类"。
- 碰撞掩码(Mask):第 n 位打开,表示"我要主动检测第 n 类的对象"。
引擎判定时做的是交叉查询:A 的层命中 B 的掩码,或者 B 的层命中 A 的掩码,任意一条成立,就算这对对象发生了碰撞。注意这个不对称性,它正是 Godot 碰撞掩码配置好用的地方。
拿玩家和子弹举例:玩家设成层 1、掩码 1,它只管第 1 类;子弹设成层 2、掩码 1,它挂在第 2 类,却只去查第 1 类。子弹飞进玩家范围,触发玩家身上的body_shape_entered信号,玩家换哭脸;而两颗子弹同属第 2 类,谁的掩码里都没有 2 这一位,它们之间永远不会产生判定。
🗂️ 给每类粒子分一个碰撞层:先数对象,再填位号
动手之前,把场景里的物理对象数一遍,每类固定一个位号,之后不再挪动:
- 位号 1:玩家
- 位号 2:子弹
- 位号 3:边界墙(或地形)
- 位号 4、5:留给日后要加的道具、敌人
规划好再去填数。检查器里把玩家的 Collision Layer 勾上 1、Collision Mask 勾上 1;子弹的 Collision Layer 勾上 2、Collision Mask 只勾 1。用界面上的勾选框一个个点,比在脑子里记二进制数省心,也方便随时回头核对:某个位号到底是谁、谁在查它,一眼能看清。
🎬 一行代码,把 Godot 2D 粒子的同类互碰关掉
bullets.gd 没走检查器那条路——它不用场景节点,而是在脚本里通过PhysicsServer2D批量建出 500 个刚体,每建出一颗就顺手配好掩码:
# 每颗子弹建好刚体后,掩码置 0:不再检测任何层,子弹互不碰撞 PhysicsServer2D.body_set_collision_mask(bullet.body, 0)掩码为 0 的意思是:这颗子弹主动去查任何一类对象都不在乎,它和同属位号 2 的其他子弹从此互不相关。而子弹打到玩家依然成立,因为那次判定由玩家那边的掩码发起——碰撞检测是双向查询,关掉一头,另一头照样工作。另外,掩码 0 并不会把刚体从物理世界里摘走:它照旧被脚本每帧移动、照旧能被别的对象检测到,只是自己不再发起任何查询。
关掉和没关掉,差别直接写在截图里。互碰打开时,子弹互相顶住、轨迹乱偏,密集区挤成一坨:
掩码置 0 之后,雨丝均匀地横穿屏幕,只有玩家被碰到时才会换表情:
省下的是每帧成片的碰撞判定,换来的是帧率和画面整洁度——粒子类视觉效果最忌讳的就是彼此打架。
🧪 换个场景:多类粒子各自独立跑的思路
同一套分层思路在 2d/particles 里同样成立。那个示例里火焰、烟雾、星点各是一个GPUParticles2D节点配自己的ParticleProcessMaterial,视觉和运动参数互不串扰,所以三类效果能同时跑在同一屏里不打架:
如果哪天你想给它们接上真实物理——比如烟雾只落回地面、火花只打中敌人——办法还是上面那套:给地面、火花、敌人各占一个位号,掩码里只勾自己要碰的那几格。层管身份,掩码管意向,配完跑一遍场景,用画面而不是脑补来验证。
- 把 2d/bullet_shower 里的子弹数从 500 调到 2000,对比一下掩码为 0 和为 1 时的帧率差,直观感受"少查一对"省下的量。
- 给自己正在做的项目画一张两列表:先写"谁需要碰谁",再回头填位号和掩码,比边写边勾不容易漏勾或撞位号。
- 翻一翻 2d/particles 里的
.tres材质文件,看看每类粒子是怎么在ParticleProcessMaterial里各自独立配参数的,思路和碰撞分层如出一辙。
【免费下载链接】godot-demo-projectsDemonstration and Template Projects项目地址: https://gitcode.com/GitHub_Trending/go/godot-demo-projects
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考