Notification System¶
Overview¶
The Notification Service provides a centralized and flexible system for managing and dispatching in-game notifications. It allows various game systems to send notifications and other systems to subscribe and react to them based on category or specific ID. This framework is designed to be robust, easy to use, and integrated with the LoL Engine's core services like the Event Manager.
It supports different notification priorities, categories, custom data payloads, and automatic dismissal, making it suitable for a wide range of use cases from simple system messages to complex gameplay alerts.
Quick Start¶
- Enable Enable Notification Service in your
ServiceConfigurationasset.ImprovedGameInitializerregisters the service and creates theNotificationHelperautomatically. - Send a notification from any MonoBehaviour via the extension method:
using LoLEngine.Core.Notifications.Extensions;
using LoLEngine.Core.Notifications.Service;
this.SendNotification("Level Up!", "You reached level 5!",
NotificationCategory.Gameplay, NotificationPriority.High);
- React by subscribing to a category or ID — see the Usage Examples below.
Core Components¶
INotificationService (Interfaces/INotificationService.cs)¶
The main interface defining the contract for the notification system. It outlines methods for sending, subscribing, managing, and configuring notifications.
NotificationService (Service/NotificationService.cs)¶
The concrete implementation of INotificationService. It handles the logic for:
- Storing subscribers (by category and ID).
- Managing active notifications.
- Maintaining a history of notifications.
- Dispatching notifications to subscribers.
- Integrating with IEventManager to broadcast global notification events.
- Handling notification categories and their enabled states.
- Scheduling auto-dismissal of notifications.
INotification (Interfaces/INotification.cs)¶
Interface representing a single notification. It defines properties like Id, Title, Message, Priority, Category, Data, Timestamp, Duration, and IsPersistent.
Notification (Data/Notification.cs)¶
The default concrete implementation of INotification. It provides constructors for creating new notifications and manages their data. Custom notification types should typically inherit from this class or implement INotification.
NotificationCategory (Providers/NotificationCategory.cs)¶
An enum defining different categories for notifications (e.g., UI, System, Gameplay, Level, Combat, Achievement, Error). This allows for broad-stroke subscription and filtering.
NotificationPriority (Providers/NotificationPriority.cs)¶
An enum defining the priority levels for notifications (e.g., Low, Normal, High, Critical). This can be used by UI systems to display notifications differently.
NotificationEvents (Events/NotificationEvents.cs)¶
A static class defining global game events related to notifications, which are broadcast via the IEventManager:
- NotificationSent: Fired when a notification is successfully sent.
- NotificationDismissed: Fired when a notification is manually dismissed.
- NotificationExpired: Fired when a notification's duration ends and it's auto-dismissed.
NotificationExtensions (Extensions/NotificationExtensions.cs)¶
Provides convenient extension methods for MonoBehaviours to easily send and subscribe/unsubscribe to notifications without needing to directly fetch the INotificationService instance every time.
NotificationHelper (Service/NotificationService.cs)¶
A simple MonoBehaviour singleton used by the NotificationService to run coroutines, specifically for auto-dismissing notifications after their specified duration.
Key Features¶
- Flexible Sending: Send notifications by providing full
INotificationobjects or by specifying individual parameters (title, message, category, etc.). - Targeted Subscriptions:
- Subscribe to all notifications of a specific
NotificationCategory. - Subscribe to notifications with a specific
Id(useful for unique, identifiable events).
- Subscribe to all notifications of a specific
- Active Notification Management:
- Retrieve a list of currently active (non-dismissed, non-expired) notifications.
- Clear active notifications, optionally filtered by category.
- Manually dismiss a specific notification by its ID.
- Notification History:
- Maintains a configurable history of recently sent notifications.
- Retrieve a portion of the history.
- Category Management:
- Enable or disable entire notification categories at runtime. Notifications sent to a disabled category will be ignored.
- Auto-Dismissal:
- Notifications with a
Duration > 0andIsPersistent = falsewill automatically be dismissed after their duration. This is handled via theNotificationHelpercoroutine.
- Notifications with a
- Custom Data Payloads: Attach arbitrary data to notifications using a
Dictionary<string, object>. - Event System Integration: Global events (
NotificationSent,NotificationDismissed,NotificationExpired) are broadcast, allowing any system to listen and react without direct subscription to theNotificationService. - Thread Safety Considerations: Uses
ConcurrentDictionaryfor subscriber and active notification collections, providing a degree of thread safety for dictionary operations. List modifications within these dictionaries are not inherently thread-safe if accessed from multiple threads simultaneously (primarily a concern if extending the system for multi-threaded send/subscribe). - Ease of Use: Extension methods simplify common operations for
MonoBehaviourscripts.
Setup and Registration¶
To use the NotificationService, it needs to be instantiated and registered with your ServiceLocator (or equivalent dependency injection system). The NotificationHelper also needs to be present in the scene.
Example Registration (in your bootstrapper, or via ImprovedGameInitializer):
Note: With ImprovedGameInitializer, the NotificationService is automatically registered when enabled in ServiceConfiguration. The NotificationHelper is also automatically created during initialization. You typically don't need to manually register the service.
// In ServiceConfiguration ScriptableObject
Enable Notification Service: [x]
For custom initialization or manual setup:
using LoLEngine.Core.Events.Interfaces;
using LoLEngine.Core.Notifications.Interfaces;
using LoLEngine.Core.Notifications.Service;
using LoLEngine.Core.ServiceManagement.Service;
using UnityEngine;
public class CustomNotificationSetup : MonoBehaviour
{
protected void RegisterNotificationSystem()
{
// Ensure IEventManager is already registered
var eventManager = ServiceLocator.Instance.Get<IEventManager>();
if (eventManager == null)
{
Debug.LogError("IEventManager not found. NotificationService requires it.");
return;
}
// Create and register notification service
var notificationService = new NotificationService(eventManager);
ServiceLocator.Instance.Register<INotificationService>(notificationService);
notificationService.Initialize();
// Create NotificationHelper for coroutines (auto-dismissal)
if (NotificationHelper.Instance == null)
{
GameObject helperObj = new GameObject("NotificationHelper");
helperObj.AddComponent<NotificationHelper>();
DontDestroyOnLoad(helperObj); // Persist across scenes
}
Debug.Log("NotificationService registered and NotificationHelper created.");
}
}
NotificationHelper GameObject is set up to persist (e.g., DontDestroyOnLoad) if your notifications or the service itself need to function across scene loads.
Usage Examples¶
Sending Notifications¶
From a MonoBehaviour (using extension methods):
using LoLEngine.Core.Notifications.Extensions;
using LoLEngine.Core.Notifications.Service;
using UnityEngine;
using System.Collections.Generic;
public class GameplayManager : MonoBehaviour
{
public void PlayerLeveledUp(int newLevel)
{
// Simple notification
this.SendNotification("Level Up!", $"You reached level {newLevel}!", NotificationCategory.Gameplay, NotificationPriority.High);
}
public void QuestCompleted(string questName)
{
// Notification with custom data and duration
var data = new Dictionary<string, object> { { "questId", questName }, { "rewardGold", 100 } };
this.SendNotification(
title: "Quest Complete",
message: $"You completed: {questName}",
category: NotificationCategory.Gameplay,
priority: NotificationPriority.Normal,
duration: 5f, // Auto-dismiss after 5 seconds
data: data
);
}
public void SendCustomNotificationObject()
{
// Using a pre-defined custom notification type (see Creating Custom Notification Types below)
// var customNotification = new MyCustomNotification(...);
// this.SendNotification(customNotification);
}
}
Directly via the service instance:
var notificationService = ServiceLocator.Instance.Get<INotificationService>();
notificationService.Send("System Alert", "Maintenance soon.", NotificationCategory.System, NotificationPriority.Critical);
Subscribing to Notifications¶
From a MonoBehaviour (using extension methods):
using LoLEngine.Core.Notifications.Extensions;
using LoLEngine.Core.Notifications.Interfaces;
using LoLEngine.Core.Notifications.Service;
using UnityEngine;
public class UIManager : MonoBehaviour
{
void Start()
{
// Subscribe to all Gameplay notifications
this.SubscribeToNotifications(NotificationCategory.Gameplay, HandleGameplayNotification);
// Subscribe to a specific system notification by ID (if known)
// var notificationService = ServiceLocator.Instance.Get<INotificationService>();
// notificationService.Subscribe("SpecificSystemEventID", HandleSpecificNotification);
}
private void HandleGameplayNotification(INotification notification)
{
Debug.Log($"[UI] Gameplay Notification: {notification.Title} - {notification.Message}");
// Update UI elements based on notification.Category, notification.Id, or notification.Data
if (notification.Data.TryGetValue("rewardGold", out object goldValue))
{
Debug.Log($"Player received {goldValue} gold!");
}
}
private void HandleSpecificNotification(INotification notification)
{
Debug.Log($"[UI] Specific Notification Received: {notification.Title}");
}
void OnDestroy()
{
// IMPORTANT: Always unsubscribe to prevent memory leaks and errors
this.UnsubscribeFromNotifications(NotificationCategory.Gameplay, HandleGameplayNotification);
// var notificationService = ServiceLocator.Instance.Get<INotificationService>();
// notificationService?.Unsubscribe("SpecificSystemEventID", HandleSpecificNotification);
}
}
Listening to Global Notification Events¶
Any system can listen to the global notification events broadcast by the IEventManager.
using LoLEngine.Core.Events.Interfaces;
using LoLEngine.Core.Events.Providers;
using LoLEngine.Core.Notifications.Events;
using UnityEngine;
public class GlobalNotificationLogger : MonoBehaviour,
IEventListener<NotificationEvents.NotificationSent>
{
void OnEnable() => this.EventStartListening<NotificationEvents.NotificationSent>();
void OnDisable() => this.EventStopListening<NotificationEvents.NotificationSent>();
public void OnGameEvent(NotificationEvents.NotificationSent gameEvent)
{
Debug.Log($"Global Event: Notification Sent - ID: {gameEvent.Notification.Id}, Title: {gameEvent.Notification.Title}");
}
}
Creating Custom Notification Types¶
You can create specialized notification classes by inheriting from Notification or implementing INotification directly. This is useful for strongly-typed data payloads.
Example PlayerDamageNotification.cs:
// In a new file, e.g., PlayerDamageNotification.cs
using LoLEngine.Core.Notifications.Data;
using LoLEngine.Core.Notifications.Service;
using System.Collections.Generic;
public class PlayerDamageNotification : Notification
{
public float DamageAmount { get; }
public string DamageSource { get; }
public PlayerDamageNotification(float damageAmount, string damageSource, bool criticalHit)
: base(
title: criticalHit ? "Critical Hit!" : "Damage Taken",
message: $"You took {damageAmount} damage from {damageSource}.",
category: NotificationCategory.Gameplay,
priority: criticalHit ? NotificationPriority.High : NotificationPriority.Normal,
duration: 2f // Show for 2 seconds
)
{
DamageAmount = damageAmount;
DamageSource = damageSource;
// Add to custom data payload
Data["damageAmount"] = damageAmount;
Data["damageSource"] = damageSource;
Data["isCritical"] = criticalHit;
}
}
Sending the custom notification:
// In some combat script
var damageNotification = new PlayerDamageNotification(25f, "Goblin Archer", false);
this.SendNotification(damageNotification); // Using MonoBehaviour extension
Advanced Usage & Extensibility¶
The NotificationSystem sample (see Samples~/NotificationSystem/README.md) provides examples of more advanced scenarios:
- UI Integration: Displaying popups, updating health bars from your UI controllers.
- Gameplay System Integration: Reacting to gameplay conditions, sending damage/power-up notifications.
- Level Flow Integration: Broadcasting level-wide conditions.
- Performance-Optimized Notification Queue: For scenarios with very high notification throughput.
- Configuration-Driven Service: Extending NotificationService to be configured by a ScriptableObject.
- Animated UI Displays: Creating more dynamic visual feedback for notifications.
- Editor Debug Tools: For testing and inspecting notifications during development.
Refer to that document for detailed code examples of these advanced patterns.
Best Practices¶
- Unsubscribe: Always unsubscribe from notifications in
OnDestroy(or when the listener is no longer needed) to prevent memory leaks and errors. - Use Categories and Priorities: Leverage categories and priorities to allow for flexible filtering and differentiated UI treatment.
- Specific IDs for Unique Events: Use the ID-based subscription for notifications that are unique and require a very specific handler.
- Custom Data for Context: Use the
Datadictionary to pass along any relevant contextual information with your notifications. - Global Events for Broad Impact: Use the
IEventManagerintegration if many unrelated systems need to be aware of notification lifecycle events without direct coupling. - Keep Payloads Lean: While flexible, avoid putting excessively large objects directly into the
Datadictionary if performance is critical. Consider passing IDs or references instead. NotificationHelperPersistence: EnsureNotificationHelper.Instance.gameObjectis configured withDontDestroyOnLoad(gameObject)if notifications need to auto-dismiss across scene loads.
Troubleshooting¶
Notifications are never received
- Confirm Enable Notification Service is on in ServiceConfiguration, and resolve via the interface: ServiceLocator.Instance.Get<INotificationService>().
- The target NotificationCategory may be disabled at runtime — notifications sent to a disabled category are ignored.
Auto-dismiss (duration) doesn't fire
- The NotificationHelper MonoBehaviour runs the dismissal coroutine. With ImprovedGameInitializer it is created automatically; in a custom bootstrap, ensure it exists and persists (DontDestroyOnLoad).
See Also¶
- Events —
NotificationSent/NotificationDismissed/NotificationExpiredevents - Service Locator — resolving
INotificationService - Getting Started
This Notification Service provides a powerful and adaptable foundation for handling various types of in-game communication.