今日速览

  • 碰撞检测 = 检测(谁和谁相交)+ 响应(相交后发生什么),Godot 用四类碰撞对象覆盖这两种需求。
  • 四类碰撞对象选型:Area2D(只检测/影响,不参与物理阻挡)、StaticBody2D(静止环境,如墙/平台)、RigidBody2D(交给物理引擎,如掉落/弹跳)、CharacterBody2D(你写代码控制移动与响应,玩家/敌人首选)。
  • 碰撞层(layer) vs 碰撞遮罩(mask) 是新手最容易混的一对:collision_layer 表示「我待在哪些层」,collision_mask 表示「我会去扫描哪些层」。
  • 只有「A 的 mask 命中 B 的 layer(或反之)」时,两者的检测才成立——默认全都是层 1,所以小项目感觉「都能撞」,一旦分层就容易漏配。
  • 物理应统一放在 _physics_process(delta) 里跑,且永远按 delta 计算位移,避免不同帧率下手感/穿透不一致。

核心学习内容

1. 先选对对象,再谈碰撞

  • Area2D:用于「检测」而非「阻挡」。拾取物、伤害判定区、镜头边界、重力区域都用它。它能发出 body_entered/area_entered 等信号,默认还接收鼠标/触摸输入。
  • StaticBody2D:不被物理引擎移动的环境物体——墙、地面、平台,甚至是「传送带」(靠 constant_linear_velocity 假装在动)。
  • RigidBody2D:把行为交给引擎,堆叠、下落、弹跳「白嫖」。注意不要让物理引擎睡着时还指望 _integrate_forces 被调用;想安全改速度/施加力,用 _integrate_forces(state) 而不是 _physics_process。
  • CharacterBody2D:玩家和敌人最常用。它检测碰撞但不被重力/摩擦推动,移动必须用 move_and_collide() 或 move_and_slide(),不要直接改 position。

2. 层与遮罩:一句话记住

collision_layer = 我「住」在哪几层;collision_mask = 我「看」哪几层。30 秒自检法:碰撞不生效时,先问「A 的 mask 有没有勾到 B 所在的 layer」。每类对象有 32 个物理层可交互,别浪费,先在 项目设置 → 层名称 → 2D 物理 里给层起名(walls/player/enemies/coins…),后期维护成本直接砍半。

3. 位掩码的三种写法

代码里层是位掩码,常用三种等价写法(以启用层 1、3、4 为例):

# 二进制:层32是第1位,层1是最后一位
0b00000000_00000000_00000000_00001101   # 可简写 0b1101
# 十六进制
0x000d                                   # 可简写 0xd
# 十进制 / 位移写法(推荐,最不易错)
(1 << (1 - 1)) | (1 << (3 - 1)) | (1 << (4 - 1))   # = 1 + 4 + 8 = 13

运行时单独开关某层更直观:

var col: CollisionObject2D = $CollisionObject2D
col.set_collision_mask_value(1, true)
col.set_collision_mask_value(3, true)
col.set_collision_mask_value(4, true)

想让策划/自己能在检查器里可视化勾选层,用导出标注:

@export_flags_2d_physics var scan_layers: int

4. 一张可照抄的分配表(官方 GUI 例子)

四个对象 Walls / Player / Enemy / Coin:

对象Layer(我在哪)Mask(我扫描谁)
Wallswallsplayer, enemies
Playerplayerwalls, enemies, coins
Enemyenemieswalls, player
Coincoins(由 Area2D 检测,可不设 mask)

要点:Coin 与 Enemy 互不理会,所以谁的 mask 里都不勾对方。层遮罩是「成对满足」的——Player 想撞 Wall,不要求 Wall 的 mask 里也有 player,只要 Player 的 mask 勾了 walls 即可满足阻挡判定。

5. 两个高频踩坑

  • 别缩放碰撞形状:检查器里 CollisionShape2D 的 Scale 必须保持 (1,1),改大小请拖形状控制柄,缩放会引发诡异的碰撞穿透。
  • 别把物理写进 _process:物理固定 60Hz,渲染帧率浮动;物理相关代码一律进 _physics_process(delta)。

🎯 DeepSeek 提示词

提示词一:让 DeepSeek 帮你规划整套碰撞层方案

适用场景:新项目起步,或系统变多后层遮罩开始混乱。

你是资深 Godot 4(GDScript)游戏架构师。我要做一款 {游戏类型,如 2D 平台跳跃/俯视角射击}。
游戏中的实体有:{列举实体,如 玩家、敌人、子弹、地面、平台、金币、伤害区、可破坏箱子}。
请:
1. 设计一套 2D 物理层命名方案(层号 1~N + 英文层名 + 中文说明),给出建议在「项目设置→层名称→2D物理」中的填写顺序;
2. 输出一张表格:每个实体节点的 Layer 与 Mask 分别勾选哪些层,并解释为什么;
3. 指出哪些实体应该用 Area2D、哪些用 CharacterBody2D/StaticBody2D/RigidBody2D,并说明理由。
约束:用 markdown 表格输出;只提最简洁的方案,不要罗列所有可能性;对「不需要发生碰撞」的组合明确写「不勾选」。

提示词二:诊断「碰撞检测不生效」

适用场景:明明两个物体碰上了,信号不触发 / 角色直接穿墙。

你是 Godot 4 物理调试专家。我的问题:{描述现象,如 玩家穿过地面 / Coin 的 body_entered 不触发}。
相关节点结构:{粘贴场景树};相关代码:{粘贴脚本}。
请按以下顺序给出排查清单,每条都要说明「怎么验证」:
1. 双方 collision_layer / collision_mask 是否成对满足(谁该扫谁);
2. 碰撞形状是否被缩放(Scale 是否非 1,1);
3. CharacterBody2D 是否用了 move_and_slide()/move_and_collide() 而非直接改 position;
4. Area2D 的 monitoring/monitorable 开关是否开启、信号是否真的连上;
5. 物理代码是否放在了 _physics_process 且乘了 delta。
最后给出最可能的一条根因和修改后的最小代码片段。

提示词三:生成可复用的碰撞配置脚本

适用场景:想用代码统一管理层遮罩,或做「不同状态切换扫描目标」(如隐身时不受敌人检测)。

你是 Godot 4(GDScript)工程师。请写一个可复用的组件脚本 collision_config.gd:
1. 用 @export_flags_2d_physics 暴露一个可在检查器勾选的「扫描层」;用 @export_enum 暴露「所在层」选择;
2. 在 _ready() 用 set_collision_layer_value / set_collision_mask_value 应用配置;
3. 提供一个 public 方法 set_scan(layer_num: int, enabled: bool),运行时增删扫描层;
4. 顶部写中文注释说明每个导出变量的用途与勾选方法。
约束:Godot 4 语法;不依赖任何第三方插件;代码可直接 Ctrl+C 粘贴;附一句如何在玩家节点上挂载使用。

学习心得

碰撞层遮罩看着「只是一个勾选框」,但它其实是整个游戏世界的「交互规则表」。对「用 DeepSeek 从零做一款能上架 Steam 的游戏」来说,它是最省钱的提前投资:规则一次理清,后面加子弹、机关、陷阱、可破坏物时,只要往表里加行、改勾选,而不用满地找 bug。把上面的提示词一,在你动手写第一行玩法代码前先跑一遍,让 DeepSeek 产出这张表,你会少走至少一周的弯路。

明日关注

  • 用提示词一为当前原型生成一张完整层遮罩表,填进项目设置并截图存进设计文档。
  • 实测 Area2D 的 body_entered 与 area_entered 区别,写一个「金币拾取」最小可跑 demo。
  • 研究单向碰撞平台(one-way collision)在 Godot 4 里的实现方式,为平台跳跃玩法铺路。

本文由自动化学习流水线每日生成,坚持即学即记。