godot-physics

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Godot Physics (4.x, 2D + 3D)

Godot 物理系统(4.x版本,2D + 3D)

Pick the right physics body, wire up collision layers/masks, detect overlaps, and cast rays. Concepts apply to both 2D and 3D (swap the
2D
/
3D
suffix). Targets Godot 4.3+.
选择合适的物理体,配置碰撞层与掩码,检测重叠并进行射线投射。相关概念同时适用于2D和3D场景(只需替换
2D
/
3D
后缀即可)。适配**Godot 4.3+**版本。

When to use

适用场景

  • Use when choosing between body types, setting collision layers/masks so the right things collide, detecting overlaps (triggers, hurtboxes) with
    Area
    , applying forces/impulses to a
    RigidBody
    , or casting rays for line-of-sight/ground checks.
When not to use: kinematic character controllers (
move_and_slide
) →
godot-2d-movement
; tile collision setup →
godot-tilemap
; tuning the feel of physics (timestep, mass, jitter) →
physics-tuning
.
  • 适用于选择物理体类型、设置碰撞层/掩码以实现正确的碰撞交互、通过
    Area
    检测重叠(触发器、伤害判定框)、向
    RigidBody
    施加力/冲量,或通过射线投射实现视线检测/地面检测的场景。
不适用场景: 运动学角色控制器(
move_and_slide
)→ 参考
godot-2d-movement
;瓦片碰撞设置 → 参考
godot-tilemap
;物理手感调优(时间步长、质量、抖动)→ 参考
physics-tuning

Core workflow

核心工作流程

  1. Choose the body type:
    • StaticBody
      — never moves (floors, walls). Collides, no simulation.
    • RigidBody
      — fully simulated (gravity, forces, bouncing). Don't set its
      position
      directly; apply forces/impulses or set
      linear_velocity
      .
    • CharacterBody
      — script-driven kinematic (see
      godot-2d-movement
      ).
    • Area
      — detects overlaps and can apply gravity/damping; no solid collision. Every body needs a
      CollisionShape
      (or
      CollisionPolygon
      ) child.
  2. Configure layers and masks. A body is on its layers and scans for its masks. Two bodies interact only if one's layer is in the other's mask. Name layers in Project Settings > Layer Names for clarity.
  3. Detect overlaps with
    Area
    signals (
    body_entered
    ,
    area_entered
    ).
  4. Drive RigidBodies with forces/impulses, or override
    _integrate_forces
    for full control.
  5. Cast rays with a
    RayCast2D/3D
    node (polled each frame) or a one-shot space-state query from code.
  1. 选择物理体类型:
    • StaticBody
      — 静止不动(地板、墙体)。可发生碰撞,但不参与物理模拟。
    • RigidBody
      — 完全受物理模拟(重力、外力、弹跳)。请勿直接设置其
      position
      ;应施加力/冲量或设置
      linear_velocity
    • CharacterBody
      — 由脚本驱动的运动学体(参考
      godot-2d-movement
      )。
    • Area
      — 检测重叠区域,可施加重力/阻尼;无实体碰撞。 每个物理体都需要一个
      CollisionShape
      (或
      CollisionPolygon
      )子节点。
  2. 配置碰撞层与掩码。物理体属于自身的碰撞层,并会扫描自身碰撞掩码中的层。只有当一个物体的碰撞层在另一个物体的碰撞掩码中时,两者才会产生交互。可在项目设置 > 层名称中为碰撞层命名,提升可读性。
  3. 通过
    Area
    信号检测重叠
    body_entered
    area_entered
    )。
  4. 通过力/冲量驱动RigidBody,或重写
    _integrate_forces
    实现完全控制。
  5. 射线投射:使用
    RayCast2D/3D
    节点(每帧轮询),或通过代码执行一次性空间状态查询。

Patterns

实践模式

1. Collision layers vs masks (set from code)

1. 碰撞层与掩码(通过代码设置)

gdscript
undefined
gdscript
undefined

Player is on layer 1, scans layers 2 (walls) and 3 (enemies).

玩家位于第1层,检测第2层(墙体)和第3层(敌人)的对象。

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
undefined
func _ready() -> void: set_collision_layer_value(1, true) # 我属于第1层 set_collision_mask_value(2, true) # 我与第2层的物体碰撞 set_collision_mask_value(3, true) # ...同时与第3层的物体碰撞 # 也支持位域形式:collision_layer = 1; collision_mask = 0b110
undefined

2. Area2D as a trigger / hurtbox

2. 将Area2D用作触发器 / 伤害判定框

gdscript
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)
gdscript
extends Area2D                            # 例如:一个伤害区域

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("重叠区域:", area.name)

3. Applying force and impulse to a RigidBody3D

3. 向RigidBody3D施加力与冲量

gdscript
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.
gdscript
extends RigidBody3D

func push(direction: Vector3) -> void:
    apply_central_impulse(direction * 8.0)     # 瞬时速度变化

func _physics_process(_delta: float) -> void:
    apply_central_force(Vector3.FORWARD * 4.0) # 持续力(每物理帧)
    # 切勿通过设置`position`移动RigidBody;应使用力/冲量或设置linear_velocity。若需固定位置,可设置freeze=true。

4. Raycast two ways

4. 两种射线投射方式

gdscript
undefined
gdscript
undefined

A) RayCast2D node: enable it, then poll after physics has updated.

A) RayCast2D节点:启用后,在物理更新完成后进行轮询。

@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()
@onready var ray: RayCast2D = $RayCast2D # 在编辑器中设置target_position
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).

B) 通过代码执行一次性查询(无需节点)。

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
undefined
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 # 仅检测第1层 return space.intersect_ray(query) # 未命中返回{},命中则返回collider/position/normal等数据
undefined

Pitfalls

常见陷阱

  • Layer vs mask confusion is the #1 bug. Layer = "what I am"; mask = "what I look for". For A to detect B, B's layer must be in A's mask. Detection can be one-directional.
  • Moving a RigidBody by
    position
    fights the solver and causes tunneling/jitter. Use impulses/forces, set
    linear_velocity
    , or
    freeze
    it. To teleport, set position and zero the velocities inside
    _integrate_forces
    .
  • Area
    doesn't fire
    when neither monitoring nor monitorable is set, or layers/masks don't overlap.
    monitoring
    must be on for the Area to detect;
    monitorable
    lets others detect it.
  • RayCast2D/3D read stale or no data if
    enabled
    is false, or if you read it before physics updated — read in
    _physics_process
    , and call
    force_raycast_update()
    after moving it within the same tick.
  • Forgetting a
    CollisionShape
    (or leaving it empty) means the body never collides.
  • Fast objects tunnel through thin walls; enable continuous CD on the RigidBody (
    continuous_cd
    ) or use a raycast-based check.
  • intersect_ray
    excludes its own body?
    Pass
    query.exclude = [self.get_rid()]
    (an
    Array[RID]
    , not an array of nodes) to skip self-hits.
  • 混淆碰撞层与掩码是最常见的错误。碰撞层 = "我是什么";掩码 = "我要检测什么"。若A要检测B,B的碰撞层必须在A的掩码中。检测可以是单向的。
  • 通过
    position
    移动RigidBody
    会干扰物理求解器,导致穿模/抖动。应使用冲量/力、设置
    linear_velocity
    ,或
    freeze
    该物体。若需瞬移,应在
    _integrate_forces
    内设置位置并重置速度。
  • Area未触发信号:当monitoring和monitorable均未开启,或碰撞层/掩码不重叠时,Area不会触发信号。
    monitoring
    必须开启才能让Area检测其他对象;
    monitorable
    开启才能让其他对象检测该Area。
  • RayCast2D/3D读取到过期或无数据:若
    enabled
    为false,或在物理更新前读取数据,会出现此问题——应在
    _physics_process
    中读取,且在同一帧移动射线后调用
    force_raycast_update()
  • 遗漏
    CollisionShape
    (或留空)会导致物理体无法发生碰撞。
  • 快速移动的对象穿模:开启RigidBody的连续碰撞检测
    continuous_cd
    ),或使用基于射线投射的检测。
  • intersect_ray
    排除自身?
    传入
    query.exclude = [self.get_rid()]
    Array[RID]
    类型,而非节点数组)即可跳过自身碰撞。

References

参考资料

  • For
    _integrate_forces
    , joints, one-way collision,
    PhysicsServer
    direct access, shape queries (
    intersect_shape
    ), and 3D
    move_and_collide
    , read
    references/bodies-and-queries.md
    .
  • 关于
    _integrate_forces
    、关节、单向碰撞、直接访问
    PhysicsServer
    、形状查询(
    intersect_shape
    )以及3D的
    move_and_collide
    ,请阅读
    references/bodies-and-queries.md

Related skills

相关技能

  • godot-2d-movement
    — kinematic
    CharacterBody2D
    controllers.
  • godot-tilemap
    — tile collision shapes and their layers.
  • physics-tuning
    — engine-agnostic feel: timestep, mass, drag, CCD.
  • godot-3d-essentials
    — 3D scene setup these bodies live in.
  • godot-2d-movement
    — 运动学
    CharacterBody2D
    控制器。
  • godot-tilemap
    — 瓦片碰撞形状及其层设置。
  • physics-tuning
    — 引擎无关的物理手感调优:时间步长、质量、阻力、连续碰撞检测。
  • godot-3d-essentials
    — 这些物理体所在的3D场景设置。