I’m pretty sure this is something that is solved by script but for the longest time now I have been unable to get an explanation of how to solve this issue.
Basically I have a game object that has items assigned to it in the inspector like a UI slide, and Audio source reference, and some UI Text references, now when I change scenes these items gets lost which for the scene change is is OK because in the 2nd scene they are not needed and would only be in the way. BUT when I return to my main scene where they are needed they are missing. So there must be a way to re attache them? I have searched all over the place for some explanation of this but no one seems to explain it properly.
I didn’t encounter that yet, but you can check what scene was loaded in http://docs.unity3d.com/ScriptReference/MonoBehaviour.OnLevelWasLoaded.html and if it was original one, you set those variables to what you need them ‘back’ to. (back isn’t appropriate because old objects were destroyed and new ones were instantiated when scenes change).
To get variables either Find for each one or create another MonoBehaviour which only stores them within original scene(and gets destroyed when another scene loaded and loaded when this scene is loaded back) and get values from it. With this you can give that MonoBehaviour’s GameObject unique name and Find or GetComponent that one monobehaviour anytime. (Just be sure to keep that object always active).
Optimally, you shouldn’t even try to save links for objects that get destroyed between scenes. That’s a small design oversight you suffer from.
Thanks Teravisor,
Sounds like the kind of thing I’m after but can you give a little more information on how I would go about doing that? This is what the inspector looks like when I start of in scene one.
And this is what it looks like when I return to scene one.
using UnityEngine;
public class Storage : MonoBehaviour
{
public AudioSource source;
public Text temperatureText;
public GameObject lightSlider;
public Slider slider;
}
I don’t know: Slider and Text are UnityEngine.UI elements? Then add in beginning using UnityEngine.UI;
Attach it to always-active GameObject for example called “DelayedSending” and fill it with same values as Sending. That object must be in scene, but must not persist between scenes like Sending.
Then in your Sending script I’d do
void OnLevelWasLoaded (int level)
{
if(level == 0) //I don't know what number your base scene is, replace 0 with that. Or check something like Application.loadedLevelName
{
Storage stor = GameObject.Find("DelayedSending").GetComponent<Storage>();
//Variable names here are a guess. Replace them with how they are written in script.
source = stor.source;
temperatureText = stor.temperatureText;
lightSlider = stor.lightSlider;
slider = stor.slider;
}
}
However the best of the best ways is to separate Sending script into two: one is persistant and contains all logic that is scene-independent and another is scene-dependant and gets destroyed when scene is unloaded. That requires re-writing Sending(and possibly scripts that use Sending or init), and I can’t help you with that. What I suggested above is more like just patch-on-the-knee(whoever reads code after you will be quite unhappy with those patches).
I covered this in a tutorial once. I had two gui’s one for the game and one for the intro screen. I utilized DontDestroyOnLoad() to keep both of them. Since the game I was making didn’t have different interfaces than that it was easy. I did however, have to disable the Canvas for each. Since disabling the object had bad effects when I went to change scenes.
Thanks Teravisor,
I did make an empty game object and call it “DataHolder” which just has this in it.
using UnityEngine;
using System.Collections;
using UnityEngine.UI;
public class DataHolder : MonoBehaviour {
//UI Text Reference
public Text TemperatureText;
public GameObject lightSlider;
public Slider slider;
//Slider lightSlider;
public AudioSource source;
}
now the thing is, is that my “Sending” script on that “Init” game object sends and receives data from an Arduino, all of that is working fine, even when I return to the main scene the button inputs and outputs all work fine, and in the inspector I can see the data from the LDR and Temperature sensor are still being displayed in the inspector.
Like you can see here,
But now would I use the “DataHolder” script to somehow repopulate my “Init” game object with the Slider and UI text and AudioSource objects?
I’m fuzzy on how to do that.
Just to test I had to comment out the two references to the slider because in the console it gave me these errors:
Assets/Scripts/Sending.cs(116,25): error CS0029: Cannot implicitly convert type UnityEngine.GameObject' to UnityEngine.UI.Slider’
and
Assets/Scripts/Sending.cs(117,25): error CS0131: The left-hand side of an assignment must be a variable, a property or an indexer
Now my UI text for the Temperature sensor is repopulated back into the inspector when I return to scene 1 or I should say 2 technically and is working as it should so we are almost there.
Got it working, just had to pull out the one that was referencing it as a game object, apparently in my “Sending” script I did not even need that defined as it was already referencing the UI canvas slider
Thanks Teravisor FINALY it took someone like you to explain properly what it was I needed to do. Now it’s working perfectly,
Now you referred to this as a patch but it seems to work fine.
Is there a more appropriate way one should approach this sort of problem?
If you replace all links and usages from init/Sender variables to use DataHolder variables instead and remove those variables from Sender, it will be easier to understand what you wanted to do here from code. It’s not mandatory, but if you will ever hire another programmer, he might encounter that ‘patch’ and get stuck or create a bug. That’s why I reference it as ‘patch’ - it’s a bit of code that works, but hard to understand for someone other than original coder.
Oh hey Teravisor,
I wonder if I can pick your brain on another slight issue, in my main scene 2 I have a couple of timer panels one is basically a trip timer so it counts up.
and the other is a count down timer that I use to activate devices or set off a clock alarm when it reaches 0, would I be able to use this method to enable my timer to resume from where it left off when I return to scene 2?
I just realized that is another data persistence issue I had been having.
Of course you can. For same way you can add float field to Sender or a new MonoBehaviour with just that float and drag it between scenes. On Update() you can make timer write to that value and OnLevelWasLoaded() you read that value.
to Start(). In your case this should behave same way (timer doesn’t persist between scenes so it gets Start() call when scene is loaded).
That should remember timer’s time and has it finished or not. If you need to remember all those variables inside timer… You can just make it persistant between scenes. Or write something like I wrote for each variable. Or stop switching between scenes at all. Just disable one canvas and enable another. No scene load, no problems. I’m not going to write all those variables manually, I’m not maso.
Oh, and GameObject.Find(“init”.GetComponent() is quite slow if you decide to use it a lot (more than 10000 times per frame I guess) so then you should save its value in variable somewhere.
lol was it supposed to do that?
Is there a way to make it continue across scene changes?
I just have the one scene change which is for a music player that I figure probably needs it’s own scene if people load it up with a boat load of songs lol
Well, you could try making that timer itself persistant, but I see you have AudioClip and UI.Text that are not persistant. You’ll have to save those links and repeat all problems above…
Um… I have to ask one thing: Music player doesn’t load all songs at once. It just lists them then streams from disc.
Do you really think you can have enough songs on your discs to kill application from amount of buttons they create? And then those maximum of several megabytes of RAM you free up from another scene will save you? I see preemptive optimizations here. And attempts to cut uncuttable application logic
Basically what I want to say… Stop doing stupid things and just merge those scenes. I don’t know who you must be to have more than 10000000 songs (everything above will require you to start streaming song names… What you created will crash anyway unless it’s 64 bit PC)
Or if you don’t list all songs as buttons, there’s no problem at all.
Not sure what you mean by the songs being buttons. As far as I know the music player is just streaming songs from a folder. The music play could really use a loading bar or some UI text that displays the percentage of loading though.
So you would suggest making the music player part on my main scene too?
About the timer, should I handle it the same as I do the “init” object with the “Sending” script and return a reference to it’s references?
On my timer script I also have toggles that set certain devices. I set up a script called “ToggleSettings” and made it like this with the don’t destroy on it, It does carry the toggle settings back and forth between scene changes, but I’m not sure how to reference the information back to my main toggle setting on that timer script.
So much of this is still pretty new to me. But I’m getting the hang of a lot of it.
using UnityEngine;
using System.Collections;
using UnityEngine.UI;
public class ToggleSettings : MonoBehaviour {
public static ToggleSettings toggleSettings;
//Device Toggle States
public bool dev01State = false; //ScannerOn
public bool dev02State = false; //OilSlickOn
public bool dev03State = false; //FogLightsOn
public bool dev04State = false; //HeadLightsOn
public bool dev05State = false; //SurvModeOn
public bool dev06State = false; //GrapplingHookOn
public bool dev07State = false; //RearHatchOn
public bool dev08State = false; //MoviePlayerOn
public bool dev09State = false; //AutoPhoneOn
void Awake () {
if (toggleSettings == null) {
DontDestroyOnLoad (gameObject);
toggleSettings = this;
} else if (toggleSettings != this) {
Destroy (gameObject);
}
}
//Device One Toggle
public void ToogleDeviceOne() {
//Device Set to go off with timer Here
dev01State = !dev01State;
}
}
Can really cause anything bad to your program if you have only one scene? What exactly can go wrong? I just don’t see what can go wrong. So why several scenes?
You can do it like that. Or you can stop for a bit and think: why does timer need to report to UI? Why don’t you make it reverse: UI must take from timer? Like script on UI gets values from timer and changes UI accordingly while timer not knowing anything about UI at all. That’s the exact design problem you’ve been experiencing from the beginning.