fervor [>]CODING & CURIOSITY
FERVOR LEARNING SYSTEMTUTORIALS
← Godot

Godot / 8 MIN READ

Standard Player Camera Movement

Standard Player Camera Movement

From the original Fervor library. Examples may use older package versions.

Building a Player with Movement in Godot - Complete Tutorial

Part 1: Basic Scene Setup

Step 1: Create the Game Scene

  1. Open Godot
  2. Click “New Scene”
  3. Choose “Node2D” (this will be your game root)
  4. Rename it to “Game” in the Scene tab
  5. Save the scene as “game.tscn”

Step 2: Add Background

  1. Right-click on the Game node in Scene tree
  2. Add Child Node -> Select “Sprite2D”
  3. Rename it to “Background”
  4. In the Inspector panel:
    • Set “Texture” to your background image
    • Set “Z Index” to -1 (makes it stay behind everything)
    • Position at (0, 0)

Step 3: Create Player Node

  1. Right-click on Game node
  2. Add Child Node -> Select “CharacterBody2D”
  3. Rename it to “Player”
  4. Add these child nodes to Player:
    • Sprite2D (for visuals)
    • CollisionShape2D (for collisions)
    • Camera2D (for following the player)

Your scene tree should now look like this:

Game (Node2D)
  ├─ Background (Sprite2D)
  └─ Player (CharacterBody2D)
      ├─ Sprite2D
      ├─ CollisionShape2D
      └─ Camera2D

Part 2: Basic Movement Setup

Step 1: Add Player Script

  1. Select the Player node
  2. Click the Script icon in the top menu
  3. Create New Script -> Name it “Player.gd”
  4. Start with basic movement code:
extends CharacterBody2D

@export var speed = 300

func _physics_process(delta):
    # Get player input
    var direction = Input.get_vector("ui_left", "ui_right", "ui_up", "ui_down")
    
    # Set velocity
    velocity = direction * speed
    
    # Move the player
    move_and_slide()

Step 2: Add Camera Follow

  1. Select the Camera2D node under Player
  2. In the Inspector:
    • Check “Current”
    • Enable “Drag Horizontal” and “Drag Vertical”
    • Set drag margins to 0.1

Add this camera setup code to your Player script’s _ready function:

func _ready():
    # Setup camera properties
    $Camera2D.make_current()
    $Camera2D.drag_horizontal_enabled = true
    $Camera2D.drag_vertical_enabled = true
    $Camera2D.drag_left_margin = 0.1
    $Camera2D.drag_right_margin = 0.1
    $Camera2D.drag_top_margin = 0.1
    $Camera2D.drag_bottom_margin = 0.1

Part 3: Adding Click-to-Move

Step 1: Add Target Position Variable

Add this at the top of your script with other variables:

var target_position = null

Step 2: Add Mouse Input Handler

Add this new function to handle mouse clicks:

func _input(event):
    if event is InputEventMouseButton:
        if event.button_index == MOUSE_BUTTON_LEFT and event.pressed:
            target_position = get_global_mouse_position()

Step 3: Update Movement Code

Replace your _physics_process function with this enhanced version:

func _physics_process(delta):
    var direction = Vector2.ZERO
    
    # Handle keyboard input
    var keyboard_direction = Input.get_vector("ui_left", "ui_right", "ui_up", "ui_down")
    if keyboard_direction.length() > 0:
        # If using keyboard, clear any click target
        target_position = null
        direction = keyboard_direction
    # Handle click-to-move if we have a target
    elif target_position:
        # Get direction to target
        direction = (target_position - global_position).normalized()
        # Check if we're close enough to stop
        if global_position.distance_to(target_position) < 5:
            target_position = null
            direction = Vector2.ZERO
    
    # Update sprite direction
    if direction.x < 0:
        $Sprite2D.flip_h = true
    elif direction.x > 0:
        $Sprite2D.flip_h = false
    
    # Set velocity and move
    velocity = direction * speed
    move_and_slide()

Final Complete Script

Here’s the complete Player.gd script with everything combined:

extends CharacterBody2D

@export var speed = 300
var target_position = null

func _ready():
    # Setup camera properties
    $Camera2D.make_current()
    $Camera2D.drag_horizontal_enabled = true
    $Camera2D.drag_vertical_enabled = true
    $Camera2D.drag_left_margin = 0.1
    $Camera2D.drag_right_margin = 0.1
    $Camera2D.drag_top_margin = 0.1
    $Camera2D.drag_bottom_margin = 0.1

func _input(event):
    if event is InputEventMouseButton:
        if event.button_index == MOUSE_BUTTON_LEFT and event.pressed:
            target_position = get_global_mouse_position()

func _physics_process(delta):
    var direction = Vector2.ZERO
    
    # Handle keyboard input
    var keyboard_direction = Input.get_vector("ui_left", "ui_right", "ui_up", "ui_down")
    if keyboard_direction.length() > 0:
        target_position = null
        direction = keyboard_direction
    elif target_position:
        direction = (target_position - global_position).normalized()
        if global_position.distance_to(target_position) < 5:
            target_position = null
            direction = Vector2.ZERO
    
    # Update sprite direction
    if direction.x < 0:
        $Sprite2D.flip_h = true
    elif direction.x > 0:
        $Sprite2D.flip_h = false
    
    # Set velocity and move
    velocity = direction * speed
    move_and_slide()

Testing Your Game

  1. Save all your changes
  2. Click the Play Scene button (or press F6)
  3. Your character should now:
    • Move with arrow keys/WASD
    • Move to clicked positions with left mouse button
    • Face the direction it’s moving
    • Have the camera smoothly follow it
    • Stop when it reaches clicked positions

Common Issues and Solutions

  1. If the background isn’t visible:

    • Check Z Index is set to -1
    • Ensure “Visible” is checked
    • Verify texture is loaded
  2. If the player isn’t moving:

    • Check if the script is attached
    • Verify speed value isn’t 0
    • Make sure CollisionShape2D is properly sized
  3. If the camera isn’t following:

    • Verify Camera2D is set to “Current”
    • Check if camera settings are properly set in _ready()

Next Steps

You can enhance this basic setup by adding:

  1. Animations for the player
  2. A visual indicator for clicked positions
  3. Pathfinding around obstacles
  4. Sprint/walk speeds
  5. Interaction with objects

Bonus:

🚀 Cool Bonus Features for Your Player

1. Click Marker Effect

Show where the player is going to move!

extends CharacterBody2D

# Add at top of script with other variables
var click_marker: Sprite2D

func _ready():
    # Add this to your _ready function
    setup_click_marker()

func setup_click_marker():
    click_marker = Sprite2D.new()
    # You can replace this with your own marker texture
    var marker_texture = preload("res://marker.png")
    click_marker.texture = marker_texture
    click_marker.visible = false
    get_parent().add_child(click_marker)

func _input(event):
    if event is InputEventMouseButton:
        if event.button_index == MOUSE_BUTTON_LEFT and event.pressed:
            target_position = get_global_mouse_position()
            # Show marker at click position
            click_marker.global_position = target_position
            click_marker.visible = true
            
            # Optional: Make it fade out
            var tween = create_tween()
            tween.tween_property(click_marker, "modulate:a", 0.0, 1.0)
            tween.tween_callback(func(): click_marker.visible = false)

2. Sprint and Walk Speed

Add running with Shift key!

@export var walk_speed = 300
@export var sprint_speed = 500
@export var current_speed = walk_speed

func _physics_process(delta):
    # Add at start of _physics_process
    current_speed = sprint_speed if Input.is_action_pressed("ui_shift") else walk_speed
    
    # Then replace 'speed' with 'current_speed' in movement code
    velocity = direction * current_speed

3. Dust Trail Effect

Add particle effects when moving!

# Add at top with other variables
@onready var dust_particles = $GPUParticles2D

# Add to _physics_process
if velocity.length() > 0:
    dust_particles.emitting = true
else:
    dust_particles.emitting = false

Setup in editor:

  1. Add GPUParticles2D as child of Player
  2. Set Process Material to new ParticleProcessMaterial
  3. Configure particles (lifetime, speed, color, etc.)

4. Smooth Camera Zoom

Add mouse wheel zoom to your camera!

@export var min_zoom = 0.5
@export var max_zoom = 2.0
@export var zoom_speed = 0.1

func _input(event):
    if event is InputEventMouseButton:
        if event.button_index == MOUSE_BUTTON_WHEEL_UP:
            zoom_camera(-zoom_speed)
        elif event.button_index == MOUSE_BUTTON_WHEEL_DOWN:
            zoom_camera(zoom_speed)

func zoom_camera(zoom_factor):
    var current_zoom = $Camera2D.zoom.x
    var new_zoom = clamp(current_zoom + zoom_factor, min_zoom, max_zoom)
    
    # Smooth zoom transition
    var tween = create_tween()
    tween.tween_property($Camera2D, "zoom", Vector2(new_zoom, new_zoom), 0.1)

5. Interaction System

Add ability to interact with objects!

# Add to player script
var interaction_range = 100

func _input(event):
    if event.is_action_pressed("interact"):  # Set this up in Project Settings > Input Map
        check_interaction()

func check_interaction():
    # Get all objects in scene
    var potential_interactables = get_tree().get_nodes_in_group("interactable")
    
    # Find closest within range
    var closest_distance = interaction_range
    var closest_interactable = null
    
    for interactable in potential_interactables:
        var distance = global_position.distance_to(interactable.global_position)
        if distance < closest_distance:
            closest_distance = distance
            closest_interactable = interactable
    
    # Interact with closest object
    if closest_interactable:
        closest_interactable.interact()

Then for interactable objects:

# Interactable.gd
extends Node2D

func _ready():
    add_to_group("interactable")

func interact():
    print("Interacting with object!")
    # Add your interaction code here

6. Simple Player Stats

Add basic RPG-style stats!

# Add at top of player script
@export var max_stamina = 100.0
var current_stamina = max_stamina
var stamina_regen = 10.0  # Per second

# Add to _physics_process
func handle_stamina(delta):
    if velocity.length() > 0 and current_speed == sprint_speed:
        current_stamina = max(0, current_stamina - 20 * delta)
        if current_stamina == 0:
            current_speed = walk_speed
    else:
        current_stamina = min(max_stamina, current_stamina + stamina_regen * delta)

7. Screen Shake Effect

Add impact to your movement!

func shake_camera(intensity: float, duration: float):
    var tween = create_tween()
    var start_position = $Camera2D.offset
    
    for i in range(int(duration * 20)):  # 20 shakes per second
        var rand_offset = Vector2(
            randf_range(-intensity, intensity),
            randf_range(-intensity, intensity)
        )
        tween.tween_property($Camera2D, "offset", rand_offset, 0.05)
    
    tween.tween_property($Camera2D, "offset", Vector2.ZERO, 0.1)

# Example usage:
# shake_camera(5.0, 0.5) # Intensity of 5, duration of 0.5 seconds

8. Dash Ability

Add a quick dash move!

@export var dash_speed = 1000
@export var dash_duration = 0.2
var can_dash = true
var is_dashing = false

func _input(event):
    if event.is_action_pressed("dash") and can_dash:  # Set up "dash" in Input Map
        perform_dash()

func perform_dash():
    is_dashing = true
    can_dash = false
    
    # Store current velocity for dash direction
    var dash_direction = velocity.normalized()
    if dash_direction == Vector2.ZERO:
        dash_direction = Vector2.RIGHT if !$Sprite2D.flip_h else Vector2.LEFT
    
    # Create dash effect
    velocity = dash_direction * dash_speed
    
    # Reset after dash
    var tween = create_tween()
    tween.tween_callback(func(): is_dashing = false).set_delay(dash_duration)
    tween.tween_callback(func(): can_dash = true).set_delay(1.0)  # Dash cooldown

9. Simple UI Elements

Add a basic UI to show player state!

# Create a new scene for UI (UI.tscn)
# Add to your player scene as a child node

@onready var ui = $UI

func _process(delta):
    ui.update_stamina(current_stamina / max_stamina)
    ui.update_speed(current_speed == sprint_speed)

10. Follow Trail Effect

Add a cool trail when moving!

# Add Line2D node as child of player
@onready var trail = $Line2D
var trail_length = 20

func _physics_process(delta):
    # Add to end of _physics_process
    update_trail()

func update_trail():
    trail.add_point(position)
    while trail.get_point_count() > trail_length:
        trail.remove_point(0)

Implementation Tips:

  1. Add features one at a time
  2. Test thoroughly after each addition
  3. Adjust values to match your game’s feel
  4. Combine features creatively (like screen shake when dashing)
  5. Remember to add any new input actions to Project Settings > Input Map

?

Keep your curiosity going.Explore more Godot →
287 TUTORIALS · 22 TOPICSREADY