Competitive Systems Are Retention Systems
A well-designed competitive system keeps players engaged for years. League of Legends, Valorant, Rocket League — their competitive ladders are the primary retention mechanism. Players come back not just for gameplay, but to climb.
For indie developers adding competitive modes, the fundamentals matter more than complexity. A simple, fair ranking system outperforms an elaborate but broken one.
Rating Systems
Elo Rating
The classic chess rating system, adapted for games:
struct FEloRating
{
float Rating = 1000.f; // Starting rating
static constexpr float K = 32.f; // Adjustment speed
static void UpdateRatings(FEloRating& Winner, FEloRating& Loser)
{
float ExpectedWin = 1.f / (1.f + FMath::Pow(10.f, (Loser.Rating - Winner.Rating) / 400.f));
float ExpectedLose = 1.f - ExpectedWin;
Winner.Rating += K * (1.f - ExpectedWin);
Loser.Rating += K * (0.f - ExpectedLose);
}
};
Pros: Simple, well-understood, works for 1v1. Cons: Slow to converge for new players, doesn't handle team games well.
Glicko-2
An evolution of Elo that tracks rating uncertainty:
- Rating: Skill estimate (like Elo)
- Rating Deviation (RD): How confident the system is in the rating. New players have high RD (uncertain), veterans have low RD (confident).
- Volatility: How much the player's skill is expected to fluctuate
When RD is high, rating changes are large (system is learning). When RD is low, rating changes are small (system is confident).
Best for: Games with irregular play patterns. Players who take breaks return with higher RD, so the system readjusts quickly.
TrueSkill / TrueSkill 2
Microsoft's system designed specifically for multiplayer games:
- Handles team games natively (not just 1v1)
- Handles free-for-all with multiple players
- Tracks per-player skill in team contexts
- Fast convergence for new players
Best for: Team-based games, battle royales, any format beyond 1v1.
Which to Choose
| Game Type | Recommended System |
|---|---|
| 1v1 | Glicko-2 |
| Team (small, 2v2-5v5) | TrueSkill 2 |
| Battle Royale / FFA | TrueSkill 2 |
| Turn-based | Elo or Glicko-2 |
Visible Rank vs Hidden MMR
The Two-Layer System
Most competitive games separate:
- MMR (Matchmaking Rating): Hidden number used for matchmaking. Accurate, fluctuates freely.
- Visible Rank: Player-facing tier (Bronze, Silver, Gold, etc.). Moves more slowly, provides progression feeling.
Why Separate Them?
MMR is volatile — it swings by 30 points per game. Showing this to players creates anxiety ("I dropped 60 points today!"). Visible ranks are stable — they only change after accumulated performance, creating a smoother emotional experience.
Tier Design
Iron (0-499 MMR)
Bronze (500-999)
Silver (1000-1499)
Gold (1500-1999)
Platinum (2000-2499)
Diamond (2500-2999)
Master (3000-3499)
Grandmaster (3500+, top 500 players)
Within each tier: 3-4 divisions (Gold IV, Gold III, Gold II, Gold I). This creates frequent promotion moments that feel rewarding.
Promotion series: Require winning 2 out of 3 games at the tier boundary. This makes promotions feel earned and creates dramatic "clutch" moments.
Demotion protection: After promoting, players have a grace period where they can't immediately drop back. This prevents frustrating oscillation.
Placement Matches
Purpose
New players need initial calibration. Placement matches determine starting rank based on performance, not just wins.
Design
10 placement matches → Initial rank assignment
Match 1-3: Wide MMR variance (testing range)
Match 4-7: Narrowing based on results
Match 8-10: Fine-tuning near expected rank
Performance factors beyond win/loss:
- Individual score / KDA
- Objective contribution
- Damage dealt / received ratio
- Win margin
Placement Psychology
- Don't place too high: Players placed above their skill level lose repeatedly and quit. Better to place slightly low and let them climb.
- Don't place too low: Experienced players stomping beginners is bad for everyone. Use previous season data for returning players.
- Show progress: "Match 7/10" with a visual bracket of possible placement outcomes keeps players engaged through all 10 matches.
Matchmaking Queue Design
Core Algorithm
void UMatchmaker::FindMatch(FPlayerSession& Player)
{
// 1. Calculate acceptable MMR range (widens over time)
float WaitTime = Player.GetQueueTime();
float AcceptableRange = BaseRange + (WaitTime * RangeExpansionRate);
// Start at ±100 MMR, expand by 50 per minute
// 2. Find players in range
TArray<FPlayerSession*> Candidates = GetPlayersInRange(
Player.MMR - AcceptableRange,
Player.MMR + AcceptableRange);
// 3. Score candidates by match quality
for (FPlayerSession* Candidate : Candidates)
{
float Quality = CalculateMatchQuality(Player, *Candidate);
// Factors: MMR difference, ping, party size, recent opponents
}
// 4. Create match from best-quality group
if (BestGroup.Quality > MinimumQuality)
CreateMatch(BestGroup);
}
Queue Time vs Match Quality
The fundamental tradeoff: wait longer for better matches, or play sooner with worse matches.
Small player base (indie games): Prioritize queue time. Players won't wait 10 minutes for a match. Accept wider MMR ranges, compensate with dynamic handicaps if needed.
Large player base: Prioritize match quality. Enough players exist to find good matches quickly.
Peak and Off-Peak Handling
Player count varies dramatically by time of day:
- Peak hours: Strict matchmaking, short queues
- Off-peak: Relax MMR requirements, or offer alternative modes
- Very low population: Consider bot backfill or cross-region matchmaking
Server-Authoritative Anti-Cheat
The Authority Principle
The server is the only source of truth. Clients are untrusted input devices.
Client: "I fired and hit Player B in the head"
Server: "Let me verify: Were you aiming at Player B's head? Was the
distance/angle valid? Did you have ammo? Was the fire rate
legal? Was the hit within lag compensation bounds?"
Server: "Confirmed. Applying headshot damage."
Common Cheat Prevention
Speed hacks: Server tracks expected position based on movement speed. If a player moves faster than possible, reject the movement.
void AMyCharacter::ServerMove_Implementation(FVector ClientPosition, float DeltaTime)
{
float MaxDistance = MaxSpeed * DeltaTime * 1.1f; // 10% tolerance
float ActualDistance = FVector::Distance(ServerPosition, ClientPosition);
if (ActualDistance > MaxDistance)
{
// Teleport hack detected — correct to server position
ClientCorrectPosition(ServerPosition);
FlagForReview(this, "Movement violation");
}
}
Aim hacks: Statistical detection. Track accuracy over time. Human players have variable accuracy; aimbots have unnaturally consistent accuracy or inhuman reaction times.
Wall hacks: Don't send enemy position data to clients when the enemy isn't visible. This requires server-side visibility checks.
bool ShouldReplicateActor(AActor* Actor, AActor* Viewer)
{
// Don't replicate actors that the viewer can't possibly see
if (!IsInFieldOfView(Actor, Viewer))
return false;
if (!HasLineOfSight(Actor, Viewer))
return false;
if (GetDistance(Actor, Viewer) > MaxRelevanceDistance)
return false;
return true;
}
Fire rate hacks: Server validates time between shots against weapon fire rate. Reject shots that arrive faster than the weapon allows.
Anti-Cheat Services
For indie games, integrating a third-party anti-cheat is often more practical than building your own:
- Easy Anti-Cheat (EAC): Free for UE5 games, integrated into the engine
- BattlEye: Industry standard, used by many competitive games
- Valve Anti-Cheat (VAC): For Steam-published games
Competitive Progression Psychology
The Climb Fantasy
Players play competitive games to feel like they're improving. Design for this:
- Clear visual progress: Rank badges, tier colors, progress bars
- Frequent micro-rewards: Division promotions every 3-5 wins
- Seasonal resets: Soft resets give everyone a fresh start (and re-engagement reason)
- Historical tracking: Show rank over time, peak rank, season summaries
Managing Frustration
Losing streaks are inevitable. Mitigate frustration:
- Loss streak protection: Reduce MMR loss after 3+ consecutive losses (prevents spiral)
- Match quality reporting: Let players report unfair matches (data for system improvement)
- Win-streak rewards: Bonus rating for consecutive wins (amplifies good feelings)
- Post-match stats: Show performance metrics beyond just W/L so players see improvement even in losses
Season Structure
Seasons provide fresh starts and re-engagement hooks:
Season length: 2-3 months
Soft reset: MMR = (CurrentMMR + DefaultMMR) / 2
Season rewards: Cosmetics based on peak rank
Pre-season: 1-2 weeks of unranked play (system calibration)
Each new season is an opportunity for lapsed players to return and climb again.
Competitive systems are deep, and the details matter. But the foundation is simple: measure skill accurately, create fair matches, prevent cheating, and give players a satisfying sense of progression. Get those four things right, and your competitive mode will be the engine that drives long-term player retention.