FindGameObjectWithTag vs Static Class for Player Reference

So I have a sort of bullet hell game with lots of on-screen entities. For a number of reasons, each entity needs to access player components or various data elements such as position.

I’ve been using FindGameObjectWithTag(“Player”) to do this, but am well aware this is not an ideal solution as it will be called potentially hundreds of times per second. I’ve not had performance problems but I also have a top-end PC.

On the other hand, static classes are also frowned upon. But I feel that if I had a static class that called the player reference once and stored it, I could be far more efficient with this – by calling StaticClass.Player instead of finding by tag on every entity.

Does anyone know any particular reasons against one or the other? Philosophically or practically. I am new to scripting but want to have a good foundation to build upon so I am not redesigning core elements down the line.

Static classes aren’t frowned upon. They just have a specific use. I don’t see the issue with having some form of statically available data for certain core elements. You could also look to the singleton pattern as well.

Personally I’d have the player’s components publishing data about itself each update tick, that enemies can read through the static class.

Alternatively, you could also do this through scriptable objects, the benefit being you have an actual asset with fields, so you can see the data in a clean and readable fashion.

I appreciate the input, thanks. I think your suggestion of publishing data to the static element every update might work. However I’m wondering, is there any discernable reason not to simply store the player reference onto the class directly?

For example
[Static Class]
Static Class PlayerTracker()
public static GameObject Player;

[On Player Start()]
PlayerTracker.Player = gameObject;

And from here, directly access whatever variables I need such as
Vector3 playerPos = PlayerTracker.Player.transform.position;

Is there any benefit to posting the data to individual variables on the static class, instead of storing the gameobject itself and pulling what I need from it?

What you’re proposing there is closer to a singleton. Nothing inherently wrong with them, but when you starting get multiple mechanics and systems reaching through this reference and spreading their fingers throughout everything you might find debugging becomes difficult.

To that end you might want to have a go-between that contains any information that might be required, but doesn’t allow outside elements to reach in any further.

For a small project your method is no doubt A-OK, so I’d just go with it and keep the potential foibles in mind. Personally I prefer only giving as much information and access as required, but again, that’s just me.

It depends on your use cases, but one potential benefit of publishing data vs. accessing the raw reference is immutability.

Say you have a hundred different objects referencing your player object. There’s nothing stopping any of these objects from changing some aspect of the player that might break how some other objects work, and that can be hard to debug.

In your example, you could alter your PlayerTracker to store a read-only wrapper value around the player object’s position:

public class PlayerTracker
{
  private static GameObject _player;

  public static Vector3 Position => _player.transform.position;
}

...

Vector3 playerPos = PlayerTracker.Position;

In this case, other scripts have strict read-only access to the player’s position, and nothing more. It’s not possible for a script to change some other aspect of the player accidentally.

Again, it really depends on your use cases though.

1 Like

Great advice, really appreciate the input

I think this is a great solution and makes a lot of sense. I will probably go this route. Thanks for your input

I think knowing why static classes are sometimes frowned upon is key. A lot of it has to do with avoiding singletons. I think Vryken’s idea is not a bad solution to your specific question. It doesn’t get you out of all the problems in the singleton pattern, but it’s better than what you have.

Singletons are avoided because they can make your code less flexible. Singleton problems apply both when having a static player reference, or when getting the player by tag in the way that you suggested: you still expect to have only one object of a specific type at a time.

In your case, if you want to test an entity in isolation, you have to create a player, and if your player needs other elements to work, you have to create them too. The chain of dependencies that are needed for a single thing to work can get huge. Suddenly, to find out why an enemy isn’t shooting in the right direction, you need to create UI, health bars, inventories, a level manager, etc. And if you have multiple bugs, it’ll be harder to find them, because it’ll be harder to make just one of them happen. A long chain of dependencies by itself makes it easier for you to introduce bugs.

Singletons also tend to create dependencies on specific quantities of specific classes. For example, they make it much harder to add local multiplayer, or to make some entities target something that’s not a player.

The thing is, these problems aren’t too bad for simple stuff or prototypes. Even for complex stuff, singletons can be okay when used sparingly. It depends a lot on your use case and the nature of your project; for most beginners with small projects, I’d recommend not worrying about it a lot.

That said, the main alternative to singletons is having a way to assign dependencies to objects directly. It could be a field in the editor (my favorite), a method with parameters, a way for searching for things near, some dependency injection system like Extenject, etc. This way, you can easily change things like an enemy’s target. If you want, you can still have the singleton as a backup, in case a dependency isn’t assigned in other way.

Another thing that helps is to reference abstract classes or interfaces instead of concrete types. For example, it allows you to make target.position an abstract property, then you can have different targets that calculate their position in different ways.

2 Likes

If your problem is “called potentially hundreds of times per second” then simply caching the reference after you find it the first time will solve that.

However, I tend to think that a Singleton is a perfectly reasonable answer in this case. The one thing that I would say is that instead of just storing a variable there, consider if there’s anything else you can do to manage it. For example, in one of my projects the player can control various objects. I have a PlayerTracker which is Singleton which is used to manage “what thing is the player controlling now?”. It keeps a reference to the currently controlled object, and it also provides a method to request a control change. When a change is requested it checks that it’s a valid change, then changes the variable, then raises an event so that any other objects which care (the map, the camera, etc.) can react to the change without ever being told to.

When you’re told to avoid something that doesn’t mean that you can never use it under any circumstances. It means that you should look for multiple solutions and go with the best one, instead of jumping on the first one.

Static variables and Singletons are a common target of this because to a rookie they often look like a simple answer to their problems, and they’re discovered early, so they’re a popular early solution. Unfortunately they’re easy to misuse, and doing so leads to problems later. So newbie programmers are often told “do not use static variables” because it’s desirable for you to find a variety of other solutions first.

1 Like

This sounds really useful. How do you set up such events to trigger other objects? Can you point me in the general direction so I can read up on this?

Thanks

Look up UnityEvents and C# delegates. Also, the Observer pattern, Publish-Subscribe pattern, and Event Driven Programming in general.

But take it one step at a time.

Singletons are GREAT. But one common confounding problem is how Unity Destroy()s things in the scene, things that the singleton might be referencing. Even more confusingly, if your singleton is a MonoBehaviour, it can ALSO become destroyed and there is nothing you can do about it from a coding standpoint.

The best pattern I have found is to NEVER stick things in the scene. Instead, have them become available on demand, lazy-loading them.

Simple Singleton (UnitySingleton):

Some super-simple Singleton examples to take and modify:

Simple Unity3D Singleton (no predefined data):

Unity3D Singleton with a Prefab (or a ScriptableObject) used for predefined data:

These are pure-code solutions, do not put anything into any scene, just access it via .Instance!

If it is a GameManager, when the game is over, make a function in that singleton that Destroys itself so the next time you access it you get a fresh one, something like:

public void DestroyThyself()
{
   Destroy(gameObject);
   Instance = null;    // because destroy doesn't happen until end of frame
}

There are also lots of Youtube tutorials on the concepts involved in making a suitable GameManager, which obviously depends a lot on what your game might need.

These are really clean and well documented. Really appreciate you sharing them with me. I will probably take your advice and try to migrate my current system into a self-generating instance like these

1 Like