~4 min read

Physics — Bodies, Collision, Jolt

StaticBody3D, RigidBody3D, CharacterBody3D, Area3D — and why physics got faster in 4.6.

Иллюстрация: Physics — Bodies, Collision, Jolt

As of Godot 4.6, Jolt became the default physics engine for 3D — a high-performance engine used in Death Stranding 2 and other AAA games. The old Godot Physics remains available as an option and continues to be the default for 2D.

This means: predictable, scalable 3D physics out of the box. No need to integrate any third-party engines.

Four body classes

Nodes inheriting from CollisionObject3D that form a physics object:

NodeWhen
StaticBody3DDoesn’t move. Walls, floors, static architecture.
AnimatableBody3DMoves via animation or script. Pushes RigidBody3D. Equivalent to Unity’s Kinematic Rigidbody.
RigidBody3DFull dynamics — gravity, forces, impulses, collisions with response.
CharacterBody3DKinematics for a character: you set velocity, the engine moves it accounting for collisions. The only node with move_and_slide().

Also:

  • Area3D — an invisible region with body_entered/area_entered triggers. Equivalent to a Unity Collider with isTrigger=true.
  • PhysicalBone3D — for ragdolls inside Skeleton3D.
  • SoftBody3D — soft bodies (cloth, flags, simple jelly-like objects). Uses mesh-based soft-body simulation; works with both Godot Physics and Jolt (with some limitations).

CollisionShape3D — the shape for physics

Unlike Unity, where a collider is a component on the same GameObject, in Godot CollisionShape3D is a separate child node. It has a shape: Shape3D property — a resource that describes the shape:

RigidBody3D (Player)
├── MeshInstance3D (visual mesh)
└── CollisionShape3D
    └── shape: CapsuleShape3D (height=2, radius=0.4)

Types of Shape3D:

  • BoxShape3D, SphereShape3D, CapsuleShape3D, CylinderShape3D — primitives. Cheap.
  • ConvexPolygonShape3D — a convex mesh. For dynamic objects with complex shapes.
  • ConcavePolygonShape3D — an arbitrary mesh (triangulated). Static only.
  • HeightMapShape3D — terrain from a 2D heightmap.
A single body can have multiple CollisionShape3D nodes

Just add several child CollisionShape3D nodes — they all become shapes of that body. Convenient for compound colliders (for example, a car: chassis + bumper).

CharacterBody3D — the go-to node for platformers/FPS

CharacterBody3D holds velocity: Vector3 and the move_and_slide() method, which tries to move the body by velocity * delta while accounting for collisions: it stops, slides along walls, descends slopes, and steps up onto stairs.

extends CharacterBody3D

@export var speed: float = 5.0
@export var jump_velocity: float = 6.0

func _physics_process(delta: float) -> void:
    # Gravity — from project settings (Project Settings → Physics → 3D → Default Gravity)
    # get_gravity() returns Vector3(0, -9.8, 0) by default
    if not is_on_floor():
        velocity += get_gravity() * delta

    # Jump
    if Input.is_action_just_pressed("jump") and is_on_floor():
        velocity.y = jump_velocity

    # Horizontal movement
    var input_dir := Input.get_vector("move_left", "move_right", "move_forward", "move_back")
    var direction := (transform.basis * Vector3(input_dir.x, 0, input_dir.y)).normalized()
    if direction:
        velocity.x = direction.x * speed
        velocity.z = direction.z * speed
    else:
        velocity.x = move_toward(velocity.x, 0, speed)
        velocity.z = move_toward(velocity.z, 0, speed)

    move_and_slide()

Useful CharacterBody3D methods and properties:

  • is_on_floor(), is_on_wall(), is_on_ceiling() — simple contact checks.
  • get_floor_normal() — the normal of the floor you’re standing on.
  • floor_max_angle — the maximum slope angle still considered a floor (45° by default).
  • floor_snap_length — how deeply to “snap” to the floor when going down a slope (avoids bouncing on descents).
  • get_slide_collision_count() + get_slide_collision(i) — details of collisions during this tick.
Web

Custom 3D without an engine: you compute next_pos = pos + velocity * dt and handle collisions by hand. Hard and buggy.

Unity

CharacterController.Move(motion)move_and_slide(). CharacterBody3D is more powerful: it automatically slides along walls, steps up onto stairs, and snaps to slopes — all configured via flags.

RigidBody3D — dynamics

For physics objects that react to forces (a ball, a crate, a dropped item):

extends RigidBody3D

func _ready() -> void:
    mass = 2.0
    linear_damp = 0.5

func explode_push(direction: Vector3, force: float) -> void:
    apply_central_impulse(direction * force)

Methods:

  • apply_force(force: Vector3, position: Vector3 = Vector3.ZERO) — a continuous force (in _physics_process).
  • apply_central_force(force) — a force at the center of mass.
  • apply_impulse(impulse, position) — an instantaneous impulse.
  • apply_central_impulse(impulse) — an instantaneous impulse at the center of mass.
  • apply_torque_impulse(impulse) — torque.

Properties:

  • mass: float — mass.
  • gravity_scale: float — gravity multiplier (0 = none, 1 = normal).
  • linear_damp / angular_damp — damping.
  • freeze / freeze_mode — freeze.
  • lock_rotation: bool — disable rotation.
  • continuous_cd: CCDModeDISABLED / CAST_RAY / CAST_SHAPE — for fast-moving bodies, to prevent tunneling.

Area3D — triggers

Area3D is not a physics body, but it emits signals when it overlaps with other CollisionObject3D nodes:

extends Area3D

func _ready() -> void:
    body_entered.connect(_on_body_entered)
    area_entered.connect(_on_area_entered)

func _on_body_entered(body: Node3D) -> void:
    if body.is_in_group("player"):
        print("Player picked up coin")
        queue_free()

func _on_area_entered(area: Area3D) -> void:
    print("Another area overlapped: ", area.name)

Area3D signals:

  • body_entered(body) / body_exited(body) — a physics body entered/exited.
  • area_entered(area) / area_exited(area) — another Area3D.
  • body_shape_entered, area_shape_entered — detailed versions with shape indices.

Collision Layers and Masks

Each CollisionObject3D has two bitfields:

  • Collision Layer — which layers this object is on.
  • Collision Mask — which layers this object scans (overlaps with / reacts to).

For A to notice B: A’s mask must contain B’s layer(s). For the interaction to be two-way, B’s mask must also contain A’s layer.

Layer names are configured in Project Settings → Layer Names → 3D Physics.

Layer ≠ Mask, unlike Unity's Layer Collision Matrix

In Unity the layer matrix is bidirectional. In Godot there are two independent bitfields per object. This gives more flexibility (for example, “the bullet sees the enemy but not vice versa”), but requires care.

Jolt vs Godot Physics

As of 4.6, Jolt is the default for new 3D projects. Existing projects don’t switch automatically — set it in Project Settings → Physics → 3D → Physics Engine.

ParameterGodot PhysicsJolt
Performance with 1000+ bodiesDegradesStable
Stacking (stacks of crates)Can sagBetter
CCD (continuous)YesYes, better
Double-sided thin collidersOccasional bugsStable
JointsBasicFull set

If you already have a working project on Godot Physics — stay on it. For a new one — Jolt.

In the next chapter — the camera and Phantom Camera.