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);
}
Related
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?
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 =)
I'm using the following script in a separate scene to keep my object "player"(it's a car) and load another scene.
DontDestroyOnLoad(transform.gameObject);
SceneManager.LoadSceneAsync(1);
but in my game, the state of car will be changed like its speed, indicator, the level of getting hit.
i wanna reset the status of this car to the original status when I click the button Restart Game.
Is there any way I can do to reset the car apart from destroying the car and switching back to the original scene to execute DontDestroyOnLoad again?
If I understood you right, you have public variables set to user defined values and you would like these to be the initial ones which was assigned by the user when you reload your initial scene. There are two solutions for this:
(a) Destroy the DontDestroyOnLoad gameObject so that when you reload the initial scene, a new instance of this will be created, hence your user defined values will be retained. So when you want to reload to initial scene from the end scene, create a script and add these:
Destroy (GameObject.Find("NameOfTheGameObject"));
SceneManager.LoadSceneAsync(1);
(b) Retain the car and its script, instead create another script and copy the initial values to the second script. For example, attach the below code to a new script and attach that script to your car gameObject:
Script 1 - DontDestroyOnLoad
public int Acceleration_Of_Car = 20;
public int Car_Force = 100;
public static ClassNameHere instance;
void Awake()
{
if(instance == null)
{
instance = this;
DontDestroyOnLoad(gameObject);
}else if(instance != this)
{
Destroy(gameObject);
}
}
Now create second script in the End Scene or even in the Initial Scene. Create variables that are the same as first script so that you do not get confused. Copy values from first script to second script, later apply values from second script to first script.
Script 2 - Copy and Apply
public int Acceleration_Of_Car;
public int Car_Force;
private Script1 script1; //Reference your first script here
public void Start()
{
script1 = (Script1) GameObject.FindObjectOfType(typeof(Script1)); //Call the first script
}
public void CopyValues()
{
Acceleration_Of_Car = script1.Acceleration_Of_Car;
Car_Force = script1.Car_Force;
}
public void ApplyValues()
{
script1.Acceleration_Of_Car = Acceleration_Of_Car;
script1.Car_Force = Car_Force;
}
I would say its better to use DontDestroyOnLoad() even for second script so that no other instance will be running :)
Destroy(GameObject.Find("GameObjectName"));
SceneManager.LoadSceneAsync(SceneIndex);
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;
}
}
In my game, I have two scene.
What I want to achieve is if user navigates from one scene to another, background audio specific to each should be played from start(audio length=0)
But all my efforts are in vain.
I tried using 'Pause' Method of audioSound
I tried
create a new game object and assign this scene background score to it and play
destroy gameObject created for another scene if there was any
But it doesn't give the result that I want.
I searched for finding how to play audioClip from start and stop other audioClip playing but didn't find any.
I know I'm not supposed to ask for code on stack overflow but if anyone has achieved this or has some pseudo code request you to provide it
I'm not sure I understand your question properly, since it seems the simplest scenario for background music.
If you really want a change of audioclip in every scene, let's say Scene A must play Clip A and Scene B must play Clip B, and both clips should be played as soon as a scene is loaded, you just need to create a game object in both scenes with an Audio Source component, with the Play On Awake flag active, and then just assign the appropriate clip for the scene (i.e.: assign Clip A in the Audio Clip field of the Audio Source component of Scene A game object, and do the same with Clip B for Scene B).
That's pretty much it.
If you looking at the detail code then you can try this code.
First : Make a script "SoundFxScript.cs" // You can modified as you want
Insert this code :
public class SoundFxScript : MonoBehaviour {
//Background Music
public AudioSource Scene1_Sound;
public AudioSource Scene2_Sound;
// Use this for initialization
void Start () {
PlayBackgroundMusic ();
}
// Update is called once per frame
void Update () {
}
public void PlayBackgroundMusic() {
if (UnityEngine.SceneManagement.SceneManager.GetActiveScene ().name == "Scene1") {
Scene1_SoundPlay();
} else if (UnityEngine.SceneManagement.SceneManager.GetActiveScene ().name == "Scene2") {
Scene2_SoundPlay();
}
}
public void Scene1_SoundPlay() {
Scene1_Sound.Play ();
Scene2_Sound.Stop ();
}
public void Scene2_SoundPlay() {
Scene1_Sound.Stop ();
Scene2_Sound.Play ();
}
// Step Fifth
public void LoadTheScene (string Scenename) {
UnityEngine.SceneManagement.SceneManager.LoadScene (Scenename);
sf.PlayBackgroundMusic ();
}
}
Second : Make Gameobject name = "SoundMusic" at the first scene and add component script SoundFxScript.cs. In gameobject "SoundMusic" you can add you background music for scene1 and scene2.
Third : Make a singleton file Singleton.cs
Insert this code :
public class Singleton : MonoBehaviour {
static Singleton instance = null;
void Start ()
{
if (instance != null)
{
Destroy(gameObject);
return;
}
DontDestroyOnLoad(gameObject);
instance = this;
}
}
Fourth : at gameobject "SoundMusic" add component script "Singleton.cs"
Fifth : How To Call In Another Scene (Load Scene). This method is inside SoundFxScript.cs
Example You have a method to call a load scene. Try this method :
Call it with : LoadTheScene("Scene2") // Call scene2
In here you can call your SoundFxscript.cs Component from any script you have.
Example :
SoundFxScript sf;
sf = GameObject.Find ("SoundMusic").GetComponent<SoundFxScript> ();
And you can use method LoadTheScene to load a new scene and the background music will RePlay again according to the what Scene is it.
That's All.