Part of the
make-game skill. The skill links here when your agent needs the detail.3d-fps-systems.md.
Rule for Summer Engine builds: scene operations (.tscn) go through the scene
tools (summer_add_node / summer_set_prop / summer_set_resource_property,
or one summer_batch, always with scenePath); script files (.gd) go through
summer_write_file / summer_replace_text. Never cross the streams. For a
production controller with air control and external velocity, see the
fps-controller skill.
Minimum playable base (build this first)
Level with ground + light + WorldEnvironment, the player scene below, mouse look + WASD + jump. Prove “walk around and look” before any weapon exists.Player hierarchy (non-negotiable)
- Yaw rotates the Player ROOT, pitch rotates Head. Rotating the camera on both axes causes gimbal lock and drift.
- Camera under Head inherits both rotations; WeaponHolder under Camera makes weapons track the view exactly.
- Two collision shapes for crouch, toggled via
disabled. Resizing one shape at runtime glitches physics. - Short InteractionRay (2.5m) and long ShootRay (100m) are separate rays with separate masks.
Controller script
Tuning baseline: walk 5.0, sprint 8.0, crouch 2.5, acceleration 10.0, jump 4.5, mouse sensitivity 0.002, FOV 70 default / 80 sprint / 50 aim._physics_process, never _process):
velocity.y -= ProjectSettings.get_setting("physics/3d/default_gravity") * delta
when not on floor. Then move_and_slide().
Sprint + stamina
Sprint only while pressing sprint, moving (velocity > 0.5), not crouching, and stamina > 0. Drain 20/s, regen 15/s to 100. Sprinting also pushes FOV to 80 (lerp at 5.0/delta), aim pulls to 50 - the FOV lerp is a big part of feel.Crouch
Toggle: swap which CollisionShape3D is disabled, lerphead.position.y
between 1.5 and 0.8 at 8.0/delta. Standing up first checks
ceiling_check.is_colliding() - refuse to stand under low geometry.
Crouching cancels sprint and blocks jump.
Headbob + footsteps
Level scene
The real level comes from real assets, not primitives: route the need through theasset-strategy skill (assets.search, then summer_import_asset /
summer_import_asset_by_id), import a ready location or kit that fits, and build
gameplay inside it; weapons and props should share its style. A PlaneMesh ground
is only the walk-test blockout and for pieces no asset covers — never the
shipped level when a real one is available.
Blockout minimum: Node3D root + WorldEnvironment + ground (StaticBody3D with
BoxShape3D collision AND a PlaneMesh visual - a MeshInstance3D alone has no
collision) + DirectionalLight3D at (-45, 30, 0) with shadows + the player
instance at (0, 1, 0).
Environment presets:
- Outdoor: ProceduralSkyMaterial sky, angled DirectionalLight3D with shadows, ambient from sky.
- Indoor: no sky (custom color background), Omni/SpotLights per room, low ambient, SSAO on, optional fog.
Build order
- Input map + collision layer names.
- Player scene per the hierarchy above (MCP), controller script (files).
- Level: blockout ground, light, environment. Walk test.
- Swap the blockout for an imported location + props (
asset-strategy). - Hitscan shooting + one weapon + crosshair (
3d-fps-systems.md). - Interaction (door, pickup), HUD, then weapon roster.
Traps
- Mouse look on the Camera directly - split yaw (root) / pitch (Head) or you get gimbal lock.
- Movement in
_process- variable framerate stutter. CharacterBody3D moves in_physics_processonly. - Forgetting
MOUSE_MODE_CAPTURED(look does nothing) or the Escape release (player trapped). - Camera at y 0 - you see from your feet. Head height is 1.5-1.7.
- Level geometry without StaticBody3D collision - the player falls through anything that is only a MeshInstance3D.
- One resized collision shape for crouch instead of two toggled shapes.
- ShootRay mask including the Player layer - you shoot yourself; mask 6.
Source:
library/skills/make-game/references/3d-fps.md, rendered from summer-engine@4.3.0 on npm.
