Referencing an GameObject from another Scene

Hello friends!

So I have a “GameManager” scene. That scene will always be loaded. That scene will load different levels (01, 02, etc).

Those different levels will contain a couple of GameObjects that I want to reference when they load. A couple of those GameObjects would be the Player (which will always be in the levels scenes), etc.

So when a level scene loads, the Player has a script where I’m trying to set a reference of himself to the main GameManager that is in that other GameManager.scene. Then that GameManager knows the GameObject of the Player from the other scene … and from there, do something. For example I want to get this Player GameObject and give it to the camera that will follow the player.

To have a better idea. I’m trying to get the Player into that spot :smile:

I tried to do something like this:

Player has this on Start().

void Start()
    {
        p = GameObject.FindGameObjectWithTag("Player");
        gameManager.FindPlayer(p);
    }

I’m using FindGameObjectWithTag for testing purposes, since I don’t know how he can get a reference of himself… and then send himself over.

Also, the reason why the Player himself is sending himself over during the Start() is because I know that the Level Scene has loaded, and the player itself exists. If I don’t do it here, then I have to create a more complicated function on the GameManager that checks when the Level Scene has loaded. Maybe this would be a better idea actually…

GameManager has a function line this:

public void FindPlayer(GameObject p)
    {
        Debug.Log("FindPlayer (GameManager)");
        player = p;
    }

So what am I doing wrong?
I know it might be something super basic, but I’m still learing some basics.

Thanks <3

What is the gameManager variable? How does it target your existing GameManager that is in the first scene?

Also, if you are getting any errors, let us know by posting the error and telling us what line it is pointing to.

GameManager is my GameManager script. It is being detected because when I wrote gameManager.FindP… it auto-completed stating that he found the correct refference to the function.

I don’t get any errors regarding this. It just doesn’t work.

You can use this in your GameManager - Unity - Scripting API: SceneManagement.SceneManager.sceneLoaded

When the scene is loaded, just find the player.

using UnityEngine;
using UnityEngine.SceneManagement;

public GameObject player;

public class GameManager : MonoBehaviour
{
    void OnEnable()
    {
        SceneManager.sceneLoaded += OnSceneLoaded;
    }

    void OnDisable()
    {
        SceneManager.sceneLoaded -= OnSceneLoaded;
    }

    void OnSceneLoaded(Scene scene, LoadSceneMode mode)
    {
        player = GameObject.FindWithTag("Player");
    }

}

I was thinking on using something like that. It’s actually the correct way… but I remember in the past I had to use some coroutine that constantly checked the progress of the Scene Loading… and I got confused :frowning:

I also wanted to show the loading progression of several levels on a UI bar. And I remember then that I had to check to see when the Scene has loaded.

Typing gameManager into the script only means it found the variable. This doesn’t mean that it actually is targeting your GameManager in the scene. Is anything actually assigned to that variable? It looks like your GameManager is private, so how is your GameManager being assigned to that variable?

Granted, should have got a null error…so hmm… Is your player script even running? Have you put a debug message into Start to see if it prints out? You should be getting a null error if you aren’t assigning a value to gameManager and then trying to make a call on it.

Yea the player script is running. I did a debug and I saw that being printed.

So I think that I’m not sending the FindPlayer to the actual GameManager… ?
I thought that if I do “Game Manager gameManager”… my Game Manager script is being assigned to gameManager variable.

GameManager gameManager just means you are creating a variable that can hold an instance of the GameManager class. If it’s targetting something it may not be targetting the correct GameManager instance, which would keep it from giving you a null error but would also change how it works.

1 Like

Ok. So I have an GameObject that has my GameManager script.
How do I make my PlayerScript, send a message with itself to my GameObject that has my GameManager script?
Then my GameManager will run it’s function and will assign the PlayerScript to his “Player” variable.

I used to do this all the time with visual tools like FlowCanvas and PlayMaker.

P.S. I know that in theory I should make my GameManager detect then my scene has loaded, and only then he himself should do a search for the player script.

I DID IT!!!

FindObjectOfType<GameManager>().FindPlayer(p);

I noticed that I had some code that its similar. I found this code from a tutorial.

However … I’m still pretty confused.

Right now I also need to find my MainCamera, and also do something similar.

Ok. I have decided to do it the “Proper Way”. It seems that the only wany to know when a scene has loaded is to use what @Stardog suggested. Normally I wanted to do it … another way because that way seemed messy to me.

Anyway:

void OnLevelFinishedLoading(Scene scene, LoadSceneMode mode)
    {
        if (scene.name != "GameManager")
        {
            Debug.Log("Scene: " + scene.name + " Loaded!");
            player = GameObject.FindGameObjectWithTag("Player");
            mainCamera = Camera.main;
            followPlayer = mainCamera.GetComponent<FollowPlayer>();
            followPlayer.player = player.transform;

            player.GetComponent<PlayerMovement>().canMove = true;
        }
    }

I only run that code if the level that has loaded is not equal to GameManager… because the scene GameManager is also getting loaded once.

I set the variable player to be = to the GameObject that has the tag “Player”.
I set the variable mainCamera to be = to Camera.main.

Do you guys know a shorter way to write the 2 pieces of code that have the followPlayer?
Basically mainCamera has a component that has a variable called “player” of type Transform. I want to give the current player.transform to the followPlayer.player.


I know this might be some really basic shit for some people, but I didn’t had this much fun in a long time :smile: Been “coding” today for about 5 hours or so.

I know how to do a lot of those interactions with visual tools like FlowCanvas & PlayMaker but now I also want to know how to do those by code. I have no real interess to be a “real programmer” since I’m more of a Technical Game Designer, but I think it’s super important for me to know how to do those basic interactions with code and not using visual tools.

Thank you all for your help!

1 Like

Glad you got it working, I was away most of the day so I couldn’t help much further, but looks like you got it figured out!

Well maybe you can help me with something else that I think it’s super basic but something is off.

I have an int called “currentLevel”. I default it at 1.
Every time I run the function LoadNextLevel() I do currentLevel++. I also tried currentLevel = currentLevel + 1. currentLevel += 1;.

private void Start()
    {
        currentLevel = 1;
        StartCoroutine(LoadNextLevel(currentLevel));
    }

    IEnumerator LoadNextLevel(int currentLevel)
    {
        currentLevel++;
        AsyncOperation currentLevelLoading = SceneManager.LoadSceneAsync(currentLevel, LoadSceneMode.Additive);

        while (!currentLevelLoading.isDone)
        {
            Debug.Log(currentLevelLoading.progress);

            yield return null;
        }
    }

so from what I tested so far, the Scene that is getting loaded is currentLevel + 1 (meaning 2, the correct scene).
My problem is that the basic currnetLevel remains at 1, it does not get incremented.

I need to know what is my current level because I also have an UnloadScene & RestartScene functions that take the currentScene into consideration… but when they restart, they do it on the 1 scene and not 2 scene which I already incremented when I first loaded the level :smile:

So my guess right now is that my increment only happens in the LoadNextLevel function, but not on the whole script.
So how can I maniulate the variable of the whole script and not just inside that function?

Thanks <3

EDIT:
My guess is tha the currentLevel doesn’t get incremented because it’s inside a corotuine that it’s similar to a for, etc?

It should be incremented just fine, which makes me still wonder if you are creating a second instance of your GameManager script somewhere else, which would be why your other stuff didn’t work and this doesn’t seem to work. Feels like that is the case.

Thanks @Brathnann for checking my topic.

I managed to do what I proposed myself to do. There could still be a coulpe of things I would like to try out but my main task was to mess with the SceneManagement stuff.

This is my whole GameManager script:

using System.Collections;
using UnityEngine;
using UnityEngine.SceneManagement;

public class GameManager : MonoBehaviour {

    bool gameHasEnded = false;

    public int currentLevel;

    public GameObject completeLevelUI;
    public GameObject gameOverUI;
    public GameObject player;
    public Camera mainCamera;
    public GameObject scoreText;
    FollowPlayer followPlayer;
    Score score;

    void OnEnable()
    {
        SceneManager.sceneLoaded += OnLevelFinishedLoading;
    }

    void OnDisable()
    {
        SceneManager.sceneLoaded -= OnLevelFinishedLoading;
    }

    private void Start()
    {
        currentLevel = 1;
        LoadNextLevel(false); // it will load the next level but we don't have to unload our current scene.
    }

    public void LoadNextLevel(bool unload)
    {
        if (unload) // if it's set to true, we unload our current level so we load our new fresh level.
        {
            UnloadCurrentLevel();
        }

        StartCoroutine(LoadingLevel(currentLevel));
        currentLevel++;
        Time.timeScale = 1;
        completeLevelUI.SetActive(false);
    }

    IEnumerator LoadingLevel(int currentLevel)
    {
        AsyncOperation currentLevelLoading = SceneManager.LoadSceneAsync(currentLevel + 1, LoadSceneMode.Additive);

        while (!currentLevelLoading.isDone)
        {
            Debug.Log(currentLevelLoading.progress);

            yield return null;
        }
    }

    void OnLevelFinishedLoading(Scene scene, LoadSceneMode mode)
    {
        if (scene.name != "GameManager")
        {
            Debug.Log("Scene: " + scene.name + " Loaded!");
            player = GameObject.FindGameObjectWithTag("Player");
            mainCamera = Camera.main;
            followPlayer = mainCamera.GetComponent<FollowPlayer>();
            followPlayer.player = player.transform;
            score = scoreText.GetComponent<Score>();
            score.player = player.transform;

            scoreText.SetActive(true);
            player.GetComponent<PlayerMovement>().canMove = true;

            Debug.Log("Current Level: " + currentLevel);
        }
    }

    public void UnloadCurrentLevel()
    {
        Debug.Log("Unloading Level: " + currentLevel);
        SceneManager.UnloadSceneAsync(currentLevel);
    }

    public void CompleteLevel ()
    {
        completeLevelUI.SetActive(true);
        Time.timeScale = 0;
    }

    // Game Over Screen - We pause the game and we show a restart button
    // We set the gameHasEnded to false so we don't keep running this function
    public void EndGame()
    {
        if (gameHasEnded == false)
        {
            gameHasEnded = true;
            gameOverUI.SetActive(true);
            Time.timeScale = 0;
        }
    }

    public void Restart ()
    {
        Debug.Log("Restarting Level");
        UnloadCurrentLevel();
        SceneManager.LoadScene(currentLevel, LoadSceneMode.Additive);
        Time.timeScale = 1;
        gameOverUI.SetActive(false);
        gameHasEnded = false;
    }
}

I will do a small “tutorial” on my website in the next couple of days. I say “tutorial” because I don’t consider it a proper teaching code… but I like to share my experiences with others, maybe the most experienced people will come to give feedback and maybe the guys who are less experienced can still learn from this.

Anyway, if you spot some weird stuff please tell :smile:

Thanks <3

EDIT:
Here is a video with the whole thing:
https://streamable.com/ub58d

completeLevelUI declaration / reference is missing in your code.