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.

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

FloorStaticBody3D
Main/Floor

A physics body for solid geometry that does not move, such as the floor. Its collision shape defines the solid boundary.

Position
(0, -0.25, 0)
Collision layer
1
In this example

The top of this floor sits at Y = 0.

Child of Main

MeshInstance3DMeshInstance3D
Main/Floor/MeshInstance3D

Displays a mesh. The material controls its appearance; a separate physics body and shape handle collision.

Mesh
BoxMesh
Size
(12, 0.5, 12)
Position
(0, 0, 0)
In this example

The visible floor. Match the collider dimensions.

Child of Main/Floor

CollisionShape3DCollisionShape3D
Main/Floor/CollisionShape3D

Gives its parent physics body or area a collision boundary. A visible mesh alone does not create collision.

Shape
BoxShape3D
Size
(12, 0.5, 12)
Position
(0, 0, 0)
In this example

The physical floor boundary.

Child of Main/Floor

PlayerCharacterBody3D
Main/Player

A body controlled by your script. It provides movement and collision methods; it does not walk or fall without code.

Position
(0, 1, 0)
Script
player.gd
Collision mask
1
In this example

Attach the movement script to this root, not to the mesh.

Child of Main

MeshInstance3DMeshInstance3D
Main/Player/MeshInstance3D

Displays a mesh. The material controls its appearance; a separate physics body and shape handle collision.

Mesh
CapsuleMesh
Radius
0.4
Height
1.8
Position
(0, 0, 0)
In this example

The visible player capsule.

Child of Main/Player

CollisionShape3DCollisionShape3D
Main/Player/CollisionShape3D

Gives its parent physics body or area a collision boundary. A visible mesh alone does not create collision.

Shape
CapsuleShape3D
Radius
0.4
Height
1.8
Position
(0, 0, 0)
In this example

Match this capsule to the visible player.

Child of Main/Player

Camera3DCamera3D
Main/Camera3D

Defines the view used to render the game. The editor viewport is separate from this game camera.

Position
(0, 6, 9)
Rotation degrees
(-35, 0, 0)
Current
On
In this example

A fixed game camera, separate from Player.

Child of Main

SunDirectionalLight3D
Main/Sun

Lights the scene with parallel rays. Its rotation sets the light direction; its position does not.

Rotation degrees
(-55, -25, 0)
Shadow enabled
On
In this example

Rotate the light to change the direction of its rays.

Child of Main

WorldEnvironmentWorldEnvironment
Main/WorldEnvironment

Holds the Environment resource used for background, ambient lighting, and supported visual effects.

Environment
New Environment
Background mode
Custom Color
Ambient source
Color
Ambient energy
0.3
In this example

Save the environment inside the scene; editor preview lighting is separate.

Child of Main

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.

Main3D.tscnGodot scene guide · read-only
Scene

Select a node to inspect it.

InspectorExample settings
Main3DNode3D
Main3D

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

In this example

The room root.

WorldEnvironmentWorldEnvironment
Main3D/WorldEnvironment

Holds the Environment resource used for background, ambient lighting, and supported visual effects.

Ambient light
Color
Ambient energy
0.45
In this example

Sets the background and the ambient light. With no ambient light and no lamp, a correct scene still renders black.

Child of Main3D

DirectionalLight3DDirectionalLight3D
Main3D/DirectionalLight3D

Lights the scene with parallel rays. Its rotation sets the light direction; its position does not.

Energy
1.2
Shadow enabled
On
In this example

Rotation sets the light direction; position does not. Shadows are off by default in a new light.

Child of Main3D

Camera3DCamera3D
Main3D/Camera3D

Defines the view used to render the game. The editor viewport is separate from this game camera.

In this example

What the running game sees. Moving the editor viewport does not move this.

Child of Main3D

PlayerCharacterBody3D
Main3D/Player

A body controlled by your script. It provides movement and collision methods; it does not walk or fall without code.

In this example

Holds the movement script. Forward is negative Z.

Child of Main3D

MeshInstance3DMeshInstance3D
Main3D/Player/MeshInstance3D

Displays a mesh. The material controls its appearance; a separate physics body and shape handle collision.

In this example

The capsule you can see.

Child of Main3D/Player

CollisionShape3DCollisionShape3D
Main3D/Player/CollisionShape3D

Gives its parent physics body or area a collision boundary. A visible mesh alone does not create collision.

In this example

The capsule the physics engine knows about. These two are separate on purpose.

Child of Main3D/Player

GroundStaticBody3D
Main3D/Ground

A physics body for solid geometry that does not move, such as the floor. Its collision shape defines the solid boundary.

In this example

A flat box. Its mesh and its collision shape are set independently, which is the usual cause of a player falling through visible floor.

Child of Main3D

MeshInstance3DMeshInstance3D
Main3D/Ground/MeshInstance3D

Displays a mesh. The material controls its appearance; a separate physics body and shape handle collision.

In this example

The visible floor.

Child of Main3D/Ground

CollisionShape3DCollisionShape3D
Main3D/Ground/CollisionShape3D

Gives its parent physics body or area a collision boundary. A visible mesh alone does not create collision.

In this example

The solid floor. Missing this is why the player falls forever.

Child of Main3D/Ground

A 3D room that renders and can be walked around in. Select a node to see which of those two jobs it does.

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

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.

The Godot 3D viewport showing a white capsule standing on a green ground plane in front of a pale wall, with three spheres beside it: a glossy grey one, a glossy blue one and a matte red one, each casting a shadow.
The room this guide builds. The capsule is a mesh and a collision shape on one CharacterBody3D, and the ground is the same arrangement on a StaticBody3D. The three spheres are there to show one light reading across different materials.
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.stable.steam.ed1daf0bf, headless, on 2026-09-25. 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.