Unity: How to access game objects that are not destroyed on load? - unity3d

I have a variable created in the main menu called "Keep" with tag "Keeper" and has an attached script that stops it from being destroyed...
void Awake()
{
DontDestroyOnLoad(this.gameObject);
}
I need to create a reference to the objects in another scene since the objects traces from and to other scenes
(Main menu --> customisation menu --> game)
In the other script from the other scene, I have tried GameObject.Find("Keep") or GameObject.FindWithTag("Keeper") and it comes up with NullReferenceException: not set to instance of object in the code...
private void Awake()
{
keeper = GameObject.FindWithTag("Keeping");
{...}
}
What do I do?

Related

Unity: How to switch scenes and when returning to the original scene, return player to the place they switched their scene

I have two scenes: Main Street & Building Scene
When the player is Main Street, if the player's trigger box touches the building and the player presses "q", the scene would switch to the Building Scene.
I want it so that when the player exits the Building Scene and returns to the Main Street Scene, the player is back to the position they entered the Building Scene that they entered from. Apologies in advance if this doesn't make sense.
sceneSwitchingScript:
public int buildingToLoad;
public Text InputText;
public movement player;
public Vector3 playerPrevPos;
void OnTriggerEnter2D(Collider2D col){
if(col.CompareTag("Player")){
InputText.text = ("[Q] to enter");
if(Input.GetKeyDown("q")){
if (gameObject.tag == "EntryPoint"){
playerPrevPos = new Vector3(player.transform.position.x, player.transform.position.y, player.transform.position.z);
}
//Debug.Log(gameObject.tag);
Application.LoadLevel(buildingToLoad);
}
}
}
void OnTriggerStay2D(Collider2D col){
if(col.CompareTag("Player")){
if(Input.GetKeyDown("q")){
//spawn = new Vector3(player.transform.position.x, player.transform.position.y, player.transform.position.z)
Application.LoadLevel(buildingToLoad);
if (gameObject.tag == "EntryPoint"){
playerPrevPos = new Vector3(player.transform.position.x, player.transform.position.y, player.transform.position.z);
}
}
}
}
void OnTriggerExit2D(Collider2D col){
if(col.CompareTag("Player")){
InputText.text = ("");
}
}
Setting the player's position when they exit the building
public switchScene ss;
void OnTriggerStay2D(Collider2D col){
if(Input.GetKeyDown("q")){
if(col.gameObject.CompareTag("ExitPoint")){
transform.position = ss.playerPrevPos;
}
}
}
However, these two scrips do not work and I'm not sure if this is related but when I make the player do the switch scene thing in-game, this error pops up:
NullReferenceException: Object reference not set to an instance of an object
movement.OnTriggerStay2D (UnityEngine.Collider2D col)
This error message mentions the error on this line:
transform.position = ss.playerPrevPos;
With Unity, the traditional loading of a scene was fairly destructive. There was the concept of setting a GameObject as safe by setting it as DontDestroyOnLoad, which, in a way, removed the object from the scene altogether, which protected it from scene loads.
Unity finally properly implemented multiscene editing and loading. It's essentially DDOL, but done properly.
Now you can actually have multiple scenes loaded at the same time. Now, what that allows you to do is to have a "Manager" scene that handles all of the objects that are common between scenes, and only load (and unload) the specific objects required for that individual scene.
You'd use it like this:
SceneManager.LoadScene("YourScene", LoadSceneMode.Additive);
That would load "YourScene" in ADDITION to the currently loaded scene. Likewise, removing a scene is:
SceneManager.UnloadScene("YourScene");
Now, if you have a Manager scene, you can include in your manager scene a script that holds data for each individual scene. As a hacky example, you might have:
public Vector3 InsideSceneLastPosition { get; set; }
Which you then assign to before loading your outside scene. When you load your inside scene again, you can read InsideSceneLastPosition again to reposition your character.
Here's the link to the LoadSceneAsync page at Unity.
There's more to it than that, for instance, you have to listen for the SceneManager.sceneLoaded event to know when you've actually loaded the next scene, so that you can reposition your GameObejcts. You can find information about that here.
You can see multiscene editing my simply dragging multiple scenes from the Project window into you Hierarchy window. All the scenes listed will additively work together. But you'll have to be careful though, as there's another "gotcha" in that you cant reference objects from one scene to another. You can call scripts cross scene, but you won't be able to drag a game object from one scene, into the object field of a component on a different scene. Don't worry, you'll get the hang on it =)

There is no 'Renderer' attached to the "robot2" game object, but a script is trying to access it

MissingComponentException: There is no 'Renderer' attached to the
"robot2" game object, but a script is trying to access it. You
probably need to add a Renderer to the game object "robot2". Or your
script needs to check if the component is attached before using it.
UnityEngine.Renderer.get_material () (at
<94c5f4c38cdc42d2b006f8badef04394>:0) ColorChange.Start () (at
Assets/ColorChange.cs:21)
I have a fbx robot2 in my Unity program, it have been imported as a Asset. I want change the color when program start, but I get this message. How could I render my fbx in Unity?
public Color colorStart = Color.red;
public Color colorEnd = Color.green;
public Renderer rend;
// Start is called before the first frame update
void Start()
{
rend = GetComponent<Renderer>();
rend.material.color = colorStart;
}
Basically the error message says it all:
GetComponent only returns a component attached to the same GameObject as your script.
But your robo2 has no mesh and therefore also no Renderer.
What you rather want to do in your case would be using GetComponentsInChildren which rather returns all according components attached to the GameObject itself your script is attached to or any child object nested under it recursively
void Start()
{
// pass true in order to also include disabled or inactive child Renderer
foreach(var rend in GetComponentsInChildren<Renderer>(true))
{
rend.material.color = colorStart;
}
}

Problem with dontDestroyOnLoad in different scene

I am making a game that has two scenes (menu scene and game scene). In the menu scene, I create an empty game object just for my music, which includes (audio source (music), button to mute the music, and my script.
Here's the script:
public class Music : MonoBehaviour
{
public static Music Instance;
public AudioSource mainMusic;
public GameObject musicOffImage;
// Keep The Muic Playing In Diffrent Scene
void Awake()
{
if (!Instance)
Instance = this;
else
Destroy(this.gameObject);
DontDestroyOnLoad(this.gameObject);
}
// Method Mute Button
public void MusicOnOff()
{
if (mainMusic.isPlaying)
{
mainMusic.Pause();
musicOffImage.SetActive(true);
}
else
{
mainMusic.UnPause();
musicOffImage.SetActive(false);
}
}
}
With that script, I can play music in different scenes without reloading the music, and the button is working too, but the problem is when I go to the game scene and I back up to the menu scene, somehow the button didn't work. I think it's about the Destroy game object, but I am not sure how to fix it. Any help would mean a lot to me. Thanks.
I assume that everything the Music scripts needs is a child of it so that it is always fine.
However, after Destroy of the instance from the new scene, your buttons from the new scene loose the reference to the Music instance.
Since you have a Singleton there anyway you could as well (ab)use it and have this attached to your button itself
public MusicButton : MonoBehaviour
{
public void MusicOn()
{
Music.Instance.MusicOnOff();
}
}
And reference that instead in your button.
Also the image could e.g. register itself to the Music.Instance like e.g.
public MusicImage : MonoBehaviour
{
private void Start()
{
Music.Instance.musicOffImage = gameObject;
gameObject.SetActive(Music.Instance.mainMusic.isPlaying);
}
}
Alternative
In your question you said all objects are child's of an empty object, however the only object that gets DontDestroyOnLoad is the Music one. The others will get destroyed and reloaded so all these references might get lost as well. You might probably rather DontDestroyOnLoad the entire empty object and only hide/show the button in certain scenes.

Get Children of New Scene

I have a GameManager script which manages loading scenes, putting characters in the scene, reading map information from game objects, and so on. The GameManager script is set to DontDestroyOnLoad.
I'm trying to figure out how to access objects within my new scene from GameManager after a new scene loads. I'm using the SceneManager.sceneLoaded event to run my "scene initialization" code. Here's the event handler:
void OnLevelFinishedLoading(Scene scene, LoadSceneMode mode)
{
// I want to access GameObjects within the newly loaded scene here
//
// SceneManager.GetActiveScene().GetRootGameObjects() returns
// System.ArgumentException: the scene is not loaded
// I want to do something like this
foreach (MapFeature mapFeature in rootObject.GetComponentsInChildren<MapFeature>())
{
// Do something
}
}
I'm want to get the root level GameObject of the new scene, and then use GetComponentInChildren on that root object in order to dynamically grab various components in the scene and store them in GameManager. However, SceneManager.GetActiveScene().GetRootGameObjects() returns System.ArgumentException: the scene is not loaded
How do I get objects from my newly loaded scene within my GameManager? If there's a better method than getting the new scene's root object and using that to get its children, I'm all ears.
This seems to be possible by a workaround, where the sceneLoaded Event starts a coroutine waiting for the next frame. Relevant snippet below.
For reference, I read this thread on unityforums, recently: https://forum.unity3d.com/threads/scenemanager-sceneloaded-event-when-fired-checking-scene-isloaded-false.429659/
void Awake () {
instance = this;
DontDestroyOnLoad (gameObject);
SceneManager.sceneLoaded += OnSceneLoadedWrapper;
}
void OnSceneLoadedWrapper(Scene scene, LoadSceneMode mode) {
StartCoroutine ("OnSceneLoaded");
}
IEnumerator OnSceneLoaded(){
yield return new WaitForEndOfFrame ();
Scene scene = SceneManager.GetActiveScene ();
int count = scene.GetRootGameObjects ().Length;
string name = scene.GetRootGameObjects ()[0].name;
Debug.LogFormat ("{0} root objects in Scene, first one called {1}", count, name);
}

UI Buttons Stop Working After I Load Another Scene

I have UI buttons to toggle sound. OnClick event is linked to this singleton GameObject. when I move to the next scene and come back to the main scene, I find OnClick object becomes missing while object is still there in the hierarchy! so what's the problem ?
using UnityEngine;
using System.Collections;
using UnityEngine.UI;
public class SoundsManagerController : MonoBehaviour {
static SoundsManagerController Instance = null;
void Awake()
{
// First we check if there are any other instances conflicting
if (Instance != null )
{
            // If that is the case, we destroy other instances
            Destroy(gameObject);
}
else {
// Here we save our singleton instance
Instance = this;
// Furthermore we make sure that we don't destroy between scenes (this is optional)
DontDestroyOnLoad(gameObject);
}
}
public void toggleSound(){
Instance.GetComponent<AudioSource> ().enabled = !Instance.GetComponent< AudioSource> ().enabled;
}
}
Use a separate canvas for the button and keep them as child of the singleton object.
If you already have a canvas in the scene, make sure the button is set as child of the canvas whenever your are going to a scene.
When these should be fixing your issue, you should consider not using a button with the singleton object. Get the singleton object in Start() in any scene and access sound toggle from the already placed button which is maintaining your UI layout.
Get the singleton object at start that holding the toggle method calling function.
SoundManagerController soundManager;
void Start ()
{
soundManager = GameObject.FindWithTag("audio_manager_tag").GetComponent <SoundManagerController>();
}
Call the below method from toggle button.
public void CallToggleMethod()
{
soundManager.toggleSound ();
}