{"id":34,"date":"2021-07-24T07:58:54","date_gmt":"2021-07-24T07:58:54","guid":{"rendered":"https:\/\/docs.sisus.co\/inity\/?p=34"},"modified":"2026-03-27T07:18:19","modified_gmt":"2026-03-27T07:18:19","slug":"service-attribute","status":"publish","type":"post","link":"https:\/\/docs.sisus.co\/init-args\/services\/service-attribute\/","title":{"rendered":"1. [Service] Attribute"},"content":{"rendered":"<h2><img loading=\"lazy\" class=\"alignnone size-full wp-image-480\" src=\"https:\/\/docs.sisus.co\/init-args\/wp-content\/uploads\/sites\/6\/2021\/07\/service-tag.png\" alt=\"\" width=\"360\" height=\"166\" srcset=\"https:\/\/docs.sisus.co\/init-args\/wp-content\/uploads\/sites\/6\/2021\/07\/service-tag.png 360w, https:\/\/docs.sisus.co\/init-args\/wp-content\/uploads\/sites\/6\/2021\/07\/service-tag-300x138.png 300w\" sizes=\"(max-width: 360px) 100vw, 360px\" \/><\/h2>\n<h2>Defining a Global Service<\/h2>\n<p>You can define a <a href=\"https:\/\/docs.sisus.co\/init-args\/reference\/global-services\/\">global service<\/a> in Init(args) by simply adding the [Service] attribute to any class:<\/p>\n<pre>[Service]\r\nclass GameManager { }<\/pre>\n<p>This will instruct Init(args) to automatically create and cache a single instance of the the class when the project starts.<\/p>\n<h2>Ready To Use Before Scene Load<\/h2>\n<p>All global services are constructed and initialized before any of the objects in the initial scene become active. As such, clients can acquire and make use of global services during the Awake and OnEnable events without having to worry about execution order issues.<\/p>\n<p>All global services are also automatically initialized in optimal order based on their dependencies, so you don&#8217;t have to worry about execution order issues in this case either.<\/p>\n<h2>MonoBehaviour&lt;T&#8230;&gt; Injection<\/h2>\n<p>A global service is automatically delivered to all components in the project that derive from MonoBehaviour&lt;T&#8230;&gt;, with one its generic type arguments matching the service type.<\/p>\n<pre>class Player : MonoBehaviour&lt;GameManager&gt;\r\n{\r\n     GameManager gameManager;\r\n     protected override void Init(GameManager gameManager) =&gt; this.gameManager = gameManager;\r\n\r\n     void OnEnable() =&gt; gameManager.RegisterPlayer(this);\r\n     void OnDisable() =&gt; gameManager.UnregisterPlayer(this);\r\n}<\/pre>\n<h2>Service-To-Service Injection<\/h2>\n<p>A global service is also automatically delivered to all other global services that implement <a href=\"https:\/\/docs.sisus.co\/init-args\/reference\/iinitializable\/\">IInitializable&lt;T&#8230;&gt;<\/a>, with one the generic type arguments matching the service type, or contain a constructor with a parameter type matching the service type.<\/p>\n<pre>[Service]\r\nclass InputManager : IInitializable&lt;GameManager&gt;\r\n{\r\n     public void Init(GameManager gameManager) =&gt; Debug.Log($\"InputManager received {gameManager}.\");\r\n}\r\n\r\n[Service]\r\nclass AudioManager\r\n{\r\n     public AudioManager(GameManager gameManager) =&gt; Debug.Log($\"AudioManager received {gameManager}.\");\r\n}<\/pre>\n<p>Note that this automatic delivery of services to other services only works if <em>all<\/em> Init method \/ constructor parameters are global service types.<\/p>\n<p>Also note that if you use constructors for receiving global services, you need to make sure not to have any circular dependencies between your services. If you have a service that requires another service in its constructor, and that other services also in turn requires the original services in its constructor, then neither object can be constructed:<\/p>\n<pre>[Service]\r\nclass InputManager\r\n{\r\n     public InputManager(GameManager gameManager) =&gt; Debug.Log($\"InputManager received {gameManager}.\");\r\n}\r\n\r\n[Service]\r\nclass GameManager\r\n{\r\n     public GameManager(InputManager inputManager) =&gt; Debug.Log($\"GameManager received {inputManager}.\");\r\n}<\/pre>\n<p>The same thing also applies even if the circular dependency consists of a longer chain of services. E.g. if A&#8217;s constructor requires B, B&#8217;s constructor requires C, and C&#8217;s constructor requires A, then none of the global services can be created.<\/p>\n<p>IInitializable&lt;T&#8230;&gt; does not have the same limitation; if A implements IInitializable&lt;B&gt;, and B implements IInitializable&lt;A&gt;, both global services can still be created.<\/p>\n<h2>Service Defining Type<\/h2>\n<p>When you register a class, let&#8217;s say InputManager, as a global service by adding the [Service] attribute on it, all clients that depend on an InputManager will be able to receive it automatically.<\/p>\n<p>Okay, but what if you don&#8217;t want to couple all your client components to this one specific concrete class? Maybe you want to use a different implementation in some contexts, such as when creating a build for particular platforms, or when testing a client in a unit test.<\/p>\n<p>A good way to achieve this, is by making your client components depend an interface type, instead of the concrete type of a specific service.<\/p>\n<pre>public interface IInputManager\r\n{\r\n     bool MoveLeft { get; }\r\n     bool MoveRight { get; }\r\n}\r\n\r\nclass Player : MonoBehaviour&lt;IInputManager&gt;\r\n{\r\n     protected override void Init(IInputManager inputManager) =&gt; Debug.Log($\"Player received {inputManager}.\");\r\n}<\/pre>\n<p>You can register a global service with a defining type other than the concrete type of the class, by specifying the type in the constructor of the [Service] attribute.<\/p>\n<pre>[Service(typeof(IInputManager))]\r\nclass InputManager : IInputManager\r\n{\r\n    public bool MoveLeft =&gt; Keyboard.current[KeyCode.LeftArrow].isPressed;\r\n    public bool MoveRight =&gt; Keyboard.current[KeyCode.RightArrow].isPressed;\r\n}<\/pre>\n<p>Note that the defining type of a service must satisfy one of the following constraints:<\/p>\n<ol>\n<li>Is the concrete type of the class.<\/li>\n<li>Is an interface that the class implements.<\/li>\n<li>Is a base type from which the concrete type derives.<\/li>\n<\/ol>\n<p>You can also register a service using more than one defining type, by listing multiple types in the [Service] attribute&#8217;s constructor.<\/p>\n<pre>[Service(typeof(InputManager), typeof(IInputManager))]\r\nclass InputManager : IInputManager<\/pre>\n<p>You can make use of <a href=\"https:\/\/docs.unity3d.com\/Manual\/platform-dependent-compilation.html\">preprocessor directives<\/a> to register a different class as a global service depending on the current platform.<\/p>\n<pre>#if UNITY_STANDALONE\r\n[Service(typeof(IInputManager))]\r\n#endif\r\nclass KeyboardInputManager : MonoBehaviour, IInputManager\r\n{\r\n    public bool MoveLeft =&gt; Keyboard.current[KeyCode.LeftArrow].isPressed;\r\n    public bool MoveRight =&gt; Keyboard.current[KeyCode.RightArrow].isPressed;\r\n}<\/pre>\n<pre>#if !UNITY_STANDALONE\r\n[Service(typeof(IInputManager))]\r\n#endif\r\nclass GamepadInputManager : MonoBehaviour : IInputManager\r\n{\r\n    public bool MoveLeft =&gt; Gamepad.current[GamepadButton.DpadLeft].isPressed;\r\n    public bool MoveRight =&gt; Gamepad.current[GamepadButton.DpadRight].isPressed;\r\n}<\/pre>\n<h2>Asset Services<\/h2>\n<p>Init(args) makes it easy to create global services from prefabs and scriptable object assets in your project.<\/p>\n<p>Two different methods of loading service assets are currently supported: Resources folders and Addressables.<\/p>\n<h3>Resources<\/h3>\n<p>To load a service from a <a href=\"https:\/\/docs.unity3d.com\/Manual\/UnderstandingPerformanceResourcesFolder.html\">Resources<\/a> folder, assign the name of the asset to the ResourcePath property on the [Service] attribute.<\/p>\n<pre>[Service(ResourcePath = \"Player\")]\r\nclass Player : MonoBehaviour { }<\/pre>\n<h3>Addressables<\/h3>\n<p>To load a service using the <a href=\"https:\/\/docs.unity3d.com\/Manual\/com.unity.addressables.html\">Addressable Asset System<\/a>, assign the addressable key of the asset to the AddressableKey property on the [Service] attribute.<\/p>\n<pre>[Service(AddressableKey = \"PlayerSettings\")]\r\nclass PlayerSettings : ScriptableObject { }<\/pre>\n<p>Note that you must have the <a href=\"https:\/\/docs.unity3d.com\/Packages\/com.unity.addressables@2.3\/manual\/installation-guide.html\">Addressables package installed<\/a> in your project in order to use this functionality.<\/p>\n<h2>Scene Services<\/h2>\n<p>It is also possible to use the [Service] attribute to register a global service from a component that is loaded as part of a scene.<\/p>\n<h3>FindFromScene<\/h3>\n<p>To locate a service from any of the active scenes, set <strong><em>FindFromScene<\/em><\/strong> property of the [Service] attribute to <em>true<\/em>.<\/p>\n<pre>[Service(FindFromScene = true)]\r\nclass UIManager : MonoBehaviour { }<\/pre>\n<p>This can be a viable option for registering services, if your game consists of only a single main scene, or if you always start your game from the same <a href=\"https:\/\/docs.sisus.co\/init-args\/advanced\/preload-scene\/\">Preload scene<\/a>.<\/p>\n<p>When <em>FindFromScene<\/em> is enabled, the <strong><em>LazyInit<\/em><\/strong> option is also always enabled by default. This can help make it easier to enter Play Mode with different scenes open in the Editor, without necessarily encountering a service initialization exception if the scene doesn&#8217;t contain your scene-based service, if no clients are requesting the service at the time either.<\/p>\n<h3>LoadScene<\/h3>\n<p>To locate a service from a particular scene, and automatically load said scene if needed, assign the name or build index of the scene to the <em>LoadScene<\/em>\u00a0property in the [Service] attribute.<\/p>\n<pre>[Service(LoadScene = \"Services\")]\r\nclass UIManager : MonoBehaviour { }<\/pre>\n<p>If the scene is not already loaded when the service is requested (which happens immediately when the game starts, unless LazyInit is set to true), then it will be automatically loaded additively, and then the service will be acquired from it. This can help with testing the game in the Editor, because you can enter Play Mode from any scene, and Init(args) can still automatically initialize your scene-based global services.<\/p>\n<p>Note that loading a scene at runtime in Unity is not instantaneous, but <a href=\"https:\/\/docs.unity3d.com\/6000.0\/Documentation\/ScriptReference\/SceneManagement.SceneManager.LoadScene.html\">only completes at the end of the frame<\/a>.<\/p>\n<p>However, if you assign to <em>LoadScene<\/em> the scene that is the first scene in the <a href=\"https:\/\/docs.unity3d.com\/Manual\/build-profile-scene-list.html\">Scene List in your Build Settings<\/a>, and don&#8217;t set LazyInit nor LoadAsync to true, then Init(args) will actually load said scene synchronously when you start entering Play Mode, and makes sure that objects in that scene are initialized before objects in any other scenes. This can help ensure consistency between builds and the editor, and be very useful if you want to use the <a href=\"https:\/\/docs.sisus.co\/init-args\/advanced\/preload-scene\/\">Preload Scene architectural pattern<\/a>.<\/p>\n<p>Additionally, all components whose class derives from <a href=\"https:\/\/docs.sisus.co\/init-args\/clients\/monobehaviour-t\/\">MonoBehaviour&lt;T&#8230;&gt;<\/a> support asynchronous initialization, which means that they can automatically wait until scenes containing services that they depend on finish loading.<\/p>\n<h3>Reloading Scenes<\/h3>\n<p>If you reload a scene containing a service registered using the [Service] attribute, the old instance will <em>not<\/em> get replaced with the new one in Init(args)&#8217;s service cache automatically.<\/p>\n<p>If you need your scene-based global service to support its scene being reloaded, you can either use a <strong><a href=\"https:\/\/docs.sisus.co\/init-args\/services\/service-tag\/\">Service Tag<\/a><\/strong> instead, or manually reregister the service using a component attached to the reloaded scene:<\/p>\n<article id=\"post-78\" class=\"clearfix post-78 post type-post status-publish format-standard hentry category-features\">\n<div class=\"clearfix post-78 post type-post status-publish format-standard hentry category-features\" role=\"article\">\n<pre>[InitOrder(Category.ServiceInitializer, Order.VeryEarly)]\r\nclass PlayerServiceRegisterer : MonoBehaviour\r\n{\r\n   [SerializeField] Player player;\r\n\r\n   void Awake() =&gt; Service.Set&lt;IPlayer&gt;(player);\r\n}<\/pre>\n<\/div>\n<\/article>\n<h2><span style=\"font-size: 24px; font-weight: bold;\">Lazy Init<\/span><\/h2>\n<p>If you set <em>LazyInit<\/em> to true in the [Service] attribute, then instead of the service being initialized the moment that the game starts, it will only be lazily initialized when the first client requires it.<\/p>\n<article id=\"post-78\" class=\"clearfix post-78 post type-post status-publish format-standard hentry category-features\">\n<div class=\"clearfix post-78 post type-post status-publish format-standard hentry category-features\" role=\"article\">\n<pre>[Service(FindFromScene = true, LazyInit = true)]\r\nclass UIManager : MonoBehaviour { }<\/pre>\n<p>If your game contains a large number of global services with heavy computation occurring during their initialization, then setting LazyInit to true on your services could also help reduce the initial loading time of your game, and instead spreading it happen over a longer period of time.<\/p>\n<\/div>\n<h2><span style=\"font-size: 24px; font-weight: bold;\">Load Async<\/span><\/h2>\n<p>If <em>LoadAsync<\/em> is set to true in the [Service] attribute, then the service will be initialized using asynchronous methods when that is supported.<\/p>\n<article id=\"post-78\" class=\"clearfix post-78 post type-post status-publish format-standard hentry category-features\">\n<div class=\"clearfix post-78 post type-post status-publish format-standard hentry category-features\" role=\"article\">\n<pre>[Service(AddressableKey = \"UI\", LoadAsync = true)]\r\nclass UI : MonoBehaviour { }<\/pre>\n<p>Using asynchronous initialization can help keep the main thread running more smoothly, with services being loaded over a long period of time in the background.<\/p>\n<p>Here are all the asynchronous initialization methods that Init(args) supports:<\/p>\n<ul>\n<li>When <em>LoadAsync<\/em> is combined with <em>AddressableKey<\/em>, then Addressables.LoadAssetAsync is used to load the asset.<\/li>\n<li>When <em>LoadAsync<\/em> is combined with <em>ResourcePath<\/em>, then Resources.LoadAsync is used to load the asset.<\/li>\n<li>When <em>LoadAsync<\/em> is combined with a prefab asset loaded using <em>AddressableKey<\/em> or <em>ResourcePath<\/em>, Object.InstantiateAsync is used to instantiate the service.<\/li>\n<li>When <em>LoadAsync<\/em> is combined with a non-prefab asset loaded using <em>AddressableKey<\/em> or <em>ResourcePath<\/em>, and <em>Instantiate<\/em> is set to true, Object.InstantiateAsync is used to instantiate the service.<\/li>\n<\/ul>\n<h2><span style=\"font-size: 24px; font-weight: bold;\">Instantiate<\/span><\/h2>\n<p>When you register an asset-based service using <em>AddressableKey<\/em> or ResourcePath, Init(args) will instantiate a clone of the asset if it is a prefab, and register the asset directly if it is not a prefab (e.g. a scriptable object asset).<\/p>\n<p>If you want to deliver a prefab asset to clients directly, instead of a clone, you can set <em>Instantiate<\/em> to false.<\/p>\n<p>Similarly, if you want to deliver a clone of a non-prefab asset to clients, instead of the asset itself, you can set <em>Instantiate<\/em> to true.<\/p>\n<article id=\"post-78\" class=\"clearfix post-78 post type-post status-publish format-standard hentry category-features\">\n<div class=\"clearfix post-78 post type-post status-publish format-standard hentry category-features\" role=\"article\">\n<pre>[Service(ResourcePath = nameof(PlayerSettings), Instantiate = true)]\r\nclass PlayerSettings : ScriptableObject { }\r\n\r\n[Service(ResourcePath = \"PlayerPrefab\", Instantiate = false)]\r\nclass Player : MonoBehaviour { }<\/pre>\n<\/div>\n<\/article>\n<p><span style=\"font-size: 26px; font-weight: bold;\">Service Providers<\/span><\/p>\n<\/div>\n<\/article>\n<\/article>\n<p>It is also possible to add the [Service] attribute to a <a href=\"https:\/\/docs.sisus.co\/init-args\/reference\/ivalueprovider\/\">value provider<\/a> that provides a service, instead of the class of the service itself.<\/p>\n<p>This opens up the possibility to implement the value provider in such a way that it returns a different service every time that one is requested, rather than always returning the same globally shared instance:<\/p>\n<pre>[Service(typeof(Name))]\r\nclass RandomNameProvider : IValueProvider&lt;Name&gt;\r\n{\r\n   public Name Value =&gt; Random.Range(0, 10) switch\r\n   {\r\n      0 =&gt; \"Alice\",\r\n      1 =&gt; \"Bob\",\r\n      2 =&gt; \"Charlie\",\r\n      3 =&gt; \"David\",\r\n      4 =&gt; \"Eve\",\r\n      5 =&gt; \"Frank\",\r\n      6 =&gt; \"Grace\",\r\n      7 =&gt; \"Hank\",\r\n      8 =&gt; \"Ivy\",\r\n      _ =&gt; \"Jack\"\r\n   };\r\n}<\/pre>\n<p>If you implement the <em>IValueProvider&lt;TValue&gt;<\/em>.<em>TryGetFor<\/em> method, you can also make the value provider return different types of services based on the particular clients that are requesting them.<\/p>\n<p>For example, the example value provider below returns instances of different Visuals prefabs based on the id of the client that is requesting it:<\/p>\n<pre>[Service(typeof(Visuals), ResourcePath = \"VisualsProvider\"), CreateAssetMenu]\r\nsealed class VisualsProvider : ScriptableObject, IValueProvider&lt;Visuals&gt;, ISerializationCallbackReceiver\r\n{\r\n  \u00a0[Serializable]\r\n  \u00a0sealed class Entry\r\n  \u00a0{\r\n  \u00a0 \u00a0 public EntityId id = default;\r\n  \u00a0 \u00a0 public Visuals visuals = default;\r\n  \u00a0}\r\n\r\n  \u00a0[SerializeField] Entry[] entries = default;\r\n\r\n  \u00a0readonly Dictionary&lt;EntityId, Visuals&gt; visualsById = new();\r\n\r\n  \u00a0Visuals IValueProvider&lt;Visuals&gt;.Value =&gt; null;\r\n\r\n  \u00a0public bool TryGetFor(Component client, out Visuals visuals)\r\n  \u00a0{\r\n  \u00a0 \u00a0 if(client.TryGetComponent(out Entity entity) &amp;&amp; visualsById.TryGetValue(entity.Id, out var visualsPrefab))\r\n  \u00a0 \u00a0 {\r\n  \u00a0 \u00a0 \u00a0 \u00a0visuals = Instantiate(visualsPrefab, client.transform);\r\n  \u00a0 \u00a0 \u00a0 \u00a0return true;\r\n  \u00a0 \u00a0 }\r\n\r\n\u00a0 \u00a0 \u00a0 visuals = null;\r\n  \u00a0 \u00a0 return false;\r\n  \u00a0}\r\n\r\n\u00a0 \u00a0public void OnBeforeSerialize() { }\r\n\r\n\u00a0 \u00a0public void OnAfterDeserialize()\r\n  \u00a0{\r\n  \u00a0 \u00a0 visualsById.Clear();\r\n  \u00a0 \u00a0 foreach(var entry in entries)\r\n  \u00a0 \u00a0 {\r\n  \u00a0 \u00a0 \u00a0  visualsById[entry.id] = entry.visuals;\r\n  \u00a0 \u00a0 }\r\n  \u00a0}\r\n}<\/pre>\n<h1>Service Initializer<\/h1>\n<p>Service initializers can be used to register a global service, but instead of Init(args) automatically creating the service for you, you can handle its creation manually in code. This can be useful in situations where your services depend on some objects that are not registered as global services, or if the initialization process is so complicated that it can&#8217;t be automated.<\/p>\n<p>To create a service initializer:<\/p>\n<ol>\n<li>Define a type that derives from a <em>ServiceInitializer&lt;TService, &#8230;&gt; <\/em>base class or implements a <em>IServiceInitializer&lt;TService, &#8230;&gt;<\/em> interface.<\/li>\n<li>Add the [Service] attribute to the type.<\/li>\n<li>Specify the defining types of the global service as arguments of the <em>[Service]<\/em> attribute.<\/li>\n<\/ol>\n<p>The first generic type argument of the <em>IServiceInitializer&lt;&gt;<\/em> interface should be the concrete type of the service that the service initializer handles initializing.<\/p>\n<p>After the first argument you can list zero or more other global services that the initialized service depends on. These will get delivered to the <em>InitTarget<\/em> method automatically, and can be used to initialize the service.<\/p>\n<h2>Manual Initialization<\/h2>\n<pre>[Service(typeof(IPlayer), ResourcePath = \"PlayerInitializer\"), CreateAssetMenu]\r\nclass PlayerInitializer : ScriptableObject, IServiceInitializer&lt;Player, IInputManager&gt;\r\n{\r\n   [SerializeField] Player prefab;\r\n   [SerializeField] PlayerConfig config = default;\r\n   [SerializeField] Any&lt;string&gt; initialName = default; \/\/ Use Any&lt;string&gt; to support localization, randomization etc.\r\n\r\n   \/\/ This service initializer receives the global service IInputManager from Init(args),\r\n   \/\/ and uses it and additional configuration provided via the Inspector to initialize the Player service.\r\n   public Player InitTarget(IInputManager inputManager)\r\n   {\r\n      var player = prefab.Instantiate(config, inputManager);\r\n      prefab.name = initialName;\r\n      return player;\r\n}\r\n\r\nclass Player : MonoBehaviour&lt;PlayerConfig, IInputManager&gt;, IPlayer\r\n{\r\n   PlayerConfig config;\r\n   IInputManager inputManager;\r\n\r\n   protected override void Init(PlayerConfig config, IInputManager inputManager)\r\n   {\r\n      this.config = config;\r\n      this.inputManager = inputManager;\r\n   }\r\n}<\/pre>\n<p>Unlike value providers that have the [Service] attribute, service initializers are only used to create the service once when the game is first loading, and that same instance will be delivered to all clients that request it.<\/p>\n<h3>Automatic Initialization<\/h3>\n<p>If you derive from <em>ServiceInitializer&lt;TService&gt;<\/em>, and don&#8217;t override the InitTarget method with a custom implementation, then Init(args) will automatically create the service instance for you.<\/p>\n<pre>[Service(typeof(IPlayer))]\r\nclass PlayerInitializer : ServiceInitializer&lt;Player&gt; { }<\/pre>\n<p>This will only be possible if the target service is a component, scriptable object, has a <a href=\"https:\/\/learn.microsoft.com\/en-us\/dotnet\/csharp\/programming-guide\/classes-and-structs\/using-constructors\">parameterless constructor<\/a>, or has a constructor with every argument being a <a href=\"https:\/\/docs.sisus.co\/init-args\/reference\/global-services\/\">global service<\/a>.<\/p>\n<p>This has the same effect as adding the [Service] attribute directly to the service class, but it might be a better option in some situations:<\/p>\n<ol>\n<li>if you can&#8217;t add the attribute to the service class directly (for example, because it belongs to a third party package),<\/li>\n<li>if you would prefer to keep the service class decoupled from Init(args),<\/li>\n<li>or if you want to group all service initialization logic together inside a single file.<\/li>\n<\/ol>\n<h2 class=\"western\">Lifetime Events<\/h2>\n<article id=\"post-78\" class=\"clearfix post-78 post type-post status-publish format-standard hentry category-features\">\n<div class=\"clearfix post-78 post type-post status-publish format-standard hentry category-features\" role=\"article\">\n<div class=\"post_content\">\n<p>Global services can receive callbacks during select lifetime events, even if they don&#8217;t derive from MonoBehaviour, by implementing one of the following interfaces:<\/p>\n<ol>\n<li><b>IAwake\u00a0<\/b>\u2013 Awake is executed immediately following the initialization of the service instance. The Init functions of all services that implement an <a href=\"https:\/\/docs.sisus.co\/inity\/features\/interfaces\/\">IInitializable&lt;T&gt;<\/a> interface are always executed before any Awake functions are.<\/li>\n<li><b>I<\/b><b>O<\/b><b>nEnable<\/b> \u2013 OnEnable is executed following the initialization of the service instance. The Awake functions of all services that implement IAwake are always executed before any OnEnable functions are.<\/li>\n<li><b>IStart<\/b> \u2013 Start is executed following the initialization of the service instance. The OnEnable functions of all services that implement IOnEnable are always executed before any Start functions are.<\/li>\n<li><b>I<\/b><b>U<\/b><b>pdate<\/b>\u00a0\u2013\u00a0Receive callback during the Update event.<\/li>\n<li><b>I<\/b><b>F<\/b><b>ixedUpdate<\/b>\u00a0\u2013\u00a0Receive callback during the FixedUpdate\u00a0event.<\/li>\n<li><b>I<\/b><b>L<\/b><b>ateUpdate<\/b>\u00a0\u2013\u00a0Receive callback during the LateUpdate\u00a0event.<\/li>\n<li><strong>IOnDisable <\/strong>&#8211; Receive callback when the application is quitting or when exiting play mode in the editor.<\/li>\n<li><strong>IOnDestroy <\/strong>&#8211; Receive callback when the application is quitting or when exiting play mode in the editor.<\/li>\n<\/ol>\n<p>Additionally if your service class implements the <a href=\"https:\/\/docs.microsoft.com\/en-us\/dotnet\/api\/system.idisposable?view=net-6.0\">IDiposable<\/a> interface then IDispose will be called for it when the application is quitting or when exiting play mode in the editor.<\/p>\n<\/div>\n<\/div>\n<\/article>\n<article id=\"post-78\" class=\"clearfix post-78 post type-post status-publish format-standard hentry category-features\">\n<div class=\"clearfix post-78 post type-post status-publish format-standard hentry category-features\" role=\"article\">\n<h1>Registering Services Manually In Code<\/h1>\n<p>In addition to registering services automatically using the [Service] attribute, it is possible to register them manually in code as well.<\/p>\n<p>This might be useful for example if you want to dynamically swap your services with different ones based on some conditional logic.<br \/>\nFor example you could swap the KeyboardInputManager to GamepadInputManager when the user changes their input method in the settings.<\/p>\n<p>To manually register a <a href=\"https:\/\/docs.sisus.co\/init-args\/reference\/global-services\/\">global service<\/a> use the <em>Service.Set<\/em> method.<\/p>\n<p>To make sure that the service gets registered before the Awake method on any clients components or their initializers is executed, you can add the InitOrder attribute with Category set to ServiceInitializer to the component that does the registering.<\/p>\n<pre>[InitOrder(Category.ServiceInitializer)]\r\npublic class InputManager : MonoBehaviour, IInputManager\r\n{\r\n    void OnEnable() =&gt; Service.Set&lt;IInputManager&gt;(this);\r\n    void OnDisable() =&gt; Service.Unset&lt;IInputManager&gt;(this);\r\n}<\/pre>\n<\/div>\n<\/article>\n<p>To manually register a <a href=\"https:\/\/docs.sisus.co\/init-args\/reference\/local-services\/\">local service<\/a> limited to only some clients based on their location in the scene hierarchies, use the <em>Service.AddFor<\/em> method.<\/p>\n<pre>[InitOrder(Category.ServiceInitializer)]\r\npublic class Player : MonoBehaviour\r\n{\r\n    void Awake() =&gt; Service.AddFor(Clients.InChildren, this);\r\n    void OnDestroy() =&gt; Service.RemoveFrom(Clients.InChildren, this);\r\n}<\/pre>\n<p><span style=\"font-size: 26px; font-weight: bold;\">Using Services<\/span><\/p>\n<article id=\"post-78\" class=\"clearfix post-78 post type-post status-publish format-standard hentry category-features\">\n<div class=\"clearfix post-78 post type-post status-publish format-standard hentry category-features\" role=\"article\">\n<p>To automatically receive the InputManager service during initialization you can have your class derive from MonoBehaviour&lt;InputManager&gt;. It will then receive the InputManager object in its Init function, making it possible to assign it to a member variable.<\/p>\n<pre>public class Player : MonoBehaviour&lt;InputManager&gt;\r\n{\r\n    private InputManager inputManager;\r\n\r\n    protected override void Init(InputManager inputManager)\r\n    {\r\n        this.inputManager = inputManager;\r\n    }\r\n}<\/pre>\n<p>This means that the Player component could exist as part of a scene that is loaded or a prefab that is instantiated without any arguments being provided manually, and Init would still get called with the InputManager service.<\/p>\n<p>Note that the Init function will only get automatically called during initialization if all the Init arguments the client expects are services.<\/p>\n<p>If one or more arguments are not services, then you will need to manually provide all the arguments when initializing the client.<\/p>\n<p>For example, consider this client that requires one service object and one object that is not a service:<\/p>\n<pre>public class Player : MonoBehaviour&lt;InputManager, Camera&gt;\r\n{\r\n    private InputManager inputManager;\r\n    private Camera firstPersonCamera;\r\n\r\n    protected override void Init(InputManager inputManager, Camera firstPersonCamera)\r\n    {\r\n        this.inputManager = inputManager;\r\n        this.firstPersonCamera = firstPersonCamera;\r\n    }\r\n}<\/pre>\n<p>You can retrieve the cached instance of any service manually using Service.Get&lt;T&gt;() or Service.GetFor&lt;T&gt;(Object).<\/p>\n<pre>var inputManager = Service.Get&lt;InputManager&gt;();<\/pre>\n<p>Then you need to pass that instance to the client when it is being created.<\/p>\n<p>For example if you are creating the client by instantiating it from a prefab, you can use the Prefab.Instantiate function.<\/p>\n<pre>var inputManager = Service.Get&lt;InputManager&gt;();\r\nvar firstPersonCamera = Camera.main;\r\n\r\nvar playerPrefab = Resources.Load&lt;Player&gt;(\"Player\");\r\nplayerPrefab.Instantiate(inputManager, firstPersonCamera);<\/pre>\n<p>Or if the client is being built from scratch at runtime, you can use the GameObject.AddComponent function that accepts initialization arguments.<\/p>\n<pre>var playerGameObject = new GameObject(\"Player\");\r\nplayerGameObject.AddComponent(inputManager, firstPersonCamera);<\/pre>\n<p>If the client is a scene object, you can generate an <a href=\"https:\/\/docs.sisus.co\/init-args\/features\/initializer\/\">Initializer<\/a> to provide the arguments to the client.<br \/>\nWhen using Initializers all service arguments are passed to the client automatically, so you only need to assign the other arguments using the Inspector.<\/p>\n<div class=\"post_content\">\n<h1><span style=\"font-size: 26px; font-weight: bold;\">Reacting To Changing Services<\/span><\/h1>\n<\/div>\n<\/div>\n<\/article>\n<p>In cases where your services might change throughout the lifetime of the application, you may want to make sure clients of the services are always using the most recently registered services.<\/p>\n<p>To do this you can subscribe receive callbacks a service instance changes to a different, using the Service.AddInstanceChangedListener method.<\/p>\n<p>When you do this you should also remember to unsubscribe using Service.RemoveInstanceChangedListener when the listener gets destroyed.<\/p>\n<pre>public class Player : MonoBehaviour&lt;IInputManager&gt;\r\n{\r\n    private IInputManager inputManager;\r\n\r\n    protected override void Init(IInputManager inputManager)\r\n    {\r\n        this.inputManager = inputManager;\r\n    }\r\n\r\n    private void OnEnable()\r\n    {\r\n        Service.AddInstanceChangedListener(OnInputManagerInstanceChanged);\r\n    }\r\n\r\n   \u00a0private void OnDisable()\r\n    {\r\n        Service.RemoveInstanceChangedListener(OnInputManagerInstanceChanged);\r\n    }\r\n\r\n    private void OnInputManagerInstanceChanged(IInputManager newInstance)\r\n    {\r\n        inputManager = newInstance;\r\n    }\r\n}<\/pre>\n<h1>Best Practices<\/h1>\n<ul>\n<li>Consider using the <a href=\"https:\/\/docs.sisus.co\/init-args\/advanced\/preload-scene\/\">Preload Scene<\/a> pattern and <em>[Service(LoadScene = name)]<\/em> for scene-based global services, if you want make sure they are always initialized before any clients in other scenes.<\/li>\n<li>If a scene-based service does not exist for the entire lifetime of the application, use a <a href=\"https:\/\/docs.sisus.co\/init-args\/services\/service-tag\/\">Service Tag<\/a> or <a href=\"https:\/\/docs.sisus.co\/init-args\/services\/services-component\/\">Services component<\/a> to register it instead of a [Service] attribute.<\/li>\n<li>If you want to instantiate prefab-based services synchronously, <em>[Service(ResourcePath = path)]<\/em> can be used.<\/li>\n<li>Consider avoiding using <a href=\"https:\/\/docs.unity3d.com\/ScriptReference\/Object.DontDestroyOnLoad.html\">DontDestroyOnLoad<\/a> to convert scene objects into persistent global services &#8211; if the objects should hold any references to other objects in its original scene, or vice versa, those could easily break if the original scene is unloaded or reloaded at some point. For this reason it can be safer to simply never unload the scene(s) that contain global services as a whole, so that all objects in the scene are guaranteed to have the same lifetime.<\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>Defining a Global Service You can define a global service in Init(args) by simply adding the [Service] attribute to any class: [Service] class GameManager { } This will instruct Init(args) to automatically create and cache a single instance of the the class when the project starts. Ready To Use Before Scene Load All global services ..<\/p>\n<div class=\"clear-fix\"><\/div>\n<p><a href=\"https:\/\/docs.sisus.co\/init-args\/services\/service-attribute\/\" title=\"read more\">Read more<\/a><\/p>\n","protected":false},"author":3,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[11],"tags":[],"_links":{"self":[{"href":"https:\/\/docs.sisus.co\/init-args\/wp-json\/wp\/v2\/posts\/34"}],"collection":[{"href":"https:\/\/docs.sisus.co\/init-args\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/docs.sisus.co\/init-args\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/docs.sisus.co\/init-args\/wp-json\/wp\/v2\/users\/3"}],"replies":[{"embeddable":true,"href":"https:\/\/docs.sisus.co\/init-args\/wp-json\/wp\/v2\/comments?post=34"}],"version-history":[{"count":61,"href":"https:\/\/docs.sisus.co\/init-args\/wp-json\/wp\/v2\/posts\/34\/revisions"}],"predecessor-version":[{"id":1097,"href":"https:\/\/docs.sisus.co\/init-args\/wp-json\/wp\/v2\/posts\/34\/revisions\/1097"}],"wp:attachment":[{"href":"https:\/\/docs.sisus.co\/init-args\/wp-json\/wp\/v2\/media?parent=34"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/docs.sisus.co\/init-args\/wp-json\/wp\/v2\/categories?post=34"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/docs.sisus.co\/init-args\/wp-json\/wp\/v2\/tags?post=34"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}