As we implement this pattern, we'll be creating multiple files in a specific order.
You will see errors in Unity and your IDE until all files are complete.
How to Handle Development Errors
Don't Ignore: Note the errors - they're telling you something
Don't Panic: These are expected until all files are created
Stay Calm: Follow the implementation order and errors will resolve
As Apollo 13's Fred Haise said: "Never Panic Early"
This is a critical skill for software development. Note problems, but stay focused on the implementation path.
๐ Real-World Analogy
Restaurant Waiter System
Think about dining at a restaurant:
Without Service Locator (Direct Access):
You walk into the kitchen to get your food
You go to the bar to pour your drink
You find the chef to place your order
You need to know where everything is
Chaos! ๐ต
With Service Locator (Waiter):
You tell the waiter what you want
Waiter knows where to get food, drinks, desserts
You don't need to know how the restaurant works
Waiter acts as intermediary between you and services
Organized! ๐
The Pattern: Service Locator is like a waiter - it knows where all services are and brings them to you
๐ Understanding Service Locator
Core Concept
Maintain a central registry of services and provide a mechanism to locate and access them globally.
Key Components:
Service Locator: Central registry (the waiter)
Service Contracts: Interfaces defining services (menu)
Service Providers: Concrete implementations (kitchen, bar)
Clients: Code that needs services (customers)
The Problem It Solves
You need global access to services (logging, analytics, ads) but don't want tight coupling or complex initialization throughout your codebase.
Create a new file: ServiceLocator.cs
Static class that manages service registration and retrieval.
using System;
using System.Collections.Generic;
publicstaticclass ServiceLocator
{
// Central registry: Type โ Instanceprivatestaticreadonly IDictionary Services =
new Dictionary();
// Register a servicepublicstaticvoid RegisterService(T service)
{
if (!Services.ContainsKey(typeof(T)))
{
Services[typeof(T)] = service;
}
else
{
throw newApplicationException(
"Service already registered");
}
}
๐ป ServiceLocator Class (2/2)
// Retrieve a servicepublicstatic T GetService()
{
try
{
return (T)Services[typeof(T)];
}
catch
{
throw newApplicationException(
"Requested service not found.");
}
}
}
Key Features:
Static class: No instantiation needed
Generic methods: Type-safe registration/retrieval
Dictionary: Type as key, service instance as value
Exception handling: Clear error messages
๐ป Service Contracts (Interfaces)
๐ File Structure Note - PRODUCTION CODE
Create separate interface files for best practices: Assets/Scripts/Services/Interfaces/ILoggerService.cs Assets/Scripts/Services/Interfaces/IAnalyticsService.cs Assets/Scripts/Services/Interfaces/IAdvertisement.cs โ ๏ธ These interface files go into your Unity project for Blade Racer.
// Logger service contractpublicinterface ILoggerService
{
voidLog(string message);
}
// Analytics service contractpublicinterface IAnalyticsService
{
voidSendEvent(string eventName);
}
// Advertisement service contractpublicinterface IAdvertisement
{
voidDisplayAd();
}
Why Interfaces? Clients depend on contracts, not implementations. Easy to swap or mock services!
๐ป Logger Service Provider
๐ File Structure Note - PRODUCTION CODE
Create a new file: Assets/Scripts/Services/Logger.cs
This is the concrete implementation of the ILoggerService interface. โ ๏ธ This code goes into your Unity project for Blade Racer.
Create a new file: Assets/Scripts/Services/Analytics.cs
This is the concrete implementation of the IAnalyticsService interface. โ ๏ธ This code goes into your Unity project for Blade Racer.
publicclass UnityAnalytics : IAnalyticsService
{
publicvoidSendEvent(string eventName)
{
// Unity Analytics
Unity.Analytics.Analytics.CustomEvent(eventName);
}
}
publicclass GoogleAnalytics : IAnalyticsService
{
publicvoidSendEvent(string eventName)
{
// Google Analytics for Games
FirebaseAnalytics.LogEvent(eventName);
}
}
Swap implementations: Easy to switch from Unity Analytics to Google Analytics!
๐ป Advertisement Service Provider
๐ File Structure Note - PRODUCTION CODE
Create a new file: Assets/Scripts/Services/Advertisement.cs
This is the concrete implementation of the IAdvertisement interface. โ ๏ธ This code goes into your Unity project for Blade Racer.
using UnityEngine;
publicclass Advertisement : IAdvertisement
{
publicvoidDisplayAd()
{
Debug.Log("Displaying video advertisement");
}
}
Real Ad Integration:
using UnityEngine.Advertisements;
publicclass UnityAds : IAdvertisement
{
privatestring gameId = "1234567";
privatestring placementId = "rewardedVideo";
publicvoidDisplayAd()
{
if (Advertisement.IsReady(placementId))
{
Advertisement.Show(placementId);
}
else
{
Debug.LogWarning("Ad not ready");
}
}
๐ป Client Code - Service Registration
๐ File Structure Note - TESTING CODE
Create a new file: Assets/Scripts/Testing/ClientServiceLocator.cs
This script tests your Service Locator implementation by registering and using services. โ ๏ธ This is temporary testing code - you can remove it after testing your implementation.
Important: Register services as early as possible (e.g., in GameManager's Awake)
๐ป Client Code - Using Services
voidOnGUI()
{
GUILayout.Label("Review output in the console:");
if (GUILayout.Button("Log Event"))
{
ILoggerService logger =
ServiceLocator.GetService();
logger.Log("Hello World!");
}
if (GUILayout.Button("Send Analytics"))
{
IAnalyticsService analytics =
ServiceLocator.GetService();
analytics.SendEvent("button_clicked");
}
if (GUILayout.Button("Display Advertisement"))
{
IAdvertisement advertisement =
ServiceLocator.GetService();
advertisement.DisplayAd();
}
}
}
๐ก Alternative: When you have multiple test scripts with overlapping GUI buttons,
consider using TestPanel.cs to combine all test controls into one draggable window.
See the Event Bus lecture for the unified TestPanel implementation.
๐ฎ Updating TestPanel for Service Locator
๐ Evolving TestPanel.cs
Add Service Locator keyboard shortcuts and section to your TestPanel.cs.
// Add to TestPanel fieldsprivate bool _serviceLocatorExpanded = true;
// Add to Update() - Service Locator shortcutsif (Input.GetKeyDown(KeyCode.L)) // L = Log
{
var logger = ServiceLocator.GetService<ILoggerService>();
logger?.Log("TestPanel log event");
}
if (Input.GetKeyDown(KeyCode.Y)) // Y = analYtics
{
var analytics = ServiceLocator.GetService<IAnalyticsService>();
analytics?.SendEvent("test_event");
}
if (Input.GetKeyDown(KeyCode.H)) // H = sHow ad
{
var ads = ServiceLocator.GetService<IAdvertisement>();
ads?.DisplayAd();
}
// Add DrawServiceLocatorSection() methodvoidDrawServiceLocatorSection()
{
GUI.backgroundColor = new Color(0.2f, 0.6f, 0.2f); // Dark Green
_serviceLocatorExpanded = GUILayout.Toggle(
_serviceLocatorExpanded, "โผ Service Locator", "button");
GUI.backgroundColor = Color.white;
if (_serviceLocatorExpanded)
{
GUILayout.BeginVertical("box");
if (GUILayout.Button("Log Event (L)"))
ServiceLocator.GetService<ILoggerService>()?.Log("Test");
if (GUILayout.Button("Send Analytics (Y)"))
ServiceLocator.GetService<IAnalyticsService>()?.SendEvent("test");
if (GUILayout.Button("Display Ad (H)"))
ServiceLocator.GetService<IAdvertisement>()?.DisplayAd();
GUILayout.EndVertical();
}
}
Keymap Update: Add to DrawKeymapWindow(): L = Log Event, Y = Send Analytics, H = Display Ad (sHow)
๐งช Testing the Implementation
Steps:
Create new Unity scene
Create empty GameObject named "ServiceLocatorTest"
Attach ClientServiceLocator script
Press Play
Click buttons to test each service
Observe console output
Expected Output:
Log Event: "Logged: Hello World!"
Send Analytics: "Sent: button_clicked"
Display Advertisement: "Displaying video advertisement"
Services registered once: Can be accessed from any script anywhere in your game!
publicstatic T TryGetService() where T : class
{
if (Services.TryGetValue(typeof(T), out object service))
{
return service as T;
}
returnnull;
}
// Usagevar logger = ServiceLocator.TryGetService();
if (logger != null)
{
logger.Log("Service found!");
}
๐ Service Availability Checking
publicstaticbool HasService()
{
return Services.ContainsKey(typeof(T));
}
// Usage in client codepublicclass PlayerController : MonoBehaviour
{
voidStart()
{
// Check before usingif (ServiceLocator.HasService())
{
var analytics = ServiceLocator.GetService();
analytics.SendEvent("player_spawned");
}
}
voidOnDestroy()
{
// Safe even if analytics not registeredvar analytics = ServiceLocator.TryGetService();
analytics?.SendEvent("player_destroyed");
}
}
Best Practice: Check service availability before use, especially for optional services
Still use Service Locator: Wrap Unity services for easier testing and flexibility
๐ซ Common Mistakes to Avoid
Using Service Locator for Everything:
โ Enemy, Player, HUD in Service Locator
โ Only truly global services
Not Handling Missing Services:
// BAD - crashes if not registeredvar logger = ServiceLocator.GetService();
// GOOD - handles gracefullyvar logger = ServiceLocator.TryGetService();
logger?.Log("Message");
Registering Too Late:
Services should be registered in Awake() of first scene
Use DontDestroyOnLoad() for service manager
No Interface - Using Concrete Classes:
// BAD - tight coupling
ServiceLocator.RegisterService(newLogger());
var logger = ServiceLocator.GetService();
// GOOD - loose coupling
ServiceLocator.RegisterService(newLogger());
var logger = ServiceLocator.GetService();
โก Performance Tips
Optimization Strategies:
Cache Service References:
// BAD - lookup every framevoidUpdate()
{
var logger = ServiceLocator.GetService();
logger.Log("Frame update");
}
// GOOD - cache in Start()private ILoggerService _logger;
voidStart()
{
_logger = ServiceLocator.GetService();
}
voidUpdate()
{
_logger.Log("Frame update");
}
Use Lazy Initialization for Heavy Services:
private IAnalyticsService _analytics;
public IAnalyticsService Analytics
{
get
{
if (_analytics == null)
_analytics = ServiceLocator.GetService();
return _analytics;
}
}
๐ Debugging Tips
Add Debug Logging:
publicstaticvoid RegisterService(T service)
{
string typeName = typeof(T).Name;
if (!Services.ContainsKey(typeof(T)))
{
Services[typeof(T)] = service;
Debug.Log($"[ServiceLocator] Registered: {typeName}");
}
else
{
Debug.LogError($"[ServiceLocator] Already registered: {typeName}");
throw newApplicationException("Service already registered");
}
}
publicstatic T GetService()
{
string typeName = typeof(T).Name;
if (Services.TryGetValue(typeof(T), out object service))
{
Debug.Log($"[ServiceLocator] Retrieved: {typeName}");
return (T)service;
}
Debug.LogError($"[ServiceLocator] Service not found: {typeName}");
throw newApplicationException("Requested service not found.");
}
Implement classic games (Pac-Man, Tetris, Space Invaders)
Contribute to open-source Unity projects
Game jams (Ludum Dare, Global Game Jam)
๐ฎ Final Project Ideas
Apply What You've Learned:
Racing Game:
Facade: Vehicle engine system
Object Pool: Particle effects, tire marks
Service Locator: Analytics, ads
Strategy: AI difficulty levels
Tower Defense:
Object Pool: Projectiles, enemies
Strategy: Different enemy behaviors
Decorator: Tower upgrades
Observer: Wave completion events
Spatial Partition: Enemy targeting
RPG:
Adapter: Save system (cloud + local)
Visitor: Item effects on player
Command: Skill system
Service Locator: Quest tracking, inventory
Puzzle Game:
Command: Undo/redo moves
Observer: Combo notifications
Service Locator: Leaderboards, achievements
๐ Lecture Summary
What We Learned Today:
โ Service Locator provides central registry for global services
โ Simple to implement and understand
โ Use for truly global services (logging, analytics, ads)
โ Don't use for scene-specific or gameplay components
โ Controversial pattern - some consider it anti-pattern
โ Dependency Injection is more robust alternative
โ Unity provides many built-in services
Course Completion:
๐ 15 lectures completed!
๐ฏ 10+ design patterns mastered
๐ Ready to build professional games
๐ช Strong foundation in game architecture
Remember: Patterns are tools, not rules. Use them when they solve real problems!
Gaming History Moment ๐น๏ธ
Battle Royale & Live Service Games (2017-Present)
In 2017, PlayerUnknown's Battlegrounds (PUBG) popularized the Battle Royale genre, followed by Epic's Fortnite in 2018. These games pioneered the "live service" model - games that continuously evolve with seasons, events, and updates. Behind the scenes, they require an ecosystem of constantly-running services: matchmaking, anti-cheat, analytics, progression tracking, item shops, and social features.
The challenge? Any component in the game might need to access these services: UI needs the shop, gameplay needs analytics, inventory needs progression tracking. Hard-coding dependencies would create a tangled mess. Instead, modern live service games use service locators - centralized registries where any system can find the service it needs without tight coupling. When Epic adds a new seasonal feature, they register it as a service, and existing code can discover it dynamically.
Connection to Service Locator Pattern
Live service games are Service Locator implementations at massive scale! Just like our ServiceLocator provides global access to ILogService, IAdsService, and IAnalyticsService, Fortnite's architecture provides global access to MatchmakingService, ProgressionService, ShopService, etc. Any gameplay code can call ServiceLocator.Get<IMatchmaking>() without knowing how matchmaking works. This loose coupling enables rapid feature iteration - Epic ships updates every week because services are decoupled and discoverable!