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
- Open Godot
- Click “New Scene”
- Choose “Node2D” (this will be your game root)
- Rename it to “Game” in the Scene tab
- Save the scene as “game.tscn”
Step 2: Add Background
- Right-click on the Game node in Scene tree
- Add Child Node -> Select “Sprite2D”
- Rename it to “Background”
- 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
- Right-click on Game node
- Add Child Node -> Select “CharacterBody2D”
- Rename it to “Player”
- 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
- Select the Player node
- Click the Script icon in the top menu
- Create New Script -> Name it “Player.gd”
- 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
- Select the Camera2D node under Player
- 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
- Save all your changes
- Click the Play Scene button (or press F6)
- 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
-
If the background isn’t visible:
- Check Z Index is set to -1
- Ensure “Visible” is checked
- Verify texture is loaded
-
If the player isn’t moving:
- Check if the script is attached
- Verify speed value isn’t 0
- Make sure CollisionShape2D is properly sized
-
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:
- Animations for the player
- A visual indicator for clicked positions
- Pathfinding around obstacles
- Sprint/walk speeds
- 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:
- Add GPUParticles2D as child of Player
- Set Process Material to new ParticleProcessMaterial
- 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:
- Add features one at a time
- Test thoroughly after each addition
- Adjust values to match your game’s feel
- Combine features creatively (like screen shake when dashing)
- Remember to add any new input actions to Project Settings > Input Map
?