Skip to content
Build a world

Your First Godot 3D Scene: Movement, a Camera, and a Playable Room

Build a small 3D test room in Godot 4 with a floor, a visible player, a fixed camera, gravity, jumping, and a reset when you fall.

On this page

Build a small 3D room with a visible player, a camera, and a floor. You will add movement, gravity, jumping, and a reset when the player falls. The scene uses simple shapes so you can check collision and camera placement before importing art.

1. Choose a small, clear first result

Create a Godot 4 project with GDScript. Forward+ is a useful starting point on a supported desktop; Compatibility also supports the basic scene in this article. If your target is a browser, start with Compatibility. The renderer selection guide explains that choice before you invest in effects.

Keep the level, camera, and player in one scene for this first exercise. Save it as Main.tscn. In Project Settings → Display → Window → Size, use a 960 × 540 viewport. This is a test room, not a finished character controller: movement uses world axes, the camera does not follow, and there is no mouse look.

2. Assemble a room that can actually collide

Main.tscnGodot scene guide · read-only
Scene

Select a node to inspect it.

InspectorExample settings
MainNode3D
Main

Groups objects in 3D space. Moving or rotating it also transforms its children.

Position
(0, 0, 0)
Scale
(1, 1, 1)
In this example

The room root

All transforms start at their defaults unless a setting below says otherwise.

Explore the setup here. Build and run it in Godot.

Create a Node3D root named Main. Add Floor as a StaticBody3D and assign its position. Add the two children shown above. For the mesh, choose New BoxMesh and expand the resource to enter its Size. For the collider, choose New BoxShape3D and enter the same Size. Keep both children centered at zero; the Floor parent supplies the offset. Its top surface now sits at Y = 0.

Godot separates visible geometry from collision geometry. A mesh can look like a solid platform and still let the player fall through. The StaticBody3D supplies the fixed physics body; its CollisionShape3D supplies the boundary. Leave Floor on collision layer 1. If these node roles are unfamiliar, use the 2D player assembly exercise first: the body/visual/shape distinction carries over, although that browser activity does not simulate 3D.

3. Give the player matching visible and physical shapes

Add a CharacterBody3D named Player under Main, at position (0, 1, 0). Give it a MeshInstance3D with a CapsuleMesh and a CollisionShape3D with a CapsuleShape3D. Set Radius to 0.4 and Height to 1.8 on both resources. Keep the child transforms at zero and the root scale at (1, 1, 1). Leave Player’s collision mask checking layer 1 so it can encounter Floor.

Select the player mesh, find Material Override, and create a StandardMaterial3D. Set Albedo → Color to green. Give the floor a separate material with a neutral gray albedo. The colors help you distinguish the objects while debugging; avoid transparent or emissive materials for now. Two matching capsule dimensions make later collision checks easier than trying to guess the physical size of a detailed imported model.

4. Add the camera and real scene lighting

Add Camera3D directly under Main, at position (0, 6, 9). Set Rotation Degrees to (-35, 0, 0) and enable Current. The camera looks along its local negative Z axis; the negative X rotation tips it down toward the floor. Keep the camera outside Player so jumping does not move the viewpoint.

Add the Sun node as DirectionalLight3D, with rotation (-55, -25, 0) degrees, and enable its shadows. Add WorldEnvironment, create an Environment resource, and set Background → Mode to Custom Color with a dark gray color. Under Ambient Light, choose Color as Source, use a pale gray color, and start Energy at 0.3. These are exercise starting values, not a universal lighting preset. Editor preview lighting does not replace these scene nodes.

5. Wire input, movement, gravity, and jumping

  • In Project Settings → Input Map, add move_left (A, Left Arrow) and move_right (D, Right Arrow).
  • Add move_forward (W, Up Arrow) and move_back (S, Down Arrow).
  • Add jump (Space). Action names are case-sensitive; use the underscores shown here.

Attach player.gd to Player and replace the template with the script below. Horizontal motion is on the X/Z plane; Y is height. This uses a deliberately explicit gravity value so the exercise does not depend on changes to the project’s gravity settings.

gdscript
extends CharacterBody3D

@export var speed: float = 5.0
@export var jump_speed: float = 5.0
@export var gravity: float = 18.0

func _physics_process(delta: float) -> void:
    var input := Input.get_vector(
        "move_left", "move_right", "move_forward", "move_back"
    )
    velocity.x = input.x * speed
    velocity.z = input.y * speed

    if not is_on_floor():
        velocity.y -= gravity * delta
    elif Input.is_action_just_pressed("jump"):
        velocity.y = jump_speed
    else:
        velocity.y = 0.0

    move_and_slide()

    if global_position.y < -10.0:
        global_position = Vector3(0.0, 2.0, 0.0)
        velocity = Vector3.ZERO

Input.get_vector limits diagonal input so pressing two keys does not make the character faster. We overwrite only X and Z before handling Y; replacing the whole velocity with horizontal input would erase the falling speed. Gravity changes velocity over time, so it uses delta. move_and_slide consumes velocity and handles the movement timestep itself; do not multiply the final velocity by delta a second time.

The reset is a simple test-room rule: if the player falls below Y = -10, move it back above the floor and clear its velocity. Main remains untransformed, so the chosen global position is easy to reason about. In a larger level, store a spawn marker and resolve that position instead of embedding a coordinate in the controller.

6. Test behavior before adding more systems

  • Release every key: the player should stop horizontally.
  • Walk diagonally, then straight: the pace should be consistent.
  • Jump while grounded, then press Space again in mid-air: there should be no second jump.
  • Walk off an edge: the player should fall and return above the floor.
  • Run Main with F6, then set Main.tscn as the main scene and run the project with F5.

If the capsule falls immediately, inspect the two collision resources and the layer/mask pair. If it floats visibly above the floor, compare mesh and shape dimensions with Debug → Visible Collision Shapes enabled while running. If W moves toward the camera, check the order of the forward/back actions passed to get_vector; forward should produce negative Z in this fixed-camera setup.

7. Turn the room into your own experiment

Duplicate Floor, give the duplicate unique mesh and shape resources before resizing them, then make a small raised platform. Change only one dimension at a time and test the jump again. This is a useful way to discover the relationship between collision size, camera framing, and jump height without hiding the problem behind an animation.

Next, keep the same room and follow the lighting and materials experiment. When the controller becomes a reusable piece, the scenes and prefabs guide explains how to extract Player into its own scene. Coming from Unity? Compare this setup with the 3D migration guide.

Keep building the same room

Make the room readable

Keep the same scene and compare materials, shadows, and environment lighting.

Next: Light the room
Verification notes

Sources and revision context

Updated September 8, 2026. Clarified the room setup and controller explanations. Selected checks passed in Godot 4.7.2, standard macOS build, headless, on September 8, 2026. Checked landing, forward movement, jumping, rejection of a second airborne jump, and falling reset. Visible rendering, lighting, and exports were not tested. These checks used the article scripts with supplied scene fixtures; they are not a full tutorial playtest.