TilePlus Services

Overview

In the TilePlus system, Runtime-only Scriptable Objects are called Services or SRS, and are controlled by TpServicesManager.

Services are built on a base class called ScriptableRuntimeService, which handles automatically pre-registering the service with TpServiceManager prior to the first scene load.

It's easy to create your own, just inherit from ScriptableRuntimeService and add your own data or code. Be sure to call the base classes in OnEnable and OnDisable if you override them.

Ensure that you have something like this in your derived class:

[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
private static void InitOnLoad()
{
TpServiceManager.PreRegisterSrs(  nameof(YOUR_CLASS), Create);
}

The BeforeSceneLoad version of the attribute is REQUIRED. If omitted, the service won't be available until this specific service actually registers itself.

TpServiceManager internally keeps Services in two groups:

TpServiceManager has properties and methods to deal with services:

Properties

Methods

GetServiceOfType is quite simple:

GetRunningService<T> is also ONLY for use when in Play mode.

Finally, if you want to terminate a service, use:

TerminateServiceOfType<T>()

If the service is running this will Destroy the service. Normally, services persist throughout the lifetime of the application once they're loaded. In the Layout demo the Dialog Box seen when you first encounter a treasure chest is actually a Bundle loaded by the DialogBoxService and this service is terminated when the Dialog box close button is clicked.

Terminating a running service doesn't mean it's no longer available: you can just use GetServiceOfType to reload it.

Convenience Properties

Since the Messaging and Spawning services are commonly used, shortcut properties exist that just let you get the handle:

These use GetServiceOfType<T> and will auto-start the service if not already running.

Note that the TpTileTweener Service is available via a protected static (i.e., within the class or derived classes only) property in the TilePlusBase class (which all TilePlus tiles derive from).
This reflects the fact that this tweener is ONLY for use with TilePlus tiles.

Services as Singletons

Usually a service is a front-end for something that needs to be defined by an API and/or an Interface, and in that sense, they're by nature singletons at least from an external point of view. Internally a service may handle multiple instances of something else but that's mostly hidden from code that uses the service.

That's the general model for this simple, but efficient, service scheme.

The ScriptableRuntimeService class can actually have multiple instances: there's no static 'Instance' at all.

It's the implementation used in TpServiceManager that enforces only one instance of each service for two main reasons:

Other systems in TilePlus need multiple Scriptable Object instances. Specifically, the Layout System which creates multiple instances of TpZoneManager Scriptable Objects. Here, the different instances are differentiated by their names. It's quite a bit more complex although the details are largely hidden via the use of Monobehaviour Components for setting up parameters.

Hence, Services are forced to be singletons as an implementation detail for this particular type of dynamically loadable service.

The intent is to use Services for, well, Services and not global data storage or state.

The Tweener, Messaging, Spawning, TileFabLib, and PositionDb services are autonomous and don't depend on any external state aside from that being provided from internal Unity API interactions, and TpLib logging and 'Tilemap DB' methods. All their internal data are private and only accessible via methods or properties. The only significant dependency occurs when a Service requires an Update method invocation - that's minor and not a Service-to-Service dependency.

Creating services which have cross-dependencies is a bad idea and you will be plagued with bugs unless you are extremely careful.

Back to Singletons...

But there's nothing stopping you from using Services as (sort of) conventional singletons if you want to. See the LayoutSystem demo for an example.

More Grisly Details

The IScriptableService interface

IScriptable is an interface that lets a SRS specify how it wants to receive Update events (or not). If a service DOES NOT implement IScriptableService it inherits the default Interface properties which proudly exclaim: No Updates!

Not all Services need an Update event; for example, TpMessaging and TpSpawner. Some Services need an Update event; for example, TpTileTweener and TpTilePositionDb.

Services which require Update events must implement the following:

bool IScriptableService.WantsUpdate => true;
bool IScriptableService.ReadyForUpdate => true;
void IScriptableService.Update(float deltaTime){}

Note that these are EXPLICIT interface implementations and MUST be done this way.

Update Event Timing for Services

Note that Update timing isn't the same as Monobehaviour Update and doesn't originate from a GameObject in any scene.

Which variety of Update generation is controlled by a toggle in a TpLibInit asset. There's a preinstalled instance in your project at: Assets/TilePlus/Resources/TP/TpLibInit.


Revision #22
Created 20 June 2025 17:09:35 by Vonchor
Updated 5 July 2025 15:10:16 by Vonchor