This type of thing is often implemented using some form of the Singleton pattern. Here's one version, fairly similar to what you're doing now, just without the redundant double-search for the object, then the component, when all you really wanted was the component:
public class Player: MonoBehaviour {
static Player _instance;
public static Player instance {
get {
if(_instance == null) {
_instance = FindObjectOfType<Player>();
UnityEngine.Assertions.Assert.IsNotNull(_instance, "Failed to find player component.");
}
return _instance;
}
}
}
Now other scripts can just ask for the current instance like this:
_player = Player.instance;
...and the singleton will look it up for them. It returns a cached copy quickly if it's been asked before, and only does the slow search the first time it's asked, or after the player's been destroyed and re-created.
You can also skip the search entirely and have your player "report" itself on load:
void Awake() { _instance = this; }
...but this can cause timing issues if you have other scripts that need to reference the player in their own Awake
methods that happen to run before the player got a chance to do so. If you can safely defer all references to the player until Start
or Update
though, this is an option.
(As an aside, some folks find they use this pattern so much that they implement it in a Singleton<T> : MonoBehaviour where T: Singleton<T>
, using the curiously recurring template pattern, so they can quickly add it to any class by declaring it as Player : Singleton<Player>
. This can help if you're worried about the amount of boilerplate this adds to every type you want to look up this way.)
You'll find folks are often critical of the singleton pattern for good reasons, though the risk of making the data global doesn't really apply here - as your code shows, access to the player object was already available globally.
So in this case, the main risk this method introduces is that it limits you to a single player at a time. If you ever want to have multiplayer with multiple instances of the Player
class active in the scene at a time, this method only lets you find the first one, and so you have some heavy refactoring to do if you've got dozens of scripts that all find "the" player this way.
That's no worse than your current code, and it's acceptable for many Unity games. And it's still solvable. You can replace your instance
property with a public static GetPlayer(int playerID)
function, or a GetClosestPlayer()
, etc. Then systematically replace each occurrence of Player.instance
with the appropriate alternative. It's work, but not an insurmountable obstacle.