Back to Blog
Tutorial
January 5, 202510 min read

Implementing Leaderboards in Godot: A Complete Guide

Step-by-step tutorial on adding competitive leaderboards to your Godot game with automatic ranking.

Leaderboards add a competitive edge to your game and keep players engaged. In this comprehensive guide, we'll show you how to implement global and custom leaderboards in your Godot game using Godot BaaS.

Why Leaderboards Matter

Leaderboards provide several benefits for your game:

  • Increase player engagement and retention
  • Create competitive motivation to improve
  • Build community through friendly competition
  • Provide goals and milestones for players
  • Enable seasonal events and tournaments

Types of Leaderboards

Godot BaaS supports multiple leaderboard types:

  • Global: All-time rankings across all players
  • Daily: Resets every 24 hours
  • Weekly: Resets every Monday
  • Monthly: Resets on the 1st of each month
  • Custom: Create leaderboards for specific game modes or events

Step 1: Create a Leaderboard

First, create your leaderboard in the Godot BaaS dashboard or via code:

# Create leaderboards in your game initialization
func setup_leaderboards():
    # These are created automatically when you submit scores
    # Just define the leaderboard IDs you'll use
    var leaderboard_ids = {
        "global_score": "global",
        "daily_score": "daily",
        "weekly_score": "weekly",
        "speedrun": "custom"
    }

Step 2: Submit Scores

Submit player scores to your leaderboards:

# LeaderboardManager.gd
extends Node

func submit_score(leaderboard_id: String, score: int, metadata: Dictionary = {}):
    GodotBaaS.submit_leaderboard_score(leaderboard_id, score, metadata)

func _on_score_submitted(data):
    print("Score submitted! Rank: ", data.rank)
    print("New personal best: ", data.is_personal_best)

func _on_score_failed(error):
    print("Failed to submit score: ", error)

func _ready():
    GodotBaaS.score_submitted.connect(_on_score_submitted)
    GodotBaaS.score_submit_failed.connect(_on_score_failed)

# Example: Submit score after level completion
func on_level_complete(final_score: int):
    var metadata = {
        "level": current_level,
        "time": completion_time,
        "deaths": death_count
    }
    submit_score("global_score", final_score, metadata)

Step 3: Fetch Leaderboard Data

Retrieve and display leaderboard rankings:

func fetch_leaderboard(leaderboard_id: String, limit: int = 10, offset: int = 0):
    GodotBaaS.get_leaderboard(leaderboard_id, limit, offset)

func fetch_player_rank(leaderboard_id: String):
    GodotBaaS.get_player_rank(leaderboard_id)

func fetch_nearby_players(leaderboard_id: String, range: int = 5):
    # Get players ranked near the current player
    GodotBaaS.get_nearby_ranks(leaderboard_id, range)

func _on_leaderboard_loaded(data):
    print("Leaderboard entries:")
    for entry in data.entries:
        print(entry.rank, ". ", entry.player_name, " - ", entry.score)

func _on_rank_loaded(data):
    print("Your rank: ", data.rank, " / ", data.total_players)
    print("Your score: ", data.score)

func _ready():
    GodotBaaS.leaderboard_loaded.connect(_on_leaderboard_loaded)
    GodotBaaS.player_rank_loaded.connect(_on_rank_loaded)

Step 4: Build the UI

Create an attractive leaderboard display:

# LeaderboardUI.gd
extends Control

@onready var entries_container = $VBoxContainer/EntriesContainer
@onready var player_rank_label = $VBoxContainer/PlayerRank

var entry_scene = preload("res://scenes/leaderboard_entry.tscn")

func display_leaderboard(entries: Array):
    # Clear existing entries
    for child in entries_container.get_children():
        child.queue_free()
    
    # Add new entries
    for entry in entries:
        var entry_node = entry_scene.instantiate()
        entry_node.set_rank(entry.rank)
        entry_node.set_player_name(entry.player_name)
        entry_node.set_score(entry.score)
        
        # Highlight current player
        if entry.is_current_player:
            entry_node.highlight()
        
        entries_container.add_child(entry_node)

func display_player_rank(rank: int, total: int, score: int):
    player_rank_label.text = "Your Rank: #%d / %d (Score: %d)" % [rank, total, score]

func refresh_leaderboard():
    LeaderboardManager.fetch_leaderboard("global_score", 50)
    LeaderboardManager.fetch_player_rank("global_score")

Step 5: Real-Time Updates

Enable real-time leaderboard updates using WebSockets:

func enable_realtime_updates(leaderboard_id: String):
    GodotBaaS.subscribe_to_leaderboard(leaderboard_id)

func _on_leaderboard_updated(data):
    print("Leaderboard updated!")
    print("New top score: ", data.top_score)
    refresh_leaderboard()

func _ready():
    GodotBaaS.leaderboard_updated.connect(_on_leaderboard_updated)
    enable_realtime_updates("global_score")

Step 6: Handle Seasonal Resets

Manage leaderboard resets for daily, weekly, and monthly boards:

func check_leaderboard_reset():
    # Godot BaaS handles resets automatically
    # But you can notify players when a new season starts
    GodotBaaS.get_leaderboard_info("daily_score")

func _on_leaderboard_info(data):
    if data.reset_at:
        var time_until_reset = data.reset_at - Time.get_unix_time_from_system()
        print("Leaderboard resets in: ", format_time(time_until_reset))

func format_time(seconds: float) -> String:
    var hours = int(seconds / 3600)
    var minutes = int((seconds - hours * 3600) / 60)
    return "%dh %dm" % [hours, minutes]

Advanced Features

Multiple Leaderboards

Create separate leaderboards for different game modes:

var leaderboards = {
    "story_mode": "story_high_scores",
    "endless_mode": "endless_high_scores",
    "time_trial": "time_trial_best_times",
    "multiplayer": "multiplayer_rankings"
}

func submit_to_mode_leaderboard(mode: String, score: int):
    if mode in leaderboards:
        submit_score(leaderboards[mode], score)

Friend Leaderboards

Show rankings among friends only:

func fetch_friend_leaderboard(leaderboard_id: String):
    var friend_ids = get_friend_list()  # Your friend system
    GodotBaaS.get_leaderboard_filtered(leaderboard_id, friend_ids)

func _on_friend_leaderboard_loaded(data):
    print("Friend rankings:")
    for entry in data.entries:
        print(entry.rank, ". ", entry.player_name, " - ", entry.score)

Best Practices

  • Prevent cheating: Validate scores server-side (Godot BaaS includes anti-cheat)
  • Cache data: Store leaderboard data locally to reduce API calls
  • Show context: Display player's rank and nearby competitors
  • Celebrate achievements: Highlight when players reach new ranks
  • Provide filters: Let players view different time periods and categories
  • Handle ties: Define clear tiebreaker rules (timestamp, secondary score)

Testing Your Leaderboards

  1. Submit test scores with different values
  2. Verify ranking order is correct
  3. Test with multiple accounts to simulate competition
  4. Verify reset functionality for daily/weekly/monthly boards
  5. Test real-time updates with multiple clients
  6. Check performance with large datasets (1000+ entries)

Next Steps

Now that you have leaderboards, consider adding:

  • Rewards for top rankings
  • Seasonal tournaments with prizes
  • Achievement badges for milestones
  • Social sharing of high scores
  • Replay systems to watch top players

Ready to add leaderboards to your game?

Start building competitive features with Godot BaaS. No backend coding required.

Start Building Free