- Package:
com.gamelovers.uiservice - Unity: 6000.0+
- Dependencies (see
package.json)com.unity.addressables(2.6.0)com.cysharp.unitask(2.5.10)
This package provides a centralized UI management service that coordinates presenter load/open/close/unload, supports layering, UI sets, and multi-instance presenters, and integrates with Addressables + UniTask.
For user-facing docs, treat docs/README.md (and linked pages) as the primary documentation set. This file is for contributors/agents working on the package itself.
- Service core:
Runtime/UiService.cs(UiService : IUiServiceInit)- Owns configs, loaded presenter instances, visible list, and UI set configs.
- Creates a
DontDestroyOnLoadparent GameObject named"Ui"and attachesUiServiceMonoComponentfor resolution/orientation tracking. - Tracks presenters as instances:
Dictionary<Type, IList<UiInstance>>where eachUiInstancestores(Type, Address, UiPresenter). - Editor support:
UiService.CurrentServiceis an internal static reference used by editor windows to access the active service in play mode.
- Public API surface:
Runtime/IUiService.cs- Exposes lifecycle operations (load/open/close/unload) and readonly views:
VisiblePresenters : IReadOnlyList<UiInstanceId>UiSets : IReadOnlyDictionary<int, UiSetConfig>GetLoadedPresenters() : List<UiInstance>
- Note: multi-instance overloads (explicit
instanceAddress) exist onUiService(concrete type), not onIUiService.
- Exposes lifecycle operations (load/open/close/unload) and readonly views:
- Configuration:
Runtime/UiConfigs.cs(ScriptableObject)- Stores UI configs as
UiConfigs.UiConfigSerializable(address + layer + type name) and UI sets asUiSetConfigSerializablecontainingUiSetEntryitems. - Use the specialized subclasses (each has
CreateAssetMenu):AddressablesUiConfigs(default),ResourcesUiConfigs,PrefabRegistryUiConfigs(embedded prefab registry). - Note:
UiConfigsisabstractto prevent accidental direct usage—always use one of the specialized subclasses.
- Stores UI configs as
- UI Sets:
Runtime/UiSetConfig.csUiSetEntrystores:- presenter type as
AssemblyQualifiedNamestring - optional
InstanceAddress(empty string means default instance)
- presenter type as
UiSetConfigis the runtime shape:SetId+UiInstanceId[].
- Presenter pattern:
Runtime/UiPresenter.cs- Lifecycle hooks:
OnInitialized,OnOpened,OnClosed,OnOpenTransitionCompleted,OnCloseTransitionCompleted. - Typed presenters:
UiPresenter<T>with aDataproperty that triggersOnSetData()on assignment (works duringOpenUiAsync(..., initialData, ...)or later updates). - Presenter features are discovered via
GetComponents(_features)at init time and are notified in the open/close lifecycle. - Transition tasks:
OpenTransitionTaskandCloseTransitionTaskare publicUniTaskproperties that complete when all transition features finish. - Visibility control:
UiPresenteris the single point of responsibility forSetActive(false)on close; it waits for allITransitionFeaturetasks before hiding.
- Lifecycle hooks:
- Composable features:
Runtime/Features/*PresenterFeatureBaseallows attaching components to a presenter prefab to hook lifecycle.ITransitionFeatureinterface for features that provide open/close transition delays (presenter awaits these).- Built-in transition features:
TimeDelayFeature,AnimationDelayFeature. - UI Toolkit support:
UiToolkitPresenterFeature(viaUIDocument) providesAddVisualTreeAttachedListener(callback)for safe element queries. Callback is invoked on each open because UI Toolkit recreates elements when the presenter is deactivated/reactivated.
- Helper views:
Runtime/Views/*(GameLovers.UiService.Views)SafeAreaHelperView: adjusts anchors/size based on safe area (notches).NonDrawingView: raycast target without rendering (extendsGraphic).AdjustScreenSizeFitterView: layout fitter that clamps between min/flexible size.InteractableTextView: TMP link click handling.
- Asset loading:
Runtime/Loaders/IUiAssetLoader.csIUiAssetLoaderabstraction with multiple implementations underRuntime/Loaders/:AddressablesUiAssetLoader(default): usesAddressables.InstantiateAsyncandAddressables.ReleaseInstance.PrefabRegistryUiAssetLoader: uses direct prefab references (useful for samples/testing). Can be initialized with aPrefabRegistryUiConfigsin its constructor.ResourcesUiAssetLoader: usesResources.Load.
- Supports optional synchronous instantiation via
UiConfig.LoadSynchronously(in Addressables loader).
- Docs (user-facing):
docs/docs/README.md— documentation entry point.
- Runtime:
Runtime/- Entry points:
IUiService.cs,UiService.cs,UiPresenter.cs,UiConfigs.cs,UiSetConfig.cs,UiInstanceId.cs. - Integrations / extension points (start here when behavior differs from expectations):
Loaders/*— how presenter prefabs are instantiated / released.- If UI fails to load/unload, start at
Loaders/IUiAssetLoader.csand the active loader (AddressablesUiAssetLoader,ResourcesUiAssetLoader,PrefabRegistryUiAssetLoader). - Loader choice is typically driven by which
UiConfigssubclass you use (AddressablesUiConfigs/ResourcesUiConfigs/PrefabRegistryUiConfigs).
- If UI fails to load/unload, start at
Features/*— presenter composition (components attached to presenter prefabs).- Lifecycle hooks live in
PresenterFeatureBase; features are discovered during presenter initialization. - Transition timing issues (UI not showing/hiding when expected) usually involve
ITransitionFeatureimplementations (egTimeDelayFeature,AnimationDelayFeature). - UI Toolkit presenters rely on
UiToolkitPresenterFeature; avoid queryingUIDocument.rootVisualElementduringOnInitialized()—useAddVisualTreeAttachedListener(...).
- Lifecycle hooks live in
Views/*— optional helper components used by presenter prefabs (safe area, raycasts, layout fitters, TMP link clicks).- If interaction/layout is off but service bookkeeping looks correct, look here before changing
UiService.
- If interaction/layout is off but service bookkeeping looks correct, look here before changing
- Entry points:
- Editor:
Editor/(assembly:Editor/GameLovers.UiService.Editor.asmdef)- Config editors:
UiConfigsEditorBase.cs,*UiConfigsEditor.cs,DefaultUiConfigsEditor.cs. - Debugging:
UiPresenterManagerWindow.cs,UiPresenterEditor.cs.
- Config editors:
- Samples:
Samples~/- Demonstrates basic flows, data presenters, delay features, UI Toolkit integration.
- Tests:
Tests/Tests/EditMode/*— unit tests (configs, sets, loaders, core service behavior)Tests/PlayMode/*— integration/performance/smoke tests
- Instance address normalization
UiInstanceIdnormalizesnull/""tostring.Empty.- Prefer
string.Emptyas the default/singleton instance identifier.
- Ambiguous “default instance” calls
UiServiceuses an internalResolveInstanceAddress(type)when an API is called without an explicitinstanceAddress.- If multiple instances exist, it logs a warning and selects the first instance. For multi-instance usage, prefer calling
UiServiceoverloads that includeinstanceAddress.
- Presenter self-close + destroy with multi-instance
UiPresenter.Close(destroy: true)now correctly uses the presenter's storedInstanceAddressto unload the correct instance.- This works seamlessly for both singleton and multi-instance presenters.
- Layering
UiServiceenforces sorting by settingCanvas.sortingOrderorUIDocument.sortingOrderto the config layer when adding/loading.- Loaded presenters are instantiated under the
"Ui"root directly (no per-layer container GameObjects).
- UI Sets store types, not addresses
- UI sets are serialized as
UiSetEntry(type name + instance address). The default editor populatesInstanceAddresswith the addressable address for uniqueness.
- UI sets are serialized as
LoadSynchronouslypersistenceUiConfig.LoadSynchronouslyexists and is respected byAddressablesUiAssetLoader.- However:
UiConfigs.UiConfigSerializablecurrently does not serializeLoadSynchronously, so configs loaded from aUiConfigsasset will produceLoadSynchronously = falseinUiConfigs.Configs.
- Static events
UiService.OnResolutionChanged/UiService.OnOrientationChangedare staticUnityEvents raised byUiServiceMonoComponent.- The service does not clear listeners; consumers must unsubscribe appropriately.
- Disposal
UiService.Dispose()closes all visible UI, attempts to unload all loaded instances, clears collections, and destroys the"Ui"root GameObject.
- Editor debugging tools
- Some editor windows toggle
presenter.gameObject.SetActive(...)directly for convenience; this may not reflect inIUiService.VisiblePresenterssince it bypassesUiServicebookkeeping.
- Some editor windows toggle
- UI Toolkit visual tree timing and element recreation
UIDocument.rootVisualElementmay not be ready whenOnInitialized()is called on a presenter.- UI Toolkit recreates visual elements when the presenter GameObject is deactivated/reactivated (close/reopen cycle),
AddVisualTreeAttachedListener(callback)invokes on each open to handle element recreation.
- C#: C# 9.0 syntax; no global
usings; keep explicit namespaces. - Assemblies
- Runtime code should avoid
UnityEditorreferences; editor-only tooling belongs underEditor/andGameLovers.UiService.Editor.asmdef. - If you must add editor-only code near runtime types, guard it with
#if UNITY_EDITORand keep it minimal.
- Runtime code should avoid
- Async
- Use
UniTask; threadCancellationTokenthrough async APIs where available.
- Use
- Memory / allocations
- Avoid per-frame allocations; keep API properties allocation-free (see
UiServiceread-only wrappers forVisiblePresentersandUiSets).
- Avoid per-frame allocations; keep API properties allocation-free (see
When you need third-party source/docs, prefer the locally-cached UPM packages:
- Addressables:
Library/PackageCache/com.unity.addressables@*/ - UniTask:
Library/PackageCache/com.cysharp.unitask@*/
- Add a new presenter
- Create a prefab with a component deriving
UiPresenter(orUiPresenter<T>). - Ensure it has a
CanvasorUIDocumentif you want layer sorting to apply. - Mark the prefab Addressable and set its address.
- Add/update the entry in
UiConfigs(menu:Tools/UI Service/Select UiConfigs).
- Create a prefab with a component deriving
- Add / update UI sets
- The default
UiConfigsinspector usesDefaultUiSetId(out-of-the-box). - To customize set ids, create your own enum and your own
[CustomEditor(typeof(UiConfigs))] : UiConfigsEditor<TEnum>.
- The default
- Add multi-instance flows
- Use
UiInstanceId(default =string.Empty) when you need to track instances externally. - Presenters know their own instance address via the internal
InstanceAddressproperty;Close(destroy: true)unloads the correct instance.
- Use
- Add a presenter feature
- Extend
PresenterFeatureBaseand attach it to the presenter prefab. - Features are discovered via
GetComponentsat init time and notified during open/close. - For features with transitions (animations, delays): implement
ITransitionFeatureso the presenter can await yourOpenTransitionTask/CloseTransitionTask.
- Extend
- Change loading strategy
- Prefer using one of the built-in loaders (
AddressablesUiAssetLoader,PrefabRegistryUiAssetLoader,ResourcesUiAssetLoader) or extendingIUiAssetLoaderfor custom needs.
- Prefer using one of the built-in loaders (
- Update docs/samples
- User-facing docs live in
docs/and should be updated when behavior/API changes. - If you add a new core capability, consider adding/adjusting a sample under
Samples~/.
- User-facing docs live in
Update this file when:
- Public API changes (
IUiService,IUiServiceInit, presenter lifecycle, config formats) - Core runtime systems/features are introduced/removed (features, views, multi-instance)
- Editor tooling changes how configs or sets are generated/serialized