20

In my game there is a map view that contains a 50x50 grid of tiles. When you click on the tile you get sent to that tiles view and attack things, etc. The only difference between these "tiles" as far as the code will be concerned is the tiles ID, aka. which number on the grid. That number will be passed to the server on init to handle the rest.

Obviously, with that being the only difference in tiles it would be a mistake to create a scene "1", scene "2"...scene "2500" and call SceneManager.LoadScene to switch to the specific tile view.

I could use DontDestroyOnLoad(); when the tile is clicked to preserve the tile ID on scene switch but 1) it only accepts gameobjects not just an int variable 2) I don't need/want to preserve that variable for anything more than the init in tile view. So while it could work it seems like overkill.

Is there a better practice for essentially just passing a parameter to a scene load?

DasBeasto
  • 2,082
  • 5
  • 25
  • 65
  • 1
    I suppose you could make a static class that holds the information, if you don't need it to talk to gameobjects (inherit monobehaviour). 1 sec, I'll give you a quick example. – Fredrik Schön Feb 22 '17 at 13:50

3 Answers3

45

You can make a static class that holds the information for you. This class will not be attached to any GameObject and will not be destroyed when changing scene. It is static which means there can only be ONE of it; you can't write StaticClassName scn = new StaticClassName() to create new static classes. You access them straight through StaticClassName.SomeStaticMethod() for example and can be accessed from anywhere. See this example on how to store a value in a variable, change scene and then use it in that scene (please note the added using UnityEngine.SceneManagement;):

A normal Unity script attached to a GameObject in Scene "Test":

using UnityEngine;
using UnityEngine.SceneManagement;

public class TestingScript : MonoBehaviour 
{
    void Start()
    {
        StaticClass.CrossSceneInformation = "Hello Scene2!";
        SceneManager.LoadScene("Test2");
    }
}

A new static class (not inheriting from monobehaviour) that holds information:

public static class StaticClass 
{
    public static string CrossSceneInformation { get; set; }
}

A script attached to a game object in scene "Test2":

using UnityEngine;

public class TestingScript2: MonoBehaviour 
{
    void Start () 
    {
        Debug.Log(StaticClass.CrossSceneInformation);
    }
}

You don't need to have the entire class static (if you for some reason need to create more instances of it). If you were to remove the static from the class (not the variable) you can still access the static variable through StaticClass.CrossSceneInformation but you can also do StaticClass sc = new StaticClass();. With this sc you can use the class's non-static members but not the static CrossSceneInformation since there can only be ONE of that (because it's static).

Fredrik Schön
  • 4,888
  • 1
  • 21
  • 32
  • 2
    I usually just make one such class per scene that needs some context information – Dunno Feb 22 '17 at 14:04
  • 1
    I like this a lot! Just what I needed. And that you for the reminders on what static is I've been using them somewhat arbitrarily. – DasBeasto Feb 22 '17 at 14:07
  • Great! Glad to help! – Fredrik Schön Feb 22 '17 at 15:07
  • But where does the StaticClass live? In what file or script? – KevinVictor Apr 12 '19 at 21:25
  • Anywhere, if it's static it will be accessible from anywhere. It can be in your root directory or any subfolder, as long as it's in your solution. – Fredrik Schön Apr 13 '19 at 16:44
  • OK. So do all static fields in the entire solution get initialized when the game starts? – KevinVictor Apr 13 '19 at 17:52
  • Yes. However, a static CONSTRUCTOR is not called until when the class (or a field in the class) is called the first time. – Fredrik Schön Apr 13 '19 at 18:23
  • Okay, thank you. Btw, the part the was hard to grasp was that a static field on a MonoBehaviour attached to a GameObject in one scene can be accessed from anywhere even when that scene isn't loaded. e.g. I can have a MainMenu scene which has a script with static fields, and those fields persist even when another scene is loaded. – KevinVictor Apr 13 '19 at 18:49
  • 2
    This doesn't really seem like the right way to do it, though...? I was hoping for an example using LoadSceneParameters....? This is very not OOD. – Dylan Brams May 23 '20 at 10:46
  • This is an old answer, as technology goes. I don't think there was any other way of doing it back in 2016/2017. Loading scene with parameters seems nice, I should take a 2020 look and see if I can update the answer! – Fredrik Schön May 25 '20 at 08:25
3

Just wanted to share a premade solution for this as the question is already answered, so others or maybe even the OP use it.

This script uses a key-value approach for storing and modifying variables inside a static class that means it is available across scenes so you can use it as a cross-scene persistent storage, so all you need to do is import the script to your Unity project and use the API (check below for an example):

using System.Collections.Generic;

/// <summary>
/// A simple static class to get and set globally accessible variables through a key-value approach.
/// </summary>
/// <remarks>
/// <para>Uses a key-value approach (dictionary) for storing and modifying variables.</para>
/// <para>It also uses a lock to ensure consistency between the threads.</para>
/// </remarks>
public static class GlobalVariables
{

    private static readonly object lockObject = new object();
    private static Dictionary<string, object> variablesDictionary = new Dictionary<string, object>();

    /// <summary>
    /// The underlying key-value storage (dictionary).
    /// </summary>
    /// <value>Gets the underlying variables dictionary</value>
    public static Dictionary<string, object> VariablesDictionary => variablesDictionary;

    /// <summary>
    /// Retrieves all global variables.
    /// </summary>
    /// <returns>The global variables dictionary object.</returns>
    public static Dictionary<string, object> GetAll()
    {
        return variablesDictionary;
    }

    /// <summary>
    /// Gets a variable and casts it to the provided type argument.
    /// </summary>
    /// <typeparam name="T">The type of the variable</typeparam>
    /// <param name="key">The variable key</param>
    /// <returns>The casted variable value</returns>
    public static T Get<T>(string key)
    {
        if (variablesDictionary == null || !variablesDictionary.ContainsKey(key))
        {
            return default(T);
        }

        return (T)variablesDictionary[key];
    }

    /// <summary>
    /// Sets the variable, the existing value gets overridden.
    /// </summary>
    /// <remarks>It uses a lock under the hood to ensure consistensy between threads</remarks>
    /// <param name="key">The variable name/key</param>
    /// <param name="value">The variable value</param>
    public static void Set(string key, object value)
    {
        lock (lockObject)
        {
            if (variablesDictionary == null)
            {
                variablesDictionary = new Dictionary<string, object>();
            }
            variablesDictionary[key] = value;
        }
    }

}

And you can use it on a script that is inside your Main Menu scene let's say:

public class MainMenuScript : MonoBehaviour
{

    void Start()
    {

        // Load a sample level
        LoadLevel(12);
    }

    public void LoadLevel(int level)
    {
        GlobalVariables.Set("currentLevelIndex", level);
        UnityEngine.SceneManagement.SceneManager.LoadScene("Game");
    }

}

And the other one is inside Game scene:

public class GameScript : MonoBehaviour
{

    void Start()
    {
        int levelIndex = GlobalVariables.Get<int>("currentLevelIndex");
        Debug.Log(levelIndex); // Outputs 12
    }

}

So, the data is assigned in the Main Menu scene is accessible from the Game or any other scene.

Learn more about the script with usage examples >

Hasan Bayat
  • 926
  • 1
  • 13
  • 24
-10

Maakep! Perfect and easy code!

But your method to load scene not working.

You can use another method:

UnityEngine.SceneManagement.SceneManager.LoadScene("Vuforia-4-Spheric");
azatserzhan
  • 111
  • 1
  • 3
  • 3
    That's the same method, but specifying the entire namespace instead of importing it. If you add `using UnityEngine.SceneManagement;` in the beginning of your file you'll be able to use it like `SceneManager.LoadScene("scene")`. :) – Fredrik Schön Jul 26 '18 at 09:30