Obviously we can't do anything to a class instance that hasn't yet been instantiated, because by definition it doesn't exist yet.
The real question here is "how can a class instance created at runtime get access to its dependencies, in Unity specifically?"
There are a couple of general approaches to this:
Make the GameMap globally accessible so each Entity instance can reach out and find it. (You're already doing a flavour of this)
Provide a GameMap instance to each Entity when it's spawned. (This is called dependency injection)
For 1, we have options like...
a) Each instance searches for its dependencies, using
GameObject.Find() as you're doing now, or FindObjectOfType() to shortcut straight to grabbing the component you want like so:
GameMap map = (GameMap)FingObjectOfType(typeof(GameMap);
As you point out, this isn't ideal, but it's also not a grave sin as long as you cache the reference for reuse via a member variable, so you're not searching for it every frame/every event.
b) Implement the Singleton Pattern. There are many ways to skin this cat, but a common theme is a static method to get an instance (and possibly construct it on-demand if it's absent from the project):
public class GameMap : Monobehaviour
private static GameMap _instance;
public static GameMap GetInstance()
_instance = this;
Now any code elsewhere in your scene can call:
var map = GameMap.GetInstance();
to get an instance to the one GameMap quite cheaply.
Where Singletons tend to start running into trouble is...
- When you change scenes and your Singleton is deleted (fix with DontDestroyOnLoad)
- When you chance scenes with DontDestroyOnLoad and there's already a Singleton in the new scene, so now you have two (fix by self-destructing if there's already an instance)
- When you try to use the Singleton in a scene without one, or before it's Awake (fix by constructing on demand)
- When you want to extend your game so there can be multiple GameMaps at once (may require substantial refactoring - this applies to any of the "global access" strategies)
None of this is insurmountable, you just need to be on the lookout for it. But if we're using static members anyway, we could sidestep some of the complexity and just...
c) Make the shared dependency itself static. You lose the ability to put it in the hierarchy or manipulate it in the inspector, but if you so choose you can farm these responsibilities out to auxiliary objects that are either constructed by the GameMap as needed, or already exist in the scene and register themselves with the GameMap on start.
On to #2, dependency injection...
When you create your Entity instances, tell them which GameMap they should use.
If you're not using a Monobehaviour, this is as straightforward as passing an argument to their constructor, as you describe in the question:
var myEntity = new Entity(myGameMap);
If you are inheriting from Monobehaviour, then you wouldn't use a constructor, but instead a setter after instantiating the object or attaching the component:
var myEntity = Instantiate(myEntityPrefab) as Entity;
Either way, the Entity caches the map it's passed, and any code that runs after [construction / Awake()] will know which map to use.
This approach scales to uses where you might eventually introduce multiple GameMaps, so the Entity objects don't need to know how to request the right one - it's pushed in from whatever higher-level system created them (which usually has more knowledge of the context in which it's creating the Entity)