How to pass a variable to a script in another scene - c#

I have a main menu for a game where you can select difficulties, easy, normal etc.
Currently, I load a separate scene for each difficulty, I have a scene for easy, a scene for normal, hard, etc.
I know you can pass variables between scenes, such as
something.GetComponent<someScript>().someVariable
But how do you pass a variable to a script on a GameObject that doesn't exist yet?

You could use PlayerPrefs to solve this issue. For example, in your script on your main menu where the difficulty is set you would say:
PlayerPrefs.SetString("Difficulty", "Medium");
Then in your next scene, when you need to access that variable on a different script, simply use:
String difficulty = PlayerPrefs.GetString("Difficulty");
You could also look at using DontDestroyOnLoad to keep track of your variables on a persistent script which stays with you on an object that remains loaded even as scenes change. Then you could access the variables you need similar to how you have already described.

To have the variable available in the next scene, the standard way is to have a game object that doesn't get destroyed when the scene changes. You do this by calling DontDestroyOnload() in any script on that game object, e.g.
void Awake () {
DonDestroyOnLoad(gameObject);
}
You can use an existing game object or create one just for this purpose, with a single script that calls DontDestroyOnload() and also has variables that you want to pass to the next scene. You can set the variables, before loading the next scene, like in your example code:
someGameObject.GetComponent<GameVariablesScript>().someVariable = someValue;

Related

Destroying Instantiated Clone Components Breaks Original Game Object Components?

Maybe im missing something obvious but to keep it short. I have working code for a character. When you "dash" I want to leave behind an after-image type effect, by cloning the player, removing its unneeded components and then applying a shader. Issue is as soon as I instantiate the clone the original player stops functioning (cant move etc). It still has all its components and everything as normal, and the clone does get the correct components removed. But it still breaks. As soon as I remove that line, its back to normal. Any ideas?
This is the only relevant code in the script that instantiates the clone.
private void DodgeEffect()
{
GameObject _DodgeSFX = Instantiate(gameObject, transform.position, transform.rotation );
Destroy(_DodgeSFX.GetComponent<PlayerController>());
Destroy(_DodgeSFX.GetComponent<PlayerCombat>());
Destroy(_DodgeSFX.GetComponent<WarpController>());
Destroy(_DodgeSFX.GetComponent<Animator>());
}
its because you are making a copy of gameObject. while Instantiate() returns a GameObject, it also returns whatever you put into the first section of the method. instead, make a seperate gameObject than the player in the editor, and make _DodgeSFX public so you can put the copy into the slot. Then, just instantiate that seperate GameObject and you wont have to destroy the components through script(because you remove the components in the editor), saving time
Ok so from some testing I think its down to the Unity.InputSystem only allowing 1 instance of each Input to be enabled at once. When I instantiated a clone of the player, it still goes through the Enable/Awake functions before those components are destroyed, and since the Inputs on that clone were never disabled, that becomes the "main" one. If I set the main player's scripts with Inputs deactive and then active again, it all works as normal. Note that the script was still working on the main character just fine, it was only the inputs that was broken. Im still not sure why this is the case, if its a limitation of the input system, or intentional, cant seem to find documentation of this experience anywhere.

Spawning gameobjects relative to the position, width and length of another gameobject?

I'm currently developing a game in Unity using C# and I've run into a small problem.
I need to spawn a certain gameobjects relative to the Spawnposition and length of another game object. Now I figured that bounds.size would be the best function to use in this instance. As shown bellow I declare first the variable that uses this in my start method:
public void Start()
{
GameObject PointBar = (GameObject) Resources.Load("PointBar Horizontal");
PointBarVectorLength = PointBar.GetComponent<BoxCollider2D>().bounds.size.x;
PointBarVectorConv = Camera.main.WorldToViewportPoint(new Vector2(PointBarVectorLength, 0f));
}
However, the gameobjects in question are inactive at start's call and thus I presume don't return any value for bounds.size when called.
Does anyone know how I can access the bounds.size or equivalent of an inactive gameobject or is there something else I'm missing here?
As noted in the documentation for the Collider.bounds property, bounds "will be an empty bounding box if the collider is disabled or the game object is inactive". So your assumption was pretty much right. Since the object doesn't exist in worldspace that makes sense.
I'm not sure about the most elegant solution for your use case but two options spring to mind.
Calculate the object's spawn dimensions by accessing its transform.localScale and factoring in the scale of prospective parent objects. That could get messy but you could probably also write a helper method to make it more manageable.
Instantiate the object somewhere off screen after you load it, access its Collider.bounds property to get the information you need then move it where ever you want it or store the information for later.
There may be better solutions but that's what leaps to mind for me.
I solved the issue by using GetComponent().bounds.size.x;
instead of BoxCollider. This component can get accessed when the game object is not active.

Loading scene in Unity makes the references in that scene null

I am making my first game in Unity and I'm trying to load the first level of it when the cutscene at the start ends. I don't know if it's possible to make the script do something after a video clip ends, so I wrote my code like this:
using UnityEngine;
using System.Collections;
using UnityEngine.SceneManagement;
public class CutsceneEnd : MonoBehaviour
{
// Start is called before the first frame update
void Start()
{
StartCoroutine("wait");
}
IEnumerator wait()
{
yield return new WaitForSeconds(36);
SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex + 1);
}
}
But the problem is not with my method of waiting for the end of the video, it's with the scene it loads. I can't move my character because all the references in the scripts are null. I have no idea what I did wrong.
Unity references should be stored in the scene file. Are you using any source control like git? If someone else did not push the changes to the .unityscene file or the .meta files for the associated scripts/prefabs, it might break the references.
I think we need some more information here.
If you referring to references (public variables) to Assets/GameObjects in the loaded scene, then you may have simply never placed the references in the first place. Unity's public variables should be saved within the Scene file and should always load with the Scene.
Double check to see if the references are actually null. Are you using an animation when you load into the Scene? This can keep you from being able to manual move anything. For example, if you animate the character when loading the scene, it could get stuck in the animation clip and you won't be able to move it.
Lastly, if you are referring to references created within the code to other scripts, objects, or variables, then these may break between scenes if you don't utilize 'DontDestroyOnLoad'.
Just spit-balling here, I need more information to correctly find the solution.

Unity: Camera.main is failing after I switch scenes

I am having a weird issue with some of my code, and I could really use some help.
I have a script attached to a gameobject that is unique to a particular scene, so anything within Start() will only run when that scene is loaded. In this script, I am accessing Camera.main, since I use settings attached to the camera gameobject (it may sound inefficient, but it is necessary for the style of game we are creating). Anyway, if I start from that scene directly in Unity, it works just fine, but if I start from my intro scene and then load into the aforementioned scene, I get this error:
MissingReferenceException: The object of type 'Camera' has been destroyed but you are still trying to access it.
Your script should either check if it is null or you should not destroy the object.
The weird thing is, I can Debug.Log(Camera.main) and it gives me the name of the camera. So Camera.main is not null, and it is not destroyed like it says in the error message. Here is my full script:
private void Start()
{
Debug.LogError(Camera.main);
gameManager = FindObjectOfType<GameManagerScript>();
if (Camera.main == null)
{
Debug.LogError("Camera.main is null");
}
else
{
gameManager.LoadMusic(Camera.main);
gameManager.LoadAmbient(Camera.main);
gameManager.FadeStereoPan(Camera.main.gameObject.GetComponent<SwipeActivator>().stereoPanInNode);
}
}
The three functions above are custom functions I wrote, but I don't know why they would be causing the issue, since they work if I start up the scene directly.
I wasn't having this issue for months, and then all of a sudden, I am getting this error, even though I didn't change any of the code. Any help would be greatly appreciated.
I believe your MainCamera is being destroyed between scenes. Set it up as "DontDestroyOnLoad()"
See this Unity question:
https://answers.unity.com/questions/430141/need-the-same-main-camera-for-multiple-scenes.html
Ah, I fixed it. I dove into my custom functions and found that I was accessing a camera variable that is set at the beginning of the game, and then on each camera transition, but it was failing since I was loading a scene and no transitions that scene had yet to be completed. I just had to reassign that variable in the script above so it was not pointing to the destroyed object in the previous scene. Thank you JiveTurkey for pointing me in the right direction!

How can I fix MissingReferenceException that is telling me game object is destroyed when I didn't destroy it?

I have code that looks like this.
public static Dictionary<int, Action> functionsMap;
void Function()
{
if (!isDictionaryInitialized)
{
functionsMap = new Dictionary<int, Action>();
functionsMap.Add(1, () => StartCoroutine(Function1()));
functionsMap.Add(1, () => StartCoroutine(Function2()));
}
}
void CheckForFunction()
{
var r = currentFunctionNumber;
if (functionsMap.TryGetValue(r, out currentAction)) { currentAction(); }
}
The code works fine when I start my program. However if I go to another scene and then return to it, I get this error.
"MissingReferenceException: The object of type 'ScriptName' has been
destroyed but you are still trying to access it. Your script should
either check if it is null or you should not destroy the object."
The problem is I have never destroyed the object. Initially I didn't have bool isDictionaryInitialized and I defined the new Dictionary outside of the Function because I thought the error was related to my program trying to access a Dictionary that was deleted after the scene was closed. I get the same problem with or without the bool, and regardless of where I define the Dictionary.
What is causing this, and what is the reason so I can avoid making the same mistake?
Edit: This question was marked as duplicate, but the link I don't believe applies to my situation, or if it does I don't understand how. It says static objects are not reloaded on a scene change, and the Dictionary is defined as a static object. I also tried changing it to non-static and the result is the same.
I have dozens of gameobjects in my code and don't have this issue with any other object, so I assume the problem is related to how the dictionary object is defined. Is there a way to keep the Dictionary object from being destroyed on scene change? I don't have it as a game object in the scene, it's just defined in the code itself as a public static Dictionary. Could someone tell me what I need to do differently please and thank you?
The problem might be caused by changing scenes because simply loading another scene would destroy all gameobject in the current scene.
According to Object.DontDestroyOnLoad.
The load of a new Scene destroys all current Scene objects.
To solve this, you can either use DontDestroyOnLoad function to mark the object you want to be kept before loading another scene, or using different way to load like LoadSceneMode.Additive without destroying the current scene.
First you are adding two Actions to your Dictionary but both with the same key.
functionsMap.Add(1, () => StartCoroutine(Function1()));
functionsMap.Add(1, () => StartCoroutine(Function2()));
wouldn't this overwrite the first entry?
Than your general problem is:
While functionsMap is static the two actions / one action you added are non-static (StartCoroutine always requires an instance of MonoBehaviour to run on) and therefore only available for the according component! (You talked about an Update method so this has to be a MonoBehaviour attached to a GameObject in your first scene.)
Now if you change the Scene, the GameObject holding that component is probably destroyed if you are not using DontDestroyOnLoad
The result is that your static Dictionary stays intact and filled meaning the entry with key = 1 still exists but the Value namely the added non-static Action is no longer available.
To avoid that you should add
DontDestroyOnLoad(this.gameObject);
either in Awake or Start to prevent the GameObject holding the component and thereby holding the Action that belongs to the reference(s) in the entry in your Dictionary.

Categories