networkManager and other dontdestroyonload object don't continue to the next scene

so i’ve been making a multiplayer game, but you could only make a lobby once because of problems with the networkManager, so to solve it i decided to put it in another scene, right before the normal game which will only load once. but when clicking the button to go to the next scene every dontdestroyonload item gets destroyed and it’s not in the hierachy anymore.
first to get to the next scene i used SceneManager.LoadScene("start"); but that didn’t work so i went to Loader.loadNetwork(Loader.Scene.Menu); but that didn’t work on it’s own so that’s why the whole StartGame and allocateRelay is in there, which still doesn’t work. Does anyone know how to fix this?
ps I am using version 1.2.0 for Netcode for GameObjects.

using System.Collections;
using System.Collections.Generic;
using System.Threading.Tasks;
using Unity.Netcode;
using Unity.Netcode.Transports.UTP;
using Unity.Networking.Transport.Relay;
using Unity.Services.Core;
using Unity.Services.Authentication;
using Unity.Services.Relay;
using Unity.Services.Relay.Models;
using UnityEngine;
using UnityEngine.SceneManagement;

public class menuScript : NetworkBehaviour
{
    public GameObject optionsMenu;
    public bool paused;

    public void PlayGame(int gameMode = 1)
    {
        PlayerPrefs.SetInt("mode", gameMode);

        if (gameMode == 0)
        {
            NetworkManager.Singleton.Shutdown();
            SceneManager.LoadScene("start");
        }
        if (gameMode == 1)
        {
            SceneManager.LoadScene("Menu");
        }
        if (gameMode == 2)
        {
            SceneManager.LoadScene("characterSelect");
        }
        if (gameMode == 3)
        {
            SceneManager.LoadScene("level1");
        }
    }

    // Modified StartGame to include Unity Services initialization
    public async void StartGame()
    {
        // Ensure Unity Gaming Services are initialized
        if (UnityServices.State != ServicesInitializationState.Initialized)
        {
            try
            {
                await UnityServices.InitializeAsync();
                await SignInAnonymously();
                Debug.Log("Unity Services initialized successfully.");
            }
            catch (System.Exception e)
            {
                Debug.LogError("Failed to initialize Unity Services: " + e);
                return;
            }
        }

        // Wait until NetworkManager and optionsScript are ready
        if (NetworkManager.Singleton == null || optionsScript.Instance == null)
        {
            Debug.LogError("NetworkManager or optionsScript is missing!");
            return;
        }

        Allocation allocation = await allocateRelay();
        if (allocation != null)
        {
            NetworkManager.Singleton.GetComponent<UnityTransport>().SetRelayServerData(new RelayServerData(allocation, "dtls"));
            optionsScript.Instance.startHost();

            // Check if the NetworkManager is operating as expected before changing scenes
            if (NetworkManager.Singleton.IsServer || NetworkManager.Singleton.IsClient)
            {
                Loader.loadNetwork(Loader.Scene.Menu);
            }
            else
            {
                Debug.LogError("NetworkManager is not running as a server or client.");
            }
        }
        else
        {
            Debug.LogError("Relay allocation failed.");
        }
    }


    // Separate method for Relay allocation
    private async Task<Allocation> allocateRelay()
    {
        try
        {
            Allocation allocation = await RelayService.Instance.CreateAllocationAsync(optionsScript.MAX_PLAYER_AMOUNT - 1);
            return allocation;
        }
        catch (RelayServiceException e)
        {
            Debug.LogError("Relay allocation error: " + e);
            return default;
        }
    }

    // Optional: Sign in anonymously if using Authentication
    private async Task SignInAnonymously()
    {
        if (!AuthenticationService.Instance.IsSignedIn)
        {
            await AuthenticationService.Instance.SignInAnonymouslyAsync();
        }
    }

    // Quits game mode
    public void QuitGame()
    {
        Debug.Log("Quit");
        Application.Quit();
    }

    // Resets game, just starts it again
    public void ResetGame()
    {
        SceneManager.LoadScene(SceneManager.GetActiveScene().name);
    }

    // Pauses/unpauses game on Escape
    void Update()
    {
        if (Input.GetKeyDown(KeyCode.Escape))
        {
            TogglePause();
        }
    }

    public void TogglePause()
    {
        optionsMenu.gameObject.SetActive(!optionsMenu.gameObject.activeSelf);
        paused = !paused;
        Debug.Log("Paused state: " + paused);
    }
}

This should not occur unless some script destroys these objects.

In the first scene, make sure you don’t load the next scene right away. It shouldn’t be done in Awake, Start or OnEnable but rather in Update to allow for the existing scripts in the scene to fully initialize. Also I recall that you can’t instantly load a new scene when the scene just started up until some point (maybe Awake is not allowed, or maybe even Start and OnEnable).

Note that your ResetGame() method won’t work when networked. If the network session needs to be restarted you have to use NetworkSceneManager.

Unless you use customized scene loading, clients must not load networked scenes. They can of course load additive scenes for their local-only use, such as the player’s HUD.

If a client loads a networked scene by itself, it will be out of sync with the server or it may even have duplicate content loaded.

so the next scene isn’t loaded right away, (on start, awake or onEnable) but is when the button is pressed, sorry if that was confusing. And I will fix it so the client cannot load networked scenes.

If that script indicates you’re doing the “network start” in that first scene, that’s not how I would recommend it.

Consider the initial “launch” scene as a container for all your DDOL objects. First thing you do is load the next scene, the first actual scene, on which you can have all the buttons and start networking. Because if you have these in the first scene, at some point sooner or later you will want to come back to that scene to start over respectively you’ll have to move all of that to the next scene anyways.

okay, but how exactly would I do that? make a scene that says loading and when it’s all loaded go to the next one? or am I understanding that wrong?

In its simplest form, the first “launch” scene in the build index list contains nothing but a script that does this to load the second scene in the build index list:

void Update()
{
    SceneManager.LoadScene(1);
}

Then just add whatever DDOL objects you need in your game in the “launch” scene, the NetworkManager for instance.

so I have that now but it still doesn’t take the DDOL with it to the next scene
EDIT: okay so the problem is somewhere else, i just put them back in the scene where i begin to test something else (because i cannot test otherwise) and now they get destroyed when i start that scene. Like they just disappear when i start the game and I have no idea why, an hour ago (or like 15 minutes) it worked just fine.

I have found where it went wrong, I apparently still had an main menu “cleaner” which was if there were more destroy it but it didn’t see it right and just immediately destroyed them, so without that script it works again! But I am very happy with your help CodeSmile since otherwise I wouldn’t have gotten this far!

1 Like