Using base class as the "Service Locator", and caching the Transform

I wanted to share the main design pattern I decided to adapt for all my scripts.

Essentially, every script I create inherits from a “GameScript” class.
This GameScript does three things:

  1. It caches the Transform
  2. Provides easy manner to get and set transform properties (position, rotation)
  3. Acts as a service locator for any classes which inherit it.
//All managers are serialized here in Main class.
public class Main
{
  public static Main mainInstance;

   [SerializeField] private InventoryManager inventoryManager;
   [SerializeField] private PlayerController playerController;

   public InventoryManager GetInventoryManager()
   {
       return inventoryManager;
    }
}

//Base class for all scripts
public abstract class GameScript : MonoBehaviour
{
   //Cache the Transform
   protected Transform m_transform = null;
   protected Transform _Transform
    {
        get{
            return m_transform;
        }   
    }

   protected Main m_mainClass; //The main manager. Responsible for keeping the reference of all managers

   protected void Init()
   {
      m_transform = gameObject.transform; //Cache the transform

      m_mainClass = Main.mainInstance; //The ONLY singleton existing .
   }

//Provides manner to get and set transform properties (position, rotation)
  protected Vector3 _Position
    {
        get{     
            return _Transform.position;
        }
        set{      
            _Transform.position = value;
        }
    }

 
  //Service Locators
  protected InventoryManager _Inventory {
    get {
       return mainClass.GetInventoryManager();
     }
    }
}


public class PlayerController: GameScript
{
   void Awake()
   {
      base.Init();
    }

   //Here the "magic" happens
     void Update()
     {
        _Transform.LookAt(...) //Accessing the cached transform
         _Position = ... //Set the position, rotation, scale via cached transform
 
         _Inventory.AddItem(); //Inventory is located in base class.
       }
}

The purpose of using the base class as a service locator here is so that any scripts created do not have to be concerned with how to get the reference to the managers classes such as Inventory. It is the base class responsibility to handle this. The references could be singletons, they could not be. It doesn’t matter to any scripts which inherit GameScript.

What are people’s thoughts on this? Anyone else uses something similar to this?

I would definitely limit the “Main” singleton’s access and add control to the instantiation logic of that type.

Besides that, I’ve got the impression that further derived types may know too much about the existing systems / components. Depends which components you’re about to put into that hierarchy and whether or not these will be scripts you might want to re-use in new projects “as is”.

For my personal taste, it looks like something that belongs to the “very application specific” category. I could imagine this goes on top of alot of components that now need to be wired up.

But I have to admit that I’ve recently been doing a lot of general purpose framework and SDK development. So I might have a different focus and I probably prioritize very isolated and self-contained types that can be combined and then build more complex sub-systems.

1 Like
  1. Transform caching

Are you noticing faster speeds with this? Unity is supposed to cache the transform already. It’s still an internal call, but it’s faster these days. Anyways… if you’re trying to get that extra tiny oomph by not doing an internal call, why then does _Position access the property _Transform, rather than the field m_transform?

  1. Properties for position and what not.

See previous statement.

Also, why protected? I mean, I already have access to it via the transform. There’s really no need to protect it from public access, public access already exists.

  1. Service Locator

Why here?

By having it here you’re effectively giving it a psuedo static/global access to the thing. In which case… why not just create a static service locator?

Also, why should all GameScripts know about and define an accessor for every service/manager? It doesn’t speed anything up. You’re effectively just using inheritance to create syntax sugar for ‘Main.mainInstance.GetInventoryManager()’, so it looks like _Inventory instead.

Thing is… it now looks like a member which reads weird. Sorry, but to me, it’s like speaking in the 3rd person when you should 1st, or worse, speaking in the 1st when you should be 3rd.

Why not just have static accessors on Main that return them, so you just say:

Main.Inventory.AddItem();

Still as clean as:

_Inventory.AddItem();

If note cleaner, IMO. And it explicitly reads like you’re reaching out to some global source, rather than a local member. (and I’m not going to even start on conversation of how even that isn’t actually that good)

AND you don’t have to inherit from this GameScript class.

I personally have a base script class as well. It’s called SPComponent:

But the functionality it implements is 1) not necessary, and 2) part of the identity of a script rather than some syntax sugar.

It for example implements some interfaces that fit into my framework. It offers a reordered Start/Enable. And caches the entity root (an entity is a cluster of GameObjects treated as one). All of these functions pertain specifically to an instance of SPComponent, rather than some global syntax sugar.

And to be honest… early on I was shoving tons of junk into it. And I’ve had to back that all off over the years… honestly there’s some things in there I never use (like the cached entity root) that I’m thinking of obsoleting.

Where as your class so far offers transform caching, a featur that already exists in Unity. Shortcuts to the position property that effectively just acts as syntax sugar. And a strange service locator. Strip out the service locator… and really, you just have a MonoBehaviour with renamed properties.

1 Like

I appreaciate feedbacks.

Two years ago when I started doing this, I read a lot of threads regarding this. All of the threads I read at the time, there was no general consensus regarding what is actually happening internal to Unity when someone uses “.transform” property; (Even two mods were debating it between eachother in the thread, LOL).
There’s still this ongoing thread: https://discussions.unity.com/t/598764 . And it’s still not 100% clear.

Interesting. I’m shocked to learn now that this might not be automatically inlined for optimization. I’m reading into this article right now to learn more about this: https://answers.unity.com/questions/1195923/doesnt-the-c-jit-inline-property-getters-in-unity.html
I’ll change it just to be safe.

I invisioned that these “helper properties” would only be used internally and be not available to external scripts. I have no need for a script to call another script’s _Position or _Transform. I can make them public. But at the moment there is no need for it.

I’m contemplate doing this.

I guess that is my intention all along, though. The idea is that if someone (me) creates a script, I can simply type _Inventory, and not have to worry about (in the derived class) how the inventory manager reference is obtained. The implmentation can be changed at anytime in GameScript, without requiring any changes to the derived class.

what is/was the entityRoot used for?

I think the position call in the code would be worse off.

I don’t know. It’s good to be creative, but I don’t think you’d get a lot out of the script, unless as @Suddoha wrote, it’s specific for you (and you like it) :slight_smile:

I cache transforms sometimes, in fields, if I use them a lot.

1 Like

The real question is.
Do all these scripts actually need that access? It’s an interesting approach, but it feels like it might grow into something similar that’s known as “god object”… except that it’s based on a rich hierarchy.

If you need a type that has partially the same fields, methods and logic like an existing type, how would you solve this? It might not fit into the hierarchy and this can be a very annoying problem. The last resort will be another “new” branch in the hierarchy with redundant code (unless you outsource logic).

Don’t get me wrong though. If that’s a good approach for you, just head on. :slight_smile: I’m just very interested in software architecture and I’ve experimented and researched alot over the past years, so I’m just throwing in some of my own ideas, concerns and experience.

1 Like

So say I have my Player GameObject.

In it are several GameObjects. For example her ‘rig’ alone is made up of several bones. And there are some other GameObjects inside with specific use, like her HitBox, Some lights that follow her, and an Aspect which is a sort of center of mass point for her (it’s what other things that aim point at specifically).

This collection of GameObjects is what we call the ‘entity’. All of these GameObjects are related to one another in some manner as being part of the ‘player’.

And the defining factor of it is that they are all a child of the ‘PlayerF’ GameObject.

So that GameObject is tagged as ‘Root’.

Since it’s tagged that I can find a parent with that tag, know what is the root of the entity, and then search all of its children for components.

Just like I might ‘GetComponent’ to get a sibling component on the same GameObejct. I might instead want to search the entire entity for a specific component. Or traverse the entire entity to attach special scripts to key parts. So on and so forth.

Some things that are going extinct about it though are that I have created an ‘Entity’ script (note the ‘PlayerEntity’ on PlayerF):
https://github.com/lordofduct/spacepuppy-unity-framework-3.0/blob/master/SpacepuppyUnityFramework/SPEntity.cs

It still implements the ‘Root’ tag.

But as you can tell in the source code I just linked, SPEntity has a ‘Pool’ that can be polled for fast access to the entity. Still not quite as fast as the ‘entityRoot’ short cut. But ‘entityRoot’ still requires a GetComponent call, and the SPEntity.Pool is still pretty darn quick due to some optimizations I’ve added to it.

I almost exclusively use this SPEntity.Pool accessor rather than the SPComponent.entityRoot. But it still hangs around as a legacy thing that has dangled there for years.

That’s the annoying thing about developing these frameworks like this. You make a choice early on that can’t be easily removed down the line.

It’s why in Unity we still have so many poorly designed aspects to it. For the longest time things like Coroutines were started ONLY by its string name of the function, and it didn’t return a Coroutine object, so the only way to stop it was to call StopCoroutine with the string name… stopping all coroutines started with that name and not individual ones (if you started many), and make parameters for them a giant pain. And yet… 6 versions later, we still have these methods sitting their unused, and we have to yell at new users that “yes, we know they’re there, but don’t use them.”

As for the rest of your post, fair enough. I mean, the design is for you. You do what works for you.

Figured I’d just highlight some of my own concerns in regards to it.

As I said… some choices you make will follow you for years if you continue to maintain this code.

Of course a refactoring can resolve it. Which is what I’m doing right now to Spacepuppy (that’s why my new git repo is called 3.0). But still some choices, like entityRoot, just aren’t easily refactored out that it’s just easier for me to leave it in. It’s not hurting anything really.

1 Like

No, they might not need all of that access. A script might not even need any access at all. But it’s made available to the script if it ever needs it.

Since I started this project, I have struggled with determining how scripts should locate and access managers. I guess a lot of people just use the singleton approach where they have a singleton for each manager. In that case, any script can access any manager, anyway. The cons of that could be it’s almost impossible to track the dependencies.

I was looking through your repository for a while.
I have to say I like the idea of your MultiTag class and the “root” concept. I have struggled with this briefly on how to determine what the top level gameobject is. A few places I have, admittedly, such nonsense as:

var topLevel = someComponent.parent.parent.parent;

in my code when I know the object I’m looking for is X levels up the tree. This can cause severe issues if someone changes the hierarchy of a prefab without realizing some code is expecting some specific hierarchy to exist.
I think using something like your MultiTag class could solve this issue.

I also didn’t spend much time thinking about how to mimick assigning multiple tags to a GameObject. I say “mimick”, because I think your MultiTag is doing something that Unity doesn’t yet have implemented: allowing multiple tags to a GO.

I can’t figure out how you got the check-boxes to be displayed in the inspector GUI. How did you do that?

Well there was a few things I had to do.

First off here is the MultiTag class, as well as the MultiTagHelper static class (which creates some extension methods which you use in place of the ‘tag’ property of a GameObject to add/remove/check tags):
https://github.com/lordofduct/spacepuppy-unity-framework-3.0/blob/master/SpacepuppyUnityFramework/MultiTag.cs

Then I wrote the inspector found here:
https://github.com/lordofduct/spacepuppy-unity-framework-3.0/blob/master/SpacepuppyUnityFrameworkEditor/Base/MultiTagInspector.cs

The key thing is that Unity has a list of all the tags at editor time at this property:
UnityEditorInternal.InternalEditorUtility.tags

With that I just drew a bunch of toggles for each one.

Thing is it’s only available at editor time. I wanted to be able to check if a tag was valid at runtime for a few reasons. So I also created this ScritpableObject for caching all the tags to runtime:
https://github.com/lordofduct/spacepuppy-unity-framework-3.0/blob/master/SpacepuppyUnityFramework/Project/TagData.cs

And its inspector:
https://github.com/lordofduct/spacepuppy-unity-framework-3.0/blob/master/SpacepuppyUnityFrameworkEditor/Base/Project/TagDataInspector.cs

Though this isn’t necessary to draw the check boxes… it’s just an extra major dependency in the whole setup.

[edit]
Looking at my code… it’s been a while. This must have been one of the first things I wrote in Unity because it still uses an array even though I need to resize that array regularly. I must have written that way back before List was supported.

1 Like