Lecture 15: Service Locator Pattern - Managing Dependencies

๐Ÿ” Service Locator Pattern

Managing Dependencies with Service Locator

Game Programming - CSCI 3213

Spring 2026 - Lecture 15 (Final)

Oklahoma City University

๐Ÿ“š Learning Objectives

  • Understand the Service Locator design pattern
  • Learn to manage global dependencies efficiently
  • Implement service registration and retrieval
  • Create service contracts using interfaces
  • Recognize benefits and drawbacks of the pattern
  • Understand when to use Service Locator vs Dependency Injection
  • Integrate Unity services (Analytics, Ads, Logging)

๐ŸŒ 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.

Pattern Type: Architectural - manages application-wide concerns

๐Ÿ—๏ธ Service Locator Structure

Architecture:

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚          Client Code                โ”‚
โ”‚    (Requests services)              โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
               โ”‚ GetService()
               โ†“
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚      Service Locator                 โ”‚ โ† Central Registry
โ”‚    (Dictionary of services)          โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
               โ”‚ Returns instance
               โ†“
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚      Service Providers               โ”‚
โ”‚  - Logger (ILoggerService)           โ”‚
โ”‚  - Analytics (IAnalyticsService)     โ”‚
โ”‚  - Ads (IAdvertisement)              โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                    

Flow:

  1. Services register themselves on startup
  2. Client requests service by interface type
  3. Service Locator looks up and returns instance
  4. Client uses service through interface

โœ… Benefits of Service Locator

  • Simplicity:
    • Easy to understand and implement
    • No steep learning curve
    • Quick to get started
  • Decoupling:
    • Clients don't know about concrete implementations
    • Services can be swapped at runtime
    • Easy to mock for testing
  • Runtime Optimization:
    • Select best implementation based on context
    • Debug vs Release builds
    • Platform-specific services
  • Global Access:
    • Services accessible from anywhere
    • No need to pass dependencies through constructors

โš ๏ธ Drawbacks & The Anti-Pattern Debate

  • Hidden Dependencies (Black Boxing):
    • Class dependencies not visible in constructor
    • Hard to see what a class needs
    • Runtime errors instead of compile-time errors
  • Globally Dependent:
    • Can become a global dependency itself
    • Tight coupling to Service Locator
    • Hard to remove once widely used
  • Testing Challenges:
    • Need to set up locator before tests
    • Test isolation harder
    • Mock registration complexity
Controversial Pattern: Some experts consider it an anti-pattern. Use wisely and only for truly global services!

๐ŸŽฏ When to Use Service Locator

Use Service Locator For:

  • โœ… Truly global services (logging, analytics)
  • โœ… Services needed everywhere in codebase
  • โœ… Services that can be mocked/removed without breaking gameplay
  • โœ… Cross-cutting concerns (debugging, profiling)

DON'T Use Service Locator For:

  • โŒ Context-specific components (HUD, Player, Enemy)
  • โŒ Services only used in certain scenes
  • โŒ Dependencies that should be explicit
  • โŒ Core gameplay logic

Example Decision Tree:

Service Use Service Locator? Reason
Logger โœ… Yes Needed everywhere, non-gameplay
Analytics โœ… Yes Global tracking, removable
HUD โŒ No Scene-specific, not always loaded
PlayerController โŒ No Core gameplay, explicit dependency

๐Ÿ’ป ServiceLocator Class (1/2)

using System;
using System.Collections.Generic;

namespace Chapter.ServiceLocator
{
    public static class ServiceLocator
    {
        // Central registry: Type โ†’ Instance
        private static readonly IDictionary<Type, object> Services =
            new Dictionary<Type, object>();

        // Register a service
        public static void RegisterService<T>(T service)
        {
            if (!Services.ContainsKey(typeof(T)))
            {
                Services[typeof(T)] = service;
            }
            else
            {
                throw new ApplicationException(
                    "Service already registered");
            }
        }

๐Ÿ’ป ServiceLocator Class (2/2)

        // Retrieve a service
        public static T GetService<T>()
        {
            try
            {
                return (T)Services[typeof(T)];
            }
            catch
            {
                throw new ApplicationException(
                    "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)

namespace Chapter.ServiceLocator
{
    // Logger service contract
    public interface ILoggerService
    {
        void Log(string message);
    }

    // Analytics service contract
    public interface IAnalyticsService
    {
        void SendEvent(string eventName);
    }

    // Advertisement service contract
    public interface IAdvertisement
    {
        void DisplayAd();
    }
}
Why Interfaces? Clients depend on contracts, not implementations. Easy to swap or mock services!

๐Ÿ’ป Logger Service Provider

using UnityEngine;

namespace Chapter.ServiceLocator
{
    public class Logger : ILoggerService
    {
        public void Log(string message)
        {
            Debug.Log("Logged: " + message);
        }
    }
}

Production Logger Could Include:

  • File output to persistent data path
  • Log levels (Info, Warning, Error)
  • Timestamps and stack traces
  • Remote logging to server
  • Log rotation and size limits
// Enhanced logger example
public void Log(string message, LogLevel level = LogLevel.Info)
{
    string timestamp = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
    string logEntry = $"[{timestamp}] [{level}] {message}";

    Debug.Log(logEntry);
    WriteToFile(logEntry);

    if (level == LogLevel.Error)
        SendToAnalytics(logEntry);
}

๐Ÿ’ป Analytics Service Provider

using UnityEngine;

namespace Chapter.ServiceLocator
{
    public class Analytics : IAnalyticsService
    {
        public void SendEvent(string eventName)
        {
            Debug.Log("Sent: " + eventName);
        }
    }
}

Real Analytics Integration:

public class UnityAnalytics : IAnalyticsService
{
    public void SendEvent(string eventName)
    {
        // Unity Analytics
        Unity.Analytics.Analytics.CustomEvent(eventName);
    }
}

public class GoogleAnalytics : IAnalyticsService
{
    public void SendEvent(string eventName)
    {
        // Google Analytics for Games
        FirebaseAnalytics.LogEvent(eventName);
    }
}
Swap implementations: Easy to switch from Unity Analytics to Google Analytics!

๐Ÿ’ป Advertisement Service Provider

using UnityEngine;

namespace Chapter.ServiceLocator
{
    public class Advertisement : IAdvertisement
    {
        public void DisplayAd()
        {
            Debug.Log("Displaying video advertisement");
        }
    }
}

Real Ad Integration:

using UnityEngine.Advertisements;

public class UnityAds : IAdvertisement
{
    private string gameId = "1234567";
    private string placementId = "rewardedVideo";

    public void DisplayAd()
    {
        if (Advertisement.IsReady(placementId))
        {
            Advertisement.Show(placementId);
        }
        else
        {
            Debug.LogWarning("Ad not ready");
        }
    }
}

๐Ÿ’ป Client Code - Service Registration

using UnityEngine;

namespace Chapter.ServiceLocator
{
    public class ClientServiceLocator : MonoBehaviour
    {
        void Start()
        {
            RegisterServices();
        }

        private void RegisterServices()
        {
            // Register Logger
            ILoggerService logger = new Logger();
            ServiceLocator.RegisterService(logger);

            // Register Analytics
            IAnalyticsService analytics = new Analytics();
            ServiceLocator.RegisterService(analytics);

            // Register Advertisement
            IAdvertisement advertisement = new Advertisement();
            ServiceLocator.RegisterService(advertisement);
        }
Important: Register services as early as possible (e.g., in GameManager's Awake)

๐Ÿ’ป Client Code - Using Services

        void OnGUI()
        {
            GUILayout.Label("Review output in the console:");

            if (GUILayout.Button("Log Event"))
            {
                ILoggerService logger =
                    ServiceLocator.GetService<ILoggerService>();
                logger.Log("Hello World!");
            }

            if (GUILayout.Button("Send Analytics"))
            {
                IAnalyticsService analytics =
                    ServiceLocator.GetService<IAnalyticsService>();
                analytics.SendEvent("button_clicked");
            }

            if (GUILayout.Button("Display Advertisement"))
            {
                IAdvertisement advertisement =
                    ServiceLocator.GetService<IAdvertisement>();
                advertisement.DisplayAd();
            }
        }
    }
}

๐Ÿงช Testing the Implementation

Steps:

  1. Create new Unity scene
  2. Create empty GameObject named "ServiceLocatorTest"
  3. Attach ClientServiceLocator script
  4. Press Play
  5. Click buttons to test each service
  6. 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!

๐Ÿ”„ Runtime Service Swapping

Debug vs Release Builds:

private void RegisterServices()
{
    #if UNITY_EDITOR || DEBUG
        // Debug builds: verbose logging
        ILoggerService logger = new VerboseLogger();
    #else
        // Release builds: minimal logging
        ILoggerService logger = new MinimalLogger();
    #endif

    ServiceLocator.RegisterService(logger);

    #if UNITY_EDITOR
        // Don't send analytics during development
        IAnalyticsService analytics = new MockAnalytics();
    #else
        // Production: real analytics
        IAnalyticsService analytics = new UnityAnalytics();
    #endif

    ServiceLocator.RegisterService(analytics);
}
Benefit: No analytics noise during testing, but real tracking in production!

๐ŸŽฎ Platform-Specific Services

private void RegisterServices()
{
    IAdvertisement ads;

    #if UNITY_IOS
        ads = new iOSAds();
    #elif UNITY_ANDROID
        ads = new AndroidAds();
    #elif UNITY_WEBGL
        ads = new WebGLAds();
    #else
        ads = new MockAds(); // Editor/unknown platform
    #endif

    ServiceLocator.RegisterService(ads);

    // Platform-specific analytics
    #if UNITY_IOS || UNITY_ANDROID
        IAnalyticsService analytics = new MobileAnalytics();
    #else
        IAnalyticsService analytics = new DesktopAnalytics();
    #endif

    ServiceLocator.RegisterService(analytics);
}
Flexibility: Client code stays the same across all platforms!

๐Ÿ”ง Enhanced Service Locator Features

Add Unregister Functionality:

public static void UnregisterService<T>()
{
    if (Services.ContainsKey(typeof(T)))
    {
        Services.Remove(typeof(T));
    }
}

public static void UnregisterAllServices()
{
    Services.Clear();
}

Add Safe GetService (No Exception):

public static T TryGetService<T>() where T : class
{
    if (Services.TryGetValue(typeof(T), out object service))
    {
        return service as T;
    }
    return null;
}

// Usage
var logger = ServiceLocator.TryGetService<ILoggerService>();
if (logger != null)
{
    logger.Log("Service found!");
}

๐Ÿ” Service Availability Checking

public static bool HasService<T>()
{
    return Services.ContainsKey(typeof(T));
}

// Usage in client code
public class PlayerController : MonoBehaviour
{
    void Start()
    {
        // Check before using
        if (ServiceLocator.HasService<IAnalyticsService>())
        {
            var analytics = ServiceLocator.GetService<IAnalyticsService>();
            analytics.SendEvent("player_spawned");
        }
    }

    void OnDestroy()
    {
        // Safe even if analytics not registered
        var analytics = ServiceLocator.TryGetService<IAnalyticsService>();
        analytics?.SendEvent("player_destroyed");
    }
}
Best Practice: Check service availability before use, especially for optional services

๐ŸŽฎ GameManager Integration

public class GameManager : MonoBehaviour
{
    public static GameManager Instance { get; private set; }

    void Awake()
    {
        // Singleton pattern
        if (Instance != null && Instance != this)
        {
            Destroy(gameObject);
            return;
        }

        Instance = this;
        DontDestroyOnLoad(gameObject);

        // Register all services early
        RegisterServices();
    }

    private void RegisterServices()
    {
        ServiceLocator.RegisterService<ILoggerService>(
            new Logger());
        ServiceLocator.RegisterService<IAnalyticsService>(
            new UnityAnalytics());
        ServiceLocator.RegisterService<IAdvertisement>(
            new UnityAds());

        Debug.Log("All services registered");
    }
}

๐Ÿงช Mock Services for Unit Testing

// Mock logger that doesn't actually log
public class MockLogger : ILoggerService
{
    public List<string> LoggedMessages = new List<string>();

    public void Log(string message)
    {
        LoggedMessages.Add(message);
        // No Debug.Log - silent!
    }
}

// Unit test
[Test]
public void PlayerController_OnSpawn_LogsEvent()
{
    // Arrange
    var mockLogger = new MockLogger();
    ServiceLocator.RegisterService<ILoggerService>(mockLogger);

    var player = new GameObject().AddComponent<PlayerController>();

    // Act
    player.Spawn();

    // Assert
    Assert.AreEqual(1, mockLogger.LoggedMessages.Count);
    Assert.AreEqual("Player spawned", mockLogger.LoggedMessages[0]);

    // Cleanup
    ServiceLocator.UnregisterService<ILoggerService>();
}

๐ŸŽฎ Real-World Game Usage

Player Actions Tracked with Services:

public class PlayerController : MonoBehaviour
{
    private ILoggerService _logger;
    private IAnalyticsService _analytics;

    void Start()
    {
        _logger = ServiceLocator.GetService<ILoggerService>();
        _analytics = ServiceLocator.GetService<IAnalyticsService>();

        _logger.Log("Player controller initialized");
        _analytics.SendEvent("player_session_start");
    }

    public void CollectCoin()
    {
        _logger.Log("Coin collected");
        _analytics.SendEvent("coin_collected");
        // ... game logic
    }

    public void Die()
    {
        _logger.Log("Player died");
        _analytics.SendEvent("player_death");

        // Show rewarded ad?
        var ads = ServiceLocator.TryGetService<IAdvertisement>();
        ads?.DisplayAd();
    }
}

๐Ÿ”„ Alternative: Dependency Injection (DI)

What is Dependency Injection?

A technique where objects receive their dependencies from external sources rather than creating them.

Constructor Injection:

// Dependencies are explicit!
public class PlayerController
{
    private readonly ILoggerService _logger;
    private readonly IAnalyticsService _analytics;

    // Constructor injection - dependencies visible
    public PlayerController(
        ILoggerService logger,
        IAnalyticsService analytics)
    {
        _logger = logger;
        _analytics = analytics;
    }

    public void Die()
    {
        _logger.Log("Player died");
        _analytics.SendEvent("player_death");
    }
}
Benefit: Dependencies are explicit in the constructor - no hidden surprises!

โš–๏ธ DI vs Service Locator

Aspect Service Locator Dependency Injection
Dependencies Hidden in method calls Explicit in constructor
Learning Curve Easy to learn Steeper (especially with frameworks)
Compile-Time Safety Runtime errors Compile-time errors
Testing Need to set up locator Inject mocks directly
Flexibility Very flexible More structured
Recommendation: Start with Service Locator. Migrate to DI as project grows.

๐Ÿ”ง DI Frameworks for Unity

Popular DI Frameworks:

  • Extenject (Zenject):
    • Most popular Unity DI framework
    • Free on Unity Asset Store
    • Scene-based injection
    • https://github.com/modesttree/Extenject
  • VContainer:
    • Fast, lightweight DI framework
    • Designed for Unity performance
    • Better than Zenject for large projects
  • Microsoft.Extensions.DependencyInjection:
    • .NET standard DI container
    • Can be used in Unity
    • Good for cross-platform code
When to Use DI Framework: When tight coupling and hidden dependencies become a problem

๐Ÿ’ป Extenject (Zenject) Example

// Installer - registers services
public class GameInstaller : MonoInstaller
{
    public override void InstallBindings()
    {
        Container.Bind<ILoggerService>()
            .To<Logger>()
            .AsSingle();

        Container.Bind<IAnalyticsService>()
            .To<UnityAnalytics>()
            .AsSingle();
    }
}

// Usage - constructor injection
public class PlayerController : MonoBehaviour
{
    private ILoggerService _logger;
    private IAnalyticsService _analytics;

    // Zenject injects dependencies automatically
    [Inject]
    public void Construct(
        ILoggerService logger,
        IAnalyticsService analytics)
    {
        _logger = logger;
        _analytics = analytics;
    }
}

๐ŸŽฎ Unity Built-In Services

Unity Services (No Manual Implementation Needed):

  • Unity Analytics:
    • Free player behavior tracking
    • Dashboard with charts and funnels
    • https://docs.unity3d.com/Manual/UnityAnalytics.html
  • Unity Ads:
    • Video and banner ads
    • Rewarded ad support
    • Cross-promotion network
  • Unity Cloud Save:
    • Cross-device player data
    • Cloud backup
  • Unity IAP (In-App Purchases):
    • Cross-platform purchase handling
    • Receipt validation
  • Unity Multiplayer Services:
    • Matchmaking, lobbies, relay
Still use Service Locator: Wrap Unity services for easier testing and flexibility

๐Ÿšซ Common Mistakes to Avoid

  1. Using Service Locator for Everything:
    • โŒ Enemy, Player, HUD in Service Locator
    • โœ… Only truly global services
  2. Not Handling Missing Services:
    // BAD - crashes if not registered
    var logger = ServiceLocator.GetService<ILoggerService>();
    
    // GOOD - handles gracefully
    var logger = ServiceLocator.TryGetService<ILoggerService>();
    logger?.Log("Message");
  3. Registering Too Late:
    • Services should be registered in Awake() of first scene
    • Use DontDestroyOnLoad() for service manager
  4. No Interface - Using Concrete Classes:
    // BAD - tight coupling
    ServiceLocator.RegisterService(new Logger());
    var logger = ServiceLocator.GetService<Logger>();
    
    // GOOD - loose coupling
    ServiceLocator.RegisterService<ILoggerService>(new Logger());
    var logger = ServiceLocator.GetService<ILoggerService>();

โšก Performance Tips

Optimization Strategies:

  • Cache Service References:
    // BAD - lookup every frame
    void Update()
    {
        var logger = ServiceLocator.GetService<ILoggerService>();
        logger.Log("Frame update");
    }
    
    // GOOD - cache in Start()
    private ILoggerService _logger;
    
    void Start()
    {
        _logger = ServiceLocator.GetService<ILoggerService>();
    }
    
    void Update()
    {
        _logger.Log("Frame update");
    }
  • Use Lazy Initialization for Heavy Services:
    private IAnalyticsService _analytics;
    public IAnalyticsService Analytics
    {
        get
        {
            if (_analytics == null)
                _analytics = ServiceLocator.GetService<IAnalyticsService>();
            return _analytics;
        }
    }

๐Ÿ› Debugging Tips

Add Debug Logging:

public static void RegisterService<T>(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 new ApplicationException("Service already registered");
    }
}

public static T GetService<T>()
{
    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 new ApplicationException("Requested service not found.");
}

List All Registered Services:

public static void LogAllServices()
{
    Debug.Log("=== Registered Services ===");
    foreach (var kvp in Services)
    {
        Debug.Log($"{kvp.Key.Name}: {kvp.Value.GetType().Name}");
    }
}

๐ŸŽ“ Design Patterns We've Covered

Creational Patterns:

  • Singleton: Single global instance (GameManager)
  • Object Pool: Reusable objects (bullets, enemies)

Structural Patterns:

  • Adapter: Interface compatibility (inventory systems)
  • Facade: Simplified interface (bike engine)
  • Decorator: Dynamic behavior addition (weapon upgrades)

Behavioral Patterns:

  • Observer: Event notification
  • Strategy: Interchangeable algorithms (drone maneuvers)
  • Visitor: Operations on object structures (power-ups)
  • Command: Encapsulated requests

Architectural Patterns:

  • Service Locator: Dependency management โ† Today!
  • Spatial Partition: Collision optimization

๐ŸŽฏ Pattern Selection Guide

Problem Pattern
Need exactly one instance Singleton
Creating/destroying is expensive Object Pool
Incompatible interfaces Adapter
Complex subsystem needs simplification Facade
Add features at runtime Decorator
Notify multiple objects of changes Observer
Swap algorithms at runtime Strategy
Operations on object structures Visitor
Global service access Service Locator
Spatial queries (collision, neighbors) Spatial Partition

๐Ÿ”— Combining Patterns

Patterns Work Together!

Example: Audio System

  • Singleton: AudioManager has one instance
  • Object Pool: AudioSources pooled and reused
  • Facade: Simple Play(), Stop(), SetVolume() interface
  • Service Locator: Globally accessible via ServiceLocator.GetService<IAudioService>()

Example: Enemy System

  • Object Pool: Enemy GameObjects reused
  • Strategy: Different AI behaviors
  • Observer: Notify when enemy dies
  • Spatial Partition: Efficient enemy-player collision
Key Insight: Patterns are building blocks - combine them to solve complex problems!

๐Ÿ“ Best Practices

Service Locator Dos:

  • โœ… Use for truly global, cross-cutting services
  • โœ… Register services as early as possible
  • โœ… Use interfaces for service contracts
  • โœ… Cache service references in Start()
  • โœ… Provide mock implementations for testing
  • โœ… Handle missing services gracefully

Service Locator Don'ts:

  • โŒ Don't use for scene-specific components
  • โŒ Don't use for core gameplay dependencies
  • โŒ Don't hide critical dependencies
  • โŒ Don't register concrete classes directly
  • โŒ Don't look up services every frame

General Pattern Advice:

  • Understand the problem before applying a pattern
  • Don't over-engineer simple solutions
  • Refactor to patterns when complexity emerges
  • Patterns are guidelines, not rules

๐Ÿš€ Continuing Your Journey

Books to Read:

  • Game Programming Patterns by Robert Nystrom
    • Free online: http://gameprogrammingpatterns.com
  • Design Patterns by Gang of Four (GoF)
    • The original patterns bible
  • Head First Design Patterns
    • Beginner-friendly with great examples
  • Clean Code by Robert C. Martin
    • Writing maintainable code

Online Resources:

  • https://refactoring.guru - Interactive pattern guide
  • Unity Learn - Official Unity tutorials
  • Catlike Coding - Advanced Unity tutorials
  • GitHub - Study open-source game projects

Practice Projects:

  • 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!

๐ŸŽ“ Course Complete!

Congratulations! ๐ŸŽ‰

You've completed Game Programming CSCI 3213!

What You've Accomplished:

  • Built a 2D game engine from scratch with JavaScript
  • Mastered Unity fundamentals and C# programming
  • Implemented 10+ design patterns in game contexts
  • Created production-ready, scalable game systems
  • Learned best practices for game architecture

Your Journey Continues:

  • Keep building games - practice makes perfect
  • Study open-source game projects
  • Participate in game jams
  • Read "Game Programming Patterns" by Robert Nystrom
  • Never stop learning! ๐Ÿ“š

Thank you for an amazing semester!

Questions? Office Hours: Monday/Wednesday 2:00-4:00 PM
SSM 204A - bobby.reed@okcu.edu

๐ŸŽฎ Good luck with your game development journey! ๐Ÿš€