Problem with multiple objects sharing the same script

I’m making a 2D platformer, and I have multiple NPC’s sharing the same script. Basically, what I want to happen is that the NPC will face the direction where the player currently is. So when the player is on the left side of the NPC, the NPC will simply face left, and if the player goes to the right side of the NPC, the NPC will then face right. I did this by making a gameobject called “FaceRight” with a 2DBoxCollider on the right side of the NPC, and another one called “FaceLeft” with a 2DBoxCollider of its own, on the left side of the NPC.

The Script for that goes like this:

public class FaceRight : MonoBehaviour
{
    void Awake()
    {
        flipFacingDirection = FindObjectOfType<FlipFacingDirection>();
    }
    FlipFacingDirection flipFacingDirection;
    private void OnTriggerEnter2D(Collider2D collision)
    {
        if(collision.tag == "Player")
        {
            flipFacingDirection.FlipSpriteX(true);
        }
    }
}
public class FaceLeft : MonoBehaviour
{
    void Awake()
    {
        flipFacingDirection = FindObjectOfType<FlipFacingDirection>();
    }
    FlipFacingDirection flipFacingDirection;
    private void OnTriggerEnter2D(Collider2D collision)
    {
        if(collision.tag == "Player")
        {
            flipFacingDirection.FlipSpriteX(false);
        }
    }
}

That will then call the FlipFacingDirection script I made, which simply toggles the flip boolean on the sprite renderer.

Here’s that script:

public class FlipFacingDirection : MonoBehaviour
{
    // Reference to the SpriteRenderer component
    [SerializeField] SpriteRenderer spriteRenderer;

    // void Awake()
    // {
    //     // Get the SpriteRenderer component attached to this GameObject
    //     spriteRenderer = GetComponent<SpriteRenderer>();
    // }

    // Function to flip the sprite horizontally
    public void FlipSpriteX(bool flip)
    {
        spriteRenderer.flipX = flip;
    }
}

Works perfectly fine! But the problem is, if I make another NPC game object and give it the same scripts, it goes wrong as theyre affecting each other. For instance, NPC A’s BoxCollider Trigger, will flip NPC B instead of A.

If I may add, I won’t be coding or doing game dev long term, I simply want this to work for this project so I can finally finish it, as I will move on to different things soon! With that in mind, please set aside “good coding practices” when giving feedback. Much Appreciated!

FindObjectOfType<FlipFacingDirection>(); finds the first active object of that type. It is possible you get the FlipFacingDirection from another character.

To get a script attached to the current game object use:

Like you did with the SpriteRenderer.

Hello!

Thank you for taking the time to comment. Can I ask if you can elaborate the solution you proposed? The Line: FindObjectOfType();

Ill give you a scenario of the problem. Say I have two objects A and B, they both share the same scripts. The script works perfectly fine with A, the NPC faces left or right depending on where the player object collides. However, when I go to the B NPC, the code still works but the one facing left or right is the A NPC, despite the player interacting with the B NPC’s BoxColliders. It’s like the B NPC controls A NPC. Here’s another thing I noticed if I unload the scene where A NPC resides, and I interact with B NPC’s colliders, I then get an error that my reference to the sprite renderer is gone, so I believe that my script only references the first NPC (the A NPC in this case), and ignores the others (I already made the sprite renderer serialized field so it references each game object’s own sprite renderer, but it seems like the only sprite renderer that was considered was the A NPC)

I forgot that FindObjectOfType(); is deprecated. You should use FindFirstObjectByType<>(); instead now.

But using this in the first place is the root of your problem.
The function finds any object of that type in the whole scene. It can find the one on its gameobject, but it can find one on any other gameobject too. You won’t know that beforehand. It’s random.
So use `FindFirstObjectByType<>(); only if you want to find a random one (or you are sure there is only 1, but for that case there are better solutions).

From what you wrote I think the situation is:

NPC B

void Awake()
    {
        flipFacingDirection = FindObjectOfType<FlipFacingDirection>(); 
    }

might get the FlipFacingDirection from NPC A when using the find method.

NPC B’s flipFacingDirection now references NPC A’s attached FlipFacingDirection script.
When now OnTriggerEnter is being called on NPC B it calls flipFacingDirection.FlipSpriteX(false); on NPC A (or any other object it found the component in the first place).

So NPC A and B can have a reference to the same FlipFacingDirection instance.
That’s why A works correctly and B not. It can be different everytime you start the game.

To fix this use GetComponent<Type>() in your scripts. It looks for the script only on the same gameobject (child gameobjects excluded):

    void Awake()
    {
        // Get the FlipFacingDirection component attached to THIS GameObject
        flipFacingDirection = GetComponent<FlipFacingDirection>();
    }

You can test it:

    void Awake()
    {
        flipFacingDirection = FindObjectOfType<FlipFacingDirection>();
        print($"I'm object: {gameObject} and flipFacingDirection is a component of 
 {flipFacingDirection.gameObject}");
    }

Now in the console it should show

Initially, I tried GetComponent<Type>() in my awake method. The code is commented out in my post but it’s the first thing that I did prior to using SerializedField and sadly, it didn’t work :frowning:

The game so far loads 3 scenes. NPC A resides in Scene 1 and NPC B resides in Scene 3. I have a collider trigger in scene 3, when the player passes through that, it unloads scene 1. I also have a collider trigger in Scene 1 that loads Scene 3 when the player passes through it. Initially, when the game starts, it only loads Scene 1 and Scene 2, Scene 3 wont be loaded until the player walks through the trigger, and so NPC B doesn’t exist yet at the start. NPC A works correctly, the triggers lets him face left or right accordingly. When the player passes through the collider in Scene 1, Scene 3 loads, and when I go to scene 3, I notice that the collider triggers of NPC B makes NPC A move instead of its own self. Finally, when the player triggers Scene 3’s collider that makes Scene 1 unload, I get this error:

I suspect that GetComponent<Type>() only references NPC A’s Sprite Renderer, and the other game objects with the same script will rely on NPC A’s Sprite Renderer, which is why NPC A is the one that moves despite the player interacting with NPC B’s colliders. But i’m confused as to why this is the case because you mentioned that GetComponent<Type>() will only reference on the same gameobject. Shouldn’t NPC B reference its own sprite renderer then? Im using the same script on both NPC’s, would that mean that it will only reference one sprite renderer?

I would also like to mention that the NPC’s are coming from the same prefab. I already unpacked NPC B to make sure theyre not connected in any way, but the problem still persists. The only thing that are the same with the two are the scripts.

Keep in mind that using GetComponent() and its kin (in Children, in Parent, plural, etc) to try and tease out Components at runtime is definitely deep into super-duper-uber-crazy-Ninja advanced stuff.

Here’s the bare minimum of stuff you absolutely MUST keep track of if you insist on using these crazy Ninja methods:

  • what you’re looking for:
    → one particular thing?
    → many things?
  • where it might be located (what GameObject?)
  • where the Get/Find command will look:
    → on one GameObject? Which one? Do you have a reference to it?
    → on every GameObject?
    → on a subset of GameObjects?
  • what criteria must be met for something to be found (enabled, named, etc.)
  • if your code expects one instance and later you have many (intentional or accidental), does it handle it?

If you are missing knowledge about even ONE of the things above, your call is likely to FAIL.

This sort of coding is to be avoided at all costs unless you know exactly what you are doing.

Botched attempts at using Get- and Find- are responsible for more crashes than useful code, IMNSHO.

If you run into an issue with any of these calls, start with the documentation to understand why.

There is a clear set of extremely-well-defined conditions required for each of these calls to work, as well as definitions of what will and will not be returned.

In the case of collections of Components, the order will NEVER be guaranteed, even if you happen to notice it is always in a particular order on your machine.

It is ALWAYS better to go The Unity Way™ and make dedicated public fields and drag in the references you want.

About the actual error:

NullReference is the single most common error while programming. Fixing it is always the same.

  • also known as: Unassigned Reference Exception
  • also known as: Missing Reference Exception
  • also known as: Object reference not set to an instance of an object

How to fix a NullReferenceException error

https://forum.unity.com/threads/how-to-fix-a-nullreferenceexception-error.1230297/

Three steps to success:

  • Identify what is null ← any other action taken before this step is WASTED TIME
  • Identify why it is null
  • Fix that

If you are unsure how to architect your project and connect stuff up, that’s not a thing strangers in the forum can tell you in anything but the most general sense. Doing tutorials correctly (see below) and exposing yourself to different project structure strategies is the best way to get to a solution for whatever you want.

Remember the first rule of GameObject.Find():

Do not use GameObject.Find();

More information: Regarding GameObject.Find · UnityTipsRedux

In general, DO NOT use Find-like or GetComponent/AddComponent-like methods unless there truly is no other way, eg, dynamic runtime discovery of arbitrary objects. These mechanisms are for extremely-advanced use ONLY.

Two steps to tutorials and / or example code:

  1. do them perfectly, to the letter (zero typos, including punctuation and capitalization)
  2. stop and understand each step to understand what is going on.

If you go past anything that you don’t understand, then you’re just mimicking what you saw without actually learning, essentially wasting your own time. It’s only two steps. Don’t skip either step.

Of course. It’s all the same problem.
Both NPCs variable flipFacingDirection are refering to FlipFacingDirection on NPC A and therefore are using its reference to its SpriteRenderer.

I recommend to use GetComponent or get rid of Awake completely and assign all fields in the inspector.
Just make sure there is no FindObjectOfType involved anywhere.

I’m confused. GetComponent basically was the first thing I learned in Unity 4. What is the definition of “extremely-advanced”?

I’m not argueing, I’m just curious if this is your oppinion or is the recommended way, as you mentioned Unity way somewhere. I always prefered to use GetComponent (with RequireComponent attribute mostly), because too often I forgot to assign public fields in the inspector.

See above for an abbreviated list of things that can make Get / Find fail.

NOTE: I have seen 100% of the above ways fail here in the forum and in person, and working with other teams’ projects.

And there are probably 57,000 more possible ways that Get / Find can fail.

Use Get / Find if you want but when it fails, don’t post here.

The official documentation already all you need to know to fix your problem.

I find it easier to drag a public reference in.

And when this happens:

Then fix it. It still has way fewer ways to fail.

Do you avoid using lowercase 't` because you forget to cross it?

Do you avoid using lowercase ‘i’ because you forget to add the dot?

Okay this completely solved my problem, I realized I was referencing flipFacingdirection the wrong way, I thought I only had to worry about referencing the sprite renderer and nothing else. Got rid of my awake methods, didn’t use GetComponent anywhere and manually assigned everything in the inspector. Now everything works fine.

Thanks TrizZzle!

Yup! This did solve my problem, I had to set my variables to [SerializeField] and manually assign everything in the inspector. Everything works now! Thanks kurt, and I appreciate all the insight!