关于 godot-physics
面向 Godot 4.x 开发者的 2D 与 3D 物理技能指南,涵盖物理体、碰撞、重叠检测和射线查询,并说明 Jolt Physics 的差异。
主要功能
- 01选择物理体类型
- 02配置碰撞层与遮罩
- 03使用 Area 检测重叠
- 04为 RigidBody 施加力与冲量
- 05执行射线查询
- 06了解 Jolt Physics 差异
适用场景
- 01设置墙体与敌人的碰撞关系
- 02制作触发区或受击框
- 03驱动物理模拟物体
- 04实现视线或地面检测
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ gamedev-skills/ awesome-gamedev-agent-skills --skill godot-physics安装后在 Claude Code 中输入/godot-physics确认已加载。
附带文件references/bodies-and-queries.md
详细介绍
Godot 物理(4.x,2D + 3D)
选择合适的物理体,设置碰撞层/掩码,检测重叠并进行射线检测。
这些概念适用于 2D 和 3D(替换 2D/3D 后缀即可)。目标版本为
Godot 4.7。在 3D 中,使用 Godot 4.6 或更高版本创建的项目默认运行 Jolt Physics;
较旧项目则继续使用 GodotPhysics3D,除非有人切换过。2D 始终使用 Godot 自带的 2D 引擎。
适用场景
- 选择物理体类型,设置碰撞层/掩码以控制哪些对象会发生碰撞,检测重叠(触发器、受击框),
对
RigidBody施加力/冲量,或进行射线检测以检查视线/地面时使用。
不适用场景: 运动学角色控制器(move_and_slide)→
godot-2d-movement;瓦片碰撞设置 → godot-tilemap;调整物理的手感(时间步长、质量、抖动)→ physics-tuning。
核心工作流
- 对于 3D,先检查运行的是哪个引擎,再调试行为:项目设置 >
物理 > 3D > 物理引擎(
project.godot中的[physics]3d/physics_engine="Jolt Physics")。 API 相同,但关节、射线检测面索引、运动学接触和碰撞边距的行为各不相同(参见注意事项)。 - 选择物理体类型:
StaticBody— 永不移动(地板、墙壁)。会发生碰撞,但不参与模拟。RigidBody— 完全模拟(重力、力、弹跳)。不要直接设置它的position;应施加力/冲量,或设置linear_velocity。CharacterBody— 由脚本驱动的运动学物体(参见godot-2d-movement)。Area— 检测重叠并可施加重力/阻尼;不会产生实体碰撞。 每个物理体都需要一个CollisionShape(或CollisionPolygon)子节点。
- 配置碰撞层和掩码。 物理体属于其碰撞层,并检测其 碰撞掩码中的对象。只有当一个物理体的碰撞层包含在另一个物理体的碰撞掩码中时,两者才会交互。为了清晰起见,请在项目设置 > 层名称中为碰撞层命名。
- 使用
Area信号(body_entered、area_entered)检测重叠。 - 使用力/冲量驱动
RigidBody,或重写_integrate_forces以获得完全控制。 - 使用
RayCast2D/3D节点进行射线检测(每帧轮询),或从代码发起一次性空间状态查询。
模式
1. 碰撞层与掩码(从代码设置)
# Player is on layer 1, scans layers 2 (walls) and 3 (enemies).
func _ready() -> void:
set_collision_layer_value(1, true) # I am on layer 1
set_collision_mask_value(2, true) # I collide with things on layer 2
set_collision_mask_value(3, true) # ...and layer 3
# Bit-field forms also exist: collision_layer = 1; collision_mask = 0b110
2. 将 Area2D 用作触发器/受击框
extends Area2D # e.g. a damage zone
func _ready() -> void:
body_entered.connect(_on_body_entered)
area_entered.connect(_on_area_entered)
func _on_body_entered(body: Node2D) -> void:
if body.has_method("take_damage"):
body.take_damage(10)
func _on_area_entered(area: Area2D) -> void:
print("Overlapped area: ", area.name)
3. 对 RigidBody3D 施加力和冲量
extends RigidBody3D
func push(direction: Vector3) -> void:
apply_central_impulse(direction * 8.0) # instantaneous velocity change
func _physics_process(_delta: float) -> void:
apply_central_force(Vector3.FORWARD * 4.0) # continuous force (per tick)
# Never set `position` on a RigidBody to move it; use forces/impulses or
# set linear_velocity. Use freeze=true if you must hold it in place.
4. 两种射线检测方式
# A) RayCast2D node: enable it, then poll after physics has updated.
@onready var ray: RayCast2D = $RayCast2D # set target_position in the editor
func _physics_process(_delta: float) -> void:
if ray.is_colliding():
var hit := ray.get_collider()
var point := ray.get_collision_point()
# B) One-shot query from code (no node needed).
func ground_under(global_from: Vector2) -> Dictionary:
var space := get_world_2d().direct_space_state
var query := PhysicsRayQueryParameters2D.create(global_from, global_from + Vector2(0, 64))
query.collision_mask = 1 # only layer 1
return space.intersect_ray(query) # {} if nothing hit, else collider/position/normal
注意事项
- 混淆碰撞层和掩码是最常见的错误。碰撞层 =“我是什么”;碰撞掩码 =“我检测什么”。 若要让 A 检测到 B,B 的碰撞层必须包含在 A 的碰撞掩码中。检测可以是单向的。
- 通过
position移动物理体RigidBody会与求解器冲突,并导致穿透/抖动。 应使用冲量/力、设置linear_velocity,或冻结它(freeze)。若要瞬移,请设置位置, 并在_integrate_forces中将速度归零。 - 如果既未设置监测(monitoring),也未设置可监测(monitorable),或碰撞层/掩码不重叠,
Area就不会触发信号。要让 Area 检测其他对象,必须启用monitoring;monitorable则允许其他对象检测它。 - 如果
enabled为 false,或在物理更新前读取数据,RayCast2D/3D 可能会读取到过时数据或没有数据——请在_physics_process中读取,并在同一帧内移动射线后调用force_raycast_update()。 - 忘记添加
CollisionShape(或将其留空)意味着物理体永远不会发生碰撞。 - 快速移动的物体会穿透薄墙;请在
RigidBody上启用连续碰撞检测(CD)(continuous_cd),或使用基于射线检测的检查。 intersect_ray会排除自身物理体吗? 设置query.exclude = [self.get_rid()](类型为Array[RID],而不是节点数组)以跳过自身命中。
Jolt Physics(3D)的差异
- 在 Jolt 下,
intersect_ray()/RayCast3D的结果中,face_index始终为-1。 如果需要该索引,请启用 Project Settings > Physics > Jolt Physics 3D > Queries > Enable Ray Cast Face Index;这会使ConcavePolygonShape3D的内存占用大约增加 25%。 - 切换物理引擎后,只有一个物理体的关节会出现限制反转。 Jolt 将单个物理体视为
node_b,并将node_a视为世界;GodotPhysics3D 则始终将其视为node_a。将物理体分配到 Jolt 预期的槽位,或者使用 Physics > Jolt Physics 3D > Joints > World Node,以兼容旧项目。 - 关节软限制属性在 Jolt 下不起作用(例如 Pin、Hinge 和 ConeTwist 关节上的
bias/softness/relaxation;Slider 和 Generic6DOF 关节上的限制柔度/恢复系数/阻尼)。设置为非默认值时,Godot 会发出警告。 - 冻结的运动学
RigidBody3D不会报告与静态或运动学物理体的接触,即使max_contacts_reported > 0也是如此。如果游戏逻辑依赖这些接触,请启用 Physics > Jolt Physics 3D > Simulation > Generate All Kinematic Contacts。 - Jolt 下的形状
margin会缩小形状,而不是为其增加填充,因此尺寸保持准确,但形状较小时,形状查询可能返回异常法线;有效边距由 Physics > Jolt Physics 3D > Collisions > Collision Margin Fraction 决定。 - Jolt 下的
Area3D现在会为SoftBody3D触发body_entered(GodotPhysics3D 从未如此)。如果需要旧行为,请使用图层/掩码过滤软体。
参考资料
- 关于
_integrate_forces、关节、单向碰撞、PhysicsServer直接访问、形状查询(intersect_shape)以及 3Dmove_and_collide,请阅读references/bodies-and-queries.md。 - Jolt 相关内容:Godot 文档“使用 Jolt Physics”
(
https://docs.godotengine.org/en/stable/tutorials/physics/using_jolt_physics.html)。
相关技能
godot-2d-movement— 运动学CharacterBody2D控制器。godot-tilemap— 瓦片碰撞形状及其图层。physics-tuning— 与引擎无关的手感调校:时间步长、质量、阻力、CCD。godot-3d-essentials— 这些物理体所在的 3D 场景设置。
评论
支持 Markdown