UI System

Spark provides a panel-based UI framework for managing game windows (inventory, quest log, character sheet, settings, etc.) and a drag-and-drop system for moving items, abilities, and other objects between UI elements.

UI Panels

ISparkUIPanel Interface

Every UI panel in Spark implements ISparkUIPanel:

public interface ISparkUIPanel
{
    string PanelId { get; }
    string DisplayName { get; }
    bool IsVisible { get; }
    int Priority { get; }
    bool IsModal { get; }

    void Show();
    void Hide();
    void Toggle();
    void RefreshDisplay();

    event System.Action OnPanelShown;
    event System.Action OnPanelHidden;
}
Property
Description

PanelId

Unique identifier for this panel. Auto-generated from type name if not set.

DisplayName

Human-readable name for debugging and UI.

IsVisible

Current visibility state.

Priority

Display priority. Higher priority panels render on top.

IsModal

Whether this panel blocks interaction with panels behind it.

SparkUIPanelBase

The standard base class for UI panels. Inherit from this instead of implementing ISparkUIPanel directly:

SparkUIPanelBase handles:

  • CanvasGroup detection for show/hide transitions

  • Auto-registration with SparkUIPanelManager on Start()

  • Event publishing when shown/hidden

  • Panel ID generation from the type name

Override these virtual methods to add your logic:

Method
When It Runs

OnShowPanel()

After the panel becomes visible

OnHidePanel()

After the panel is hidden

OnRefreshDisplay()

When RefreshDisplay() is called

SparkUIPanelManager

The static manager tracks all registered panels and provides query and bulk operations:

Panel Events

The manager publishes events when panels open or close:

  • UIPanelShownEvent: A panel became visible.

  • UIPanelHiddenEvent: A panel was hidden.

  • FirstUIPanelOpenedEvent: The first panel opened (none were open before).

  • AllUIPanelsClosedEvent: All panels are now closed.

These events are useful for pausing gameplay, changing input modes, or updating other UI elements.

Drag and Drop System

Spark includes a complete drag-and-drop framework for moving data between UI slots (inventory items, ability bar slots, equipment slots, etc.).

Core Components

Class
Role

DragDropManager

Central registry for drag-drop handlers.

IDragDropHandler

Interface for handling drop operations.

DropSource

Component on a UI element that can be dragged from.

DropTarget

Component on a UI element that can be dropped onto.

DragDropCommand

Command that carries drop operation data.

DragDropCommandHandler

Default handler that routes to registered IDragDropHandler implementations.

IDragDropHandler

Implement this to define what happens when something is dropped:

Registering Handlers

Register your drag-drop handlers with the manager:

Multiple handlers can be registered. When a drop occurs, the manager finds the first handler where CanHandle() returns true.

DropSource and DropTarget

Add DropSource to any UI element that the player can drag from, and DropTarget to any element they can drop onto. These components handle the visual feedback and input, and emit DragDropCommand instances when a drop completes.

Debug Visualization

DragDropDebugUtility provides visual debugging for drag-and-drop operations during development. Enable it to see source/target highlighting and data flow in the editor.

Creating a Custom Panel

Here is a complete example of a custom UI panel:

Attach it to a Canvas GameObject with a CanvasGroup component. The base class uses the CanvasGroup's alpha and interactable properties to show and hide the panel.

Best Practices

  • Always inherit from SparkUIPanelBase rather than implementing ISparkUIPanel directly.

  • Use RefreshDisplay() to update panel contents. Don't update in Update() every frame.

  • Subscribe to relevant events to know when to refresh (e.g., subscribe to ItemAddedEvent for an inventory panel).

  • Use IsModal for panels that should block interaction (confirmations, settings).

  • Clean up subscriptions in OnHidePanel() or OnDestroy().

  • For drag-and-drop, make CanHandle() specific. The first matching handler wins.

Last updated

Was this helpful?