Skills gamedev-skills awesome-gamedev-agent-skills Gamedev Agent Skills

physics-tuning

Use it when:
Motion jitters, fast objects pass through walls, stacks explode, or movement feels floaty, sticky or laggy.
It does:
Moves simulation into the fixed physics step, interpolates rendering between ticks, turns on continuous collision for fast bodies, tunes mass, drag and materials, and checks layers and masks.
Tunneling means:
A fast, small object skipping past a thin collider between two physics steps.
You get:
Physics settings and code changes, checked at low and high frame rates by throwing fast objects at thin walls and shoving stacks.
Code examples:
GDScript for Godot, with the Unity equivalents in comments.

Skill 10 of 27 in the Gamedev Agent Skills bundle, after shader-programming and before level-design.

TL;DR

A discipline skill from a game-development collection. Its premise: most bad physics isn't an engine bug but a mismatch between the fixed-rate simulation and the variable-rate render loop, or untuned settings. Forces, velocities and collision reads go in the physics callback with its fixed step, and rendering interpolates between the last two physics states, preferably with the engine's built-in interpolation. Tunneling gets continuous collision detection and a speed cap, with the rule that one step's travel should stay under the thinnest wall. Mass sets weight in collisions, not fall speed, so feel comes from drag, gravity scale and physics materials. Stacks and joints settle with modest mass ratios, more solver iterations and sleeping. Pitfalls include one-sided layer and mask setups, huge time-step spikes after a hitch, teleports smeared by interpolation, and Godot damping that drifts after raising the physics rate. Jump buffering and coyote time are left to input-systems and the platformer skill.

Summarizes the skill at 19d93f9.

npx skills add https://github.com/gamedev-skills/awesome-gamedev-agent-skills --skill physics-tuning
Total Downloads
10,600 on skills.sh, Nov 9, 2026
Repo stars
7,863 gamedev-skills/awesome-gamedev-agent-skills
First seen
Jun 27, 2026 on skills.sh
Weekly Downloads
1.8K
+394% over the 8 weeks to Nov 9
Weekly values
Week toInstalls
Sep 21357
Sep 28422
Oct 5428
Oct 12507
Oct 19712
Oct 261,582
Nov 21,940
Nov 91,763
Category

Find root causes, review code and test what you built.

Make games: design, genres, game feel and game-jam workflows, whatever the engine.

Everything for building software: planning, coding, debugging, design, frameworks, cloud, agents and skills.

Type

A multi-step process the agent follows: interview, debug, run a TDD loop, review.

Effect

Its own steps create or change files, git state or local tools.

Needs

Instructions only. Works in any agent that supports skills.

Publisher

Everyone else. Unverified vendor claims default to community.

Topics

SKILL.md

From gamedev-skills/awesome-gamedev-agent-skills at 19d93f9. Checked daily, last changed Nov 6, 2026.

Frontmatter
name: physics-tuning
description: >
  Tune game physics for stable, good-feeling motion — fixed vs variable
  timestep, render interpolation, mass/gravity/drag, continuous collision
  detection (CCD) to stop tunneling, fixing jitter, and collision layers/masks.
  Engine-neutral. Use when the user mentions physics feel, jitter, tunneling,
  fixed timestep, FixedUpdate, CCD, bouncing/unstable physics, or collision layers.

Physics tuning

Most "bad physics" is not a bug in the engine — it's a mismatch between the fixed-timestep simulation and the variable-rate render loop, or untuned mass/drag/CCD/layer settings. This skill covers the engine-neutral knobs that make physics stable and responsive; pair it with godot-physics or unity-physics for the concrete APIs.

When to use

  • Use when motion jitters, objects pass through walls (tunneling), stacks explode, or movement feels floaty/sticky/laggy.
  • Use to decide what goes in the fixed (physics) step vs the render frame, and how to interpolate between them.
  • Use to tune gravity, mass, drag, restitution, solver iterations, sleeping, and collision layers/masks.

When not to use: for an engine's exact physics nodes/components and collision callbacks, use godot-physics or unity-physics. For movement decisions (when to jump, AI steering) use input-systems and game-ai. For platformer jump-feel specifics like coyote time/jump buffering, that's input/ controller territory — see input-systems and the platformer genre.

Core workflow

  1. Run physics on a fixed timestep. Simulate at a constant rate (e.g. 50–60 Hz). A fixed dt makes the simulation deterministic-ish and stable; a variable dt makes integration and collisions inconsistent.
  2. Put physics work in the physics callback, not the render frame. Apply forces/velocities and read collisions in the fixed step (FixedUpdate / _physics_process), using that step's dt.
  3. Interpolate rendering between physics ticks. The render frame rate ≠ the physics rate, so smoothly interpolate transforms toward the latest physics state, or enable the engine's Rigidbody interpolation, to remove visible stutter.
  4. Tune the body, not the scene. Set mass for relative weight, drag for damping, gravity scale per object, and restitution/friction via materials.
  5. Stop tunneling with CCD on small/fast bodies; cap maximum velocity.
  6. Stabilize stacks/joints with more solver iterations, sane mass ratios, and sleeping for resting bodies.
  7. Verify by feel and stress test. Play at low and high frame rates; throw fast objects at thin walls; stack and shove bodies. Report what you observed.

Patterns

1. Fixed timestep for simulation, render interpolation for smoothness
# Physics callback: runs at the FIXED rate. Use its dt for all integration.
func _physics_process(dt):                  # Unity: void FixedUpdate()
    velocity += gravity * dt                # integrate with the FIXED dt
    move_and_slide()                        # engine resolves collisions this step
    _prev_pos = _curr_pos; _curr_pos = global_position   # record for interpolation

# Render frame: runs as fast as the display. Interpolate between physics states.
func _process(_frame_dt):                   # Unity: void Update()
    var alpha = Engine.get_physics_interpolation_fraction()  # 0..1 within the tick
    visual.global_position = _prev_pos.lerp(_curr_pos, alpha)
# RIGHT: integrate in the fixed step, render via interpolation.
# WRONG: applying forces in _process/Update with frame dt — speed and collisions
# then depend on frame rate and jitter under load.

Most engines offer this for you: Godot's project setting physics/common/physics_interpolation (off by default; it interpolates every node, 2D and 3D) and Unity's Rigidbody.interpolation = Interpolate. Prefer the built-in before hand-rolling, and never do both on the same node.

2. Stop tunneling: CCD + a speed cap
# Fast, small bodies skip past thin colliders between ticks. Two fixes:
body.continuous_cd = true            # RigidBody3D bool (RigidBody2D: CCD_MODE_* enum)
# Unity: rb.collisionDetectionMode = CollisionDetectionMode.ContinuousSpeculative (cheap, covers
# dynamic bodies too) or ContinuousDynamic (fast vs fast); plain Continuous sweeps vs static only.
# Cap velocity so a single step can't move more than ~one collider thickness.
const MAX_SPEED := 40.0
if velocity.length() > MAX_SPEED:
    velocity = velocity.normalized() * MAX_SPEED
# Rule of thumb: max_distance_per_step (= speed / physics_hz) should be < the
# thinnest wall. Raise physics_hz or enable CCD when that fails.
3. Body tuning: mass, drag, gravity scale, material
# Mass is RELATIVE weight in collisions; it does NOT change fall speed (gravity
# accelerates all masses equally). Use drag and gravity_scale to shape feel.
body.mass = 2.0                      # heavier pushes lighter in collisions
body.linear_damp = 0.5               # air drag: higher = stops sooner (Unity 6: linearDamping, was drag)
body.gravity_scale = 1.5             # per-object gravity multiplier (Unity: Rigidbody2D.gravityScale)
# Bounce/slide come from the physics material, not code:
material.bounce = 0.2                # restitution 0..1 (Unity: PhysicsMaterial.bounciness)
material.friction = 0.8              # surface grip
body.physics_material_override = material
4. Collision layers and masks (who collides with whom)
# A body is ON its layer(s) and SCANS the layers in its mask. Both directions of a
# pair must be configured for them to interact.
player.collision_layer = LAYER_PLAYER
player.collision_mask  = LAYER_WORLD | LAYER_ENEMY     # player detects world+enemies
pickup.collision_layer = LAYER_PICKUP
pickup.collision_mask  = LAYER_PLAYER                  # pickup only reacts to player
# Unity equivalent: assign GameObject layers and edit the Physics collision matrix
# (or Physics.IgnoreLayerCollision). Keep a named layer constant table, not magic numbers.

Pitfalls

  • Applying forces/movement in the render frame (Update/_process) makes behavior frame-rate dependent — faster PCs run faster, and collisions get flaky. Do simulation in the fixed step.
  • Visible jitter even with a fixed step usually means no render interpolation: the physics rate and display rate beat against each other. Enable interpolation.
  • Tunneling through thin walls: discrete collision misses fast movers. Enable CCD, cap speed, thicken walls, or raise the physics rate.
  • Expecting heavier objects to fall faster. Gravity is acceleration; mass affects collision response, not fall speed. Use gravity_scale/drag for feel.
  • Exploding stacks / jittery joints: mass ratios too extreme, or too few solver iterations. Keep mass ratios modest and raise iteration counts.
  • Bodies that never rest burn CPU and twitch. Enable sleeping and a sensible sleep threshold for resting objects.
  • One-directional layer setup: A's mask includes B but B's mask excludes A. Detection/collision can need both sides; verify the full matrix.
  • Huge dt spikes (load hitches, breakpoints) blow up integration. Clamp the max physics step / substep count so a stall doesn't launch everything (Godot physics/common/max_physics_steps_per_frame, Unity Maximum Allowed Timestep).
  • Teleports smear across the screen with interpolation on: the renderer blends from the old position. Godot: call reset_physics_interpolation() after moving the node. Unity: move interpolated bodies through the Rigidbody; a direct transform write is ignored unless followed by Physics.SyncTransforms().
  • Feel changes after raising the physics rate: Godot (GodotPhysics and Jolt alike) damps by multiplying velocity by 1 - damp / ticks_per_second each tick, an approximation that drifts as damp approaches the tick rate (at damp >= ticks_per_second the body stops in one tick). Small damping values barely change; re-tune large ones after changing physics_ticks_per_second.

References

  • references/timestep-and-ccd.md — the fixed-timestep accumulator loop, interpolation math, substepping, CCD modes, solver/iteration tuning, sleeping, and a stability checklist.

Related skills

  • godot-physics, unity-physics — concrete bodies, colliders, and callbacks.
  • input-systems — responsive controls, jump buffering, coyote time.
  • game-ai — agent movement that must agree with the physics step.
  • platformer, fps-shooter — genres whose feel depends on this tuning.