Godot 物理(4.x、2D + 3D)
选择正确的物理体,设置碰撞层/掩码,检测重叠,并投射 射线。概念同时适用于 2D 和 3D(交换 2D/3D 后缀)。目标 版本为 Godot 4.7。
何时使用
- 在以下情况使用:在体类型之间选择、设置碰撞层/掩码以便正确的
对象发生碰撞、使用 Area 检测重叠(触发器、伤害框)、对 RigidBody 施加力/冲量,或投射射线以进行视线/地面检查。
**何时*不*使用:** 运动学角色控制器(move_and_slide)→ godot-2d-movement;瓦片碰撞设置 → godot-tilemap;调整物理的 *手感*(时间步长、质量、抖动)→ physics-tuning。
核心工作流
- 选择体类型:
StaticBody— 从不移动(地板、墙壁)。会发生碰撞,但不模拟。RigidBody— 完全模拟(重力、力、弹跳)。不要直接设置其CharacterBody— 由脚本驱动的运动学体(参见godot-2d-movement)。Area— 检测重叠,并可应用重力/阻尼;没有实体碰撞。
position;施加力/冲量或设置 linear_velocity。
每个体都需要一个 CollisionShape(或 CollisionPolygon)子节点。
- 配置层与掩码。 一个体*位于*其 层 上,并*扫描*其 掩码。只有当一个体的层位于另一个体的掩码中时,两个体才会交互。在 Project Settings > Layer Names 中命名层,以便清晰。
- 使用
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
陷阱
- 层与掩码混淆是 #1 错误。层 = “我是什么”;掩码 = “我寻找什么”。
- 通过
position移动RigidBody会与求解器对抗,并导致穿隧/抖动。 Area不会触发,当 monitoring 和 monitorable 都未设置,或层/掩码- RayCast2D/3D 读取过期或无数据,如果
enabled为 false,或在物理 - 忘记
CollisionShape(或留空)意味着体永远不会碰撞。 - 快速物体会穿隧,穿过薄墙;在 RigidBody 上启用 continuous CD
intersect_ray会排除自身体吗? 传递query.exclude = [self.get_rid()]
要让 A 检测到 B,B 的层必须位于 A 的掩码中。检测可以是单向的。
使用冲量/力、设置 linear_velocity,或冻结它。要传送,请在 _integrate_forces 内设置位置并使速度归零。
不重叠时。monitoring 必须开启,Area 才能检测;monitorable 让其他 对象检测到它。
更新前读取它 — 请在 _physics_process 中读取,并在同一帧移动它之后 调用 force_raycast_update()。
(continuous_cd),或使用基于射线检测的检查。
(一个 Array[RID],而不是节点数组)以跳过自身命中。
参考
- 有关
_integrate_forces、关节、单向碰撞、直接访问PhysicsServer、
形状查询(intersect_shape)以及 3D move_and_collide,请阅读 references/bodies-and-queries.md。
相关技能
godot-2d-movement— 运动学CharacterBody2D控制器。godot-tilemap— 瓦片碰撞形状及其层。physics-tuning— 与引擎无关的手感:时间步长、质量、阻力、CCD。godot-3d-essentials— 这些体所在的 3D 场景设置。