Overview
The UI System provides interfaces, base classes, and production-ready implementations for displaying relationships in your game. Build custom UI by implementing interfaces or extending optimized base classes.
View Interfaces
Core interfaces defining contracts for relationship UI components.
IRelationshipView
Core interface for any relationship display
IFactionView
Interface for faction displays
IRelationshipNotification
Interface for popup notifications
Base Classes
Abstract base classes that handle event subscription, caching, and common functionality.
RelationshipViewBase
Base class for relationship displays. Handles events, caching, and auto-updates.
- Auto-subscribes to RelationshipManager events
- Filters by entity pair and optional definition
- Caches GameObjects, Entity IDs, and Factions
- Dirty flag prevents redundant refreshes
- Abstract DisplayRelationship() method
FactionViewBase
Base class for faction displays. Tracks member changes automatically.
- Subscribes to faction member events
- Caches member count
- Tracks hierarchy changes
- Abstract UpdateFactionDisplay() method
// Extend RelationshipViewBase for custom UI
public class MyCustomView : RelationshipViewBase
{
[SerializeField] private TextMeshProUGUI m_ValueText;
[SerializeField] private Image m_FillBar;
// Called when relationship data changes
protected override void DisplayRelationship(RelationshipState state,
RelationshipSource source)
{
if (state == null) {
m_ValueText.text = "No Relationship";
m_FillBar.fillAmount = 0;
return;
}
m_ValueText.text = $"{state.CurrentValue:F0}";
m_FillBar.fillAmount = state.CurrentValue / 100f;
}
// Optional: Called specifically when value changes
protected override void UpdateValue(float newValue)
{
m_ValueText.text = $"{newValue:F0}";
m_FillBar.fillAmount = newValue / 100f;
}
}Simple Views
Ready-to-use UI components for quick implementation. Drop onto Canvas and configure.
SimpleRelationshipView
Bar DisplayFill bar with value and level display. Auto-updates on relationship changes.
- Progress bar fill
- Value text display
- Level name display
- Color coding by level
- Auto-update on changes
- Definition-scoped filtering
SimpleFactionView
Info PanelFaction icon, name, description, and member count.
- Faction icon display
- Member count
- Description text
- Hierarchy display
- Auto-update
SimpleRelationshipNotification
PopupAnimated popup for relationship changes with auto-hide.
- Fade in/out animations
- Value delta display (+/-)
- Level change announcements
- Entity name resolution
- Faction name resolution
- Frame-batching for rapid changes
Notifications
Use SimpleRelationshipNotification for popup feedback when relationships change.
Setup Steps
- Add SimpleRelationshipNotification to Canvas
- Configure animation timing (fade in/out duration)
- Set display duration and positioning
- Component auto-subscribes to RelationshipManager events
Frame Batching (v1.1)
When multiple relationship changes occur in the same frame (e.g., faction broadcasts), notifications are batched to show a single combined message instead of rapid flashes.
// Notification displays:
// "+15 Reputation with Merchant Guild"
// "Reached Level: Friendly"
// For faction-level relationships, entity names are resolved:
// "Town Guards → Bandits: -25"Definition-Scoped Views (v1.1)
Views can now filter by RelationshipDefinition, enabling multiple UI elements for different relationship dimensions between the same entity pair.
Use Case: Multi-Dimensional Relationships
Player ↔ Merchant can have separate Trust, Trade, and Respect dimensions. Each needs its own UI bar tracking only that definition.
// In SimpleRelationshipView Inspector:
// Entity A: Player
// Entity B: Merchant
// Definition: "Trust" (RelationshipDefinition asset)
// The view will ONLY show/update the Trust dimension,
// ignoring Trade and Respect changes for the same pair.How It Works
- m_Definition field filters which relationship to display
- Event handlers check
IsRelevantRelationship()before updating - Lookup uses definition-scoped key:
entityA + entityB + definitionId - Falls back to unscoped lookup if definition is null
Performance Optimizations (v1.1)
RelationshipViewBase includes several optimizations for smooth performance with many active views.
GameObject Caching
Cached references to EntityA/EntityB GameObjects
↑ Eliminates repeated PropertyGet evaluations
Entity ID Caching
Cached string IDs for fast comparison in event handlers
↑ Avoids string allocations during event filtering
Faction Lookup Caching
Cached Faction references with invalidation on entity change
↑ Reduces Registry lookups for faction-level relationships
Dirty Flag Pattern
m_NeedsRefresh flag prevents redundant LoadRelationship calls
↑ Only reloads when state actually changes
// Cache invalidation happens automatically when:
// - SetEntities() is called with new entities
// - OnEnable() initializes the view
// - Refresh() is explicitly called
// Example: Switching displayed relationship
view.SetEntities(newEntityA, newEntityB);
// All caches cleared, fresh lookup performedEvent Filtering
Views only process events for their tracked relationship. The filtering uses cached IDs for fast string comparison without allocations.
// Internal event handler (simplified)
protected virtual void OnRelationshipValueChanged(
string entityAId, string entityBId, float oldValue, float newValue)
{
// Fast check using cached IDs
if (!IsTrackedRelationship(entityAId, entityBId))
return; // Not our relationship, skip
// Also checks faction-level if entities don't match directly
if (!IsRelevantRelationship(entityAId, entityBId))
return;
UpdateValue(newValue);
}Custom Implementations
Create custom UI by implementing interfaces or extending base classes.
RelationshipViewBase for relationship displays to get event handling, caching, and filtering for free. Implement IRelationshipView directly only for completely custom solutions.Testing Custom Views
Use the built-in test helpers to verify your custom implementation:
// In Play Mode tests
var view = gameObject.AddComponent<MyCustomView>();
view.SetEntities(entityA, entityB);
// Trigger a relationship change
RelationshipManager.Instance.ModifyRelationship(
entityA.EntityId, entityB.EntityId, 25f);
// Verify your view updated
Assert.AreEqual(25f, view.DisplayedValue);