Am I doing something wrong?

After going through quite some tutorials I find myself developing my own game. I found out, that I often need references to other gameObjects or classes and what I usually to is create a variable in my script to reference the other script and then in the Start function I look for that object and assign it to the variable

OtherScript scriptName;

void Start() {
    scriptName = GameObject.find("otherObjectName").GetComponent<OtherScript>();
}

Doing this feels a bit too repetitive and “not right”…

Another way would be to assign those objects through the inspector but I’m a bit scared about losing what I dragged it if I work with or edit prefabs.

Am I doing something wrong or is that just the way it is?

That’s just the way it is, for the most part. There are a few other techniques, too. You can choose the one(s) that work best in each case.

I try to avoid GameObject.Find because it’s so easy to mistype the name string. Also, GameObject names often change, and you don’t want to have to edit all of your scripts to update the string. If you must use GameObject.Find, try to define string constants. Then, if the GameObject name changes, you only need to update the constant:

public class MyObjectNames {
    public const string PlayerName = "Player Character";
}
...
myInventory = GameObject.Find(MyObjectNames.PlayerName).GetComponent<Inventory>();

GameObject.FindWithTag is often safer, since tags change less frequently:

myInventory= GameObject.FindObjectWithTag("Player").GetComponent<Inventory>();

If it’s a unique script, consider using FindObjectOfType so you can avoid strings:

myInventory = FindObjectOfType<UniquePlayerInventory>();

Depending on the situation, it can help to have GameObjects register with a central manager. Something like:

public static class Manager {
    public static Inventory playerInventory { get; private set; }

    void Manager() {
        playerInventory = null;
    }

    public static void RegisterPlayerInventory(Inventory inventory) {
        playerInventory = inventory;
    }

    public static void UnregisterPlayerInventory(Inventory inventory) {
        playerInventory = null;
    }
}

public class Inventory : MonoBehaviour {
    private bool isPlayer;

    void Awake() {
        isPlayer = this.CompareTag("Player");
        if (isPlayer) Manager.RegisterPlayerInventory(this);
    }

    void OnDestroy() {
        if (isPlayer) Manager.UnregisterPlayerInventory(this);
    }
}

As I started typing that, I realized it wasn’t the best example, but I hope it conveys the general idea of the design pattern.

That is one way to get a reference to another object, but your instinct isn’t wrong - it’s not a very good way. It won’t survive the GameObject’s name changing, for example, and it’s also relatively slow (though that’s not a huge deal as long as you’re caching it, which you are).

In Unity there are actually quite a few ways to get references to other objects. You’ve already found two: GameObject.Find and public references linked in the Inspector. Don’t be too nervous about using the Inspector - Unity doesn’t have Alzheimer’s, and it’ll do a pretty good job of remembering what you tell it. (As an aside, the fact that you’re worried about losing data like this suggests to me that you’re not using source control - and you definitely should be!)

Two other ways come to mind for getting references to other objects:

  1. The classes are able to find themselves, such as by using the singleton pattern. If you have only one OtherScript object, then you give it a “public static OtherScript main;”, and set “main=this;” in its Awake() function. All other scripts would be able to find the OtherScript object immediately via OtherScript.main.

  2. FindObjectsOfType(); This is probably about as slow as GameObject.Find, but it at least returns a more predictable result that won’t break when you rename your object.

Which one is the “right” way always depends on your purpose and the context.

When bound through the inspector, prefabs will keep references to components in their own hierarchy but for outsiders it will be dependant on instance scope so its far less reliable.

You’ll see tons of discutions about patterns & best practices in this forum, I personnally and from experience think singletons are well suited for many tasks in a game.

I also like grouping references by scope, ie all meaningful preplaced objects/components referenced in a component that should probably be a singleton (it depends how your scenes are setup) and when this is relevant, a per prefab component reference holder at the root, this way you only need to poll for this instead of searching stuff deep down the hierarchy when interacting with it.

Tony’s suggestion is great however I would modify code as such to avoid false erasing when another instance is alive :

public static void UnregisterPlayerInventory(Inventory inventory)
{
       if (inventory == playerInventory)
        playerInventory = null;
}

^ Thanks! Good point. Also, it doesn’t have to be a static class. Since Unity calls OnDestroy on outgoing GameObjects when changing scenes, it’ll still clean up after itself. But you could just as easily make it an in-scene MonoBehaviour.

Everything has its use, including singletons, but I try to avoid them when possible. I’ve come across so many projects that implement things like player controllers as singletons (and, for brevity, including the Manager script I typed above). This works great for single player, but if you switch to multiplayer you end up having to rewrite huge amounts of code to accommodate multiple instances.

Thanks a lot for all the answers! I didn’t know “FindObjectOfType” I think it suits my level of skill and requirements pretty well. I’ll try and use this. StarManta’s solution looks pretty promising as well, I’ll give it a shot. By the way I do use version control :wink: I can’t comment on singletons as I don’t even know what they are and I’m scared that if I look it up now I will find it cool and try to implement it in my game creating a huge mess. If they’re important I’ll stumble upon them again in the future I guess.

A singleton is just what I posted in my post (#1): if there’s intended to be exactly one of something in a scene, it has a convenient reference to it as a static member of the class.

@TonyLi just for the reccord, I agree with the risk of having to refactor and that’s what I like about the reference holder I was mentionning, switching from singleton to targeting an instance is pretty easy in this setup !

1 Like