I decided to keep both scenes loaded in the hierarchy since it's easier to reference variables between them. How can I restart then a new game? - c#

In the Hierarchy I have two scenes when running the game first time.
The scene that contains all the gameplay objects all the objects are disabled.
The main menu scene objects are enabled.
Now this script is attached to the main menu and the function PlayGame is being called from a Play button On Click event :
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public class MainMenu : MonoBehaviour
{
public static bool sceneLoaded = false;
public void PlayGame()
{
SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex + 1);
SceneManager.LoadScene(0, LoadSceneMode.Additive);
SceneManager.sceneLoaded += SceneManager_sceneLoaded;
}
private void SceneManager_sceneLoaded(Scene arg0, LoadSceneMode arg1)
{
sceneLoaded = true;
}
public void QuitGame()
{
Application.Quit();
}
}
And this script is attached to one active gameobject on the gameplay scene and all it does is to check if I'm in the main menu or not and then to decide if to setactive or not all the gameplay objects :
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using UnityEngine;
public class ActiveScene : MonoBehaviour
{
public GameObject gameData;
// Update is called once per frame
void Update()
{
if(MainMenu.sceneLoaded == true)
{
gameData.SetActive(true);
MainMenu.sceneLoaded = false;
}
if(BackToMainMenu.loadedMainMenuScene == true)
{
gameData.SetActive(false);
BackToMainMenu.loadedMainMenuScene = false;
}
}
}
This is a screenshot of the Hierarchy :
You can see that the GameObject name Active Scene in the top scene is enabled gameobject while all the other gameobjects are disabled. On this Active Scene I attached the script ActiveScene.
This way when I click the Play button a new game start and when I hit the escape key it's getting back to the main menu.
The problem is when I hit the escape key and then click the Play button again it will keep load to the Hierarchy the main menu scene over and over again.
And this is after few times I hit the escape key and then started a new game over again by clicking the Play button :
This is the script to return to the main menu using the escape key. The script is attached to the Game Manager object in the game scene :
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public class BackToMainMenu : MonoBehaviour
{
public static bool loadedMainMenuScene = false;
private void Update()
{
if (Input.GetKeyDown(KeyCode.Escape))
{
loadedMainMenuScene = true;
}
}
}
I want to keep both scenes in the Hierarchy all the time since it's easier then to reference between variables in both scenes.
When both scenes in the Hierarchy it's also easier to restart a new game by re loading the gameplay scene.
The problem is when restarting a new game I have to set the flag bool sceneLoaded to false but then next time since it's false it will load the main menu scene to the Hierarchy over and over again.

Related

How do I keep my music going betwen scenes and not restart in unity

My problem is that my music restarts when my player dies, I tried the "DontDesteroyOnLoad" so the music keeps playing betwen the scenes. But when my player dies and goes to the previous scene the first generated music dosent stop, it keeps going, and after the player goes to the previous scene it starts again . And it runs at the same time as the first generated one does.
This is the code I have.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class DontDestroyAudio : MonoBehaviour
{
private void Awake()
{
DontDestroyOnLoad(transform.gameObject);
}
}
Create an emepty scene put all things that you want to manage during all the game cycles there. Like your music manager. Put the script with audio there and use DontDestroyOnLoad as you have written. At first, load that empty scene with your managers. And then load all your level scenes. This way you will only have one music manager for your entire game.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class AudioManager : MonoBehaviour
{
public static AudioManager instance;
[SerializeField]
private AudioSource audioSource;
[SerializeField]
private AudioClip audioClip;
private void Awake()
{
if (instance == null)
{
instance = this;
DontDestroyOnLoad(this.gameObject);
}
else
{
Destroy(this.gameObject);
}
}
public void PlayAudio()
{
audioSource.clip = audioClip;
audioSource.loop = true;
audioSource.Play();
}
public void StopAudio()
{
audioSource.Stop();
}
}
Function Call :
AudioManager.instance.PlayAudio();
AudioManager.instance.StopAudio();

Why the scene DontDestroyOnLoad is staying when switchig back to main menu?

The problem is that when I hit escape to go baco to main menu then I have the dont destroy objects original on the main menu and also the same objects in the DontDestroyOnLoad scene.
I have in the main menu scene 3 objects Player, Game Manager, Scene Loader that each one have attached a script DontDestroy :
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class DontDestroy : MonoBehaviour
{
private void Awake()
{
if (GameManager.backToMainMenu == false)
{
DontDestroyOnLoad(transform);
}
}
}
In the Game Manager object also attached another script :
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GameManager : MonoBehaviour
{
public SceneLoader sceneLoader;
public PlayerController playerController;
public CamMouseLook camMouseLook;
public static bool backToMainMenu = false;
public static bool togglePauseGame;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
if (Input.GetKeyDown(KeyCode.P))
{
PauseGame();
}
if (Input.GetKeyDown(KeyCode.Escape))
{
BackToMainMenu();
}
}
public void PauseGame()
{
togglePauseGame = !togglePauseGame;
if (togglePauseGame == true)
{
playerController.enabled = false;
camMouseLook.enabled = false;
Time.timeScale = 0f;
}
else
{
playerController.enabled = true;
camMouseLook.enabled = true;
Time.timeScale = 1f;
}
}
private void BackToMainMenu()
{
sceneLoader.LoadScene(0);
playerController.enabled = false;
camMouseLook.enabled = false;
Cursor.lockState = CursorLockMode.None;
Time.timeScale = 0f;
backToMainMenu = true;
}
}
When I press the escape key it's switching scenes between 1 and 0 and loading scene 0 the main menu.
But then the result is this :
So I pressed escape and back to main menu but the DontDestroyOnLoad also still loaded not removed so I have this 3 objects Player, Game Manager, Scene Loader duplicated.
If I will click on new game again the main menu scene will be remove so there will be no duplication but when back to main menu the DontDestroyOnLoad is stay.
That's exactly what DontDestroyOnLoad it's supposed to do, preserve a GameObject across multiple scenes. Your GameManager script starts with backToMainMenu stetted to false, which will trigger the execution of DontDestroyOnLoad on the Awake function the first time, but not the second (since, when you go back to the main menu, backToMainMenu is setted to true).
Yup, the objects will persists across loading of scenes. That means if there are some already in a scene to begin with there will be multiple ones after loading / reloading a scene.
I deal with by making an Init class & object in every scene, which checks a static variable, and instantiates the objects that are supposed to persist. That way you can start the game from every scene.
class Init {
public static bool hasInstantiatedController = false;
public GameObject GameController;
void Awake() {
if (!hasInstantiatedController) {
hasInstantiatedController = true;
Instantiate (GameController, transform.position, transform.rotation);
}
}
}

How can I reference back to objects/components of a parent object after destroyed it and re instantiated?

I have in the Hierarchy one scene and 3 parent objects. The 3 parent objects are :
Game Data (Where all the gameplay objects are childs of it)
Main Menu
Game Manager (That control on pausing/unpausing the game in some cases)
The idea the main goal is when the game start it's starting with the main menu then when clicking the start new game button it will start the game when hit the escape key it will be back to the main menu and again I can start a new game or resuming the game. I also did that while the game is running if you hit the P button it will pause/unpause the game.
First the script that is attached to the Game Manager :
Here I'm doing a reference for the Dialogue System, Depth Of Field, Main Menu And in the bottom I'm using find to find back the Depth Of Field and the Dialogue System since I'm destroying the Game Data object when starting a new game :
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public class GameController : MonoBehaviour
{
public GameObject dialogueSystem;
public DepthOfField depthOfField;
public static bool gamepaused = false;
public GameObject mainMenu;
private void Start()
{
Time.timeScale = 0f;
}
private void Update()
{
if (Input.GetKeyDown(KeyCode.P))
{
gamepaused = !gamepaused;
if (gamepaused)
{
Time.timeScale = 0f;
}
else
{
Time.timeScale = 1f;
}
}
if (Input.GetKeyDown(KeyCode.Escape))
{
if(depthOfField == null || dialogueSystem == null)
{
depthOfField = GameObject.Find("Player Camera").GetComponent<DepthOfField>();
dialogueSystem = GameObject.Find("Dialogue System");
}
depthOfField.DepthOfFieldInit(3f);
Time.timeScale = 0f;
dialogueSystem.SetActive(false);
mainMenu.SetActive(true);
Cursor.visible = true;
Cursor.lockState = CursorLockMode.None;
}
}
}
The problem is that the Dialogue System and Depth Of Field are in the Game Data and when I destroy the Game Data and instantiate the Game Data using a prefab over again to make a new game to start the variables in the GameController script lost reference and I need to use find. But using find so many times is a good idea ?
This script the next one is attached to the Main Menu :
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public class MainMenu : MonoBehaviour
{
public GameObject gameDataPrefab;
public GameObject gameData;
public GameObject mainMenu;
public void StartNewGame()
{
var gdata = GameObject.Find("Game Data");
if (gdata != null)
{
Destroy(gdata);
}
GameObject go = Instantiate(gameDataPrefab);
go.name = "Game Data";
mainMenu.SetActive(false);
GameController.gamepaused = false;
Time.timeScale = 1f;
}
public void ResumeGame()
{
}
public void QuitGame()
{
Application.Quit();
}
}
Here when I click the UI button to start a new game in the function StartNewGame I destroy the Game Data and create a new one. This make the variables in the GameController script to lost references.
The last script is attached to the Game Data this script I'm calling the method DepthOfFieldInit to make effect when I hit the escape key back to the Main Menu.
The problem is that some variables are lost references since the Game Data is destroyed. For example I'm getting null on :
playerLockMode.PlayerLockState(true, true);
Since playerLockMode is also on the Game Data that have been destroyed.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.PostProcessing;
public class DepthOfField : MonoBehaviour
{
public UnityEngine.GameObject player;
public PostProcessingProfile postProcessingProfile;
public bool dephOfFieldFinished = false;
public LockSystem playerLockMode;
private Animator playerAnimator;
private float clipLength;
private Coroutine depthOfFieldRoutineRef;
// Start is called before the first frame update
void Start()
{
if (depthOfFieldRoutineRef != null)
{
StopCoroutine(depthOfFieldRoutineRef);
}
playerAnimator = player.GetComponent<Animator>();
AnimationClip[] clips = playerAnimator.runtimeAnimatorController.animationClips;
foreach (AnimationClip clip in clips)
{
clipLength = clip.length;
}
DepthOfFieldInit(clipLength);
// Don't forget to set depthOfFieldRoutineRef to null again at the end of routine!
}
public void DepthOfFieldInit(float duration)
{
var depthOfField = postProcessingProfile.depthOfField.settings;
depthOfField.focalLength = 300;
StartCoroutine(changeValueOverTime(depthOfField.focalLength, 1, duration));
postProcessingProfile.depthOfField.settings = depthOfField;
}
public IEnumerator changeValueOverTime(float fromVal, float toVal, float duration)
{
playerLockMode.PlayerLockState(true, true);
float counter = 0f;
while (counter < duration)
{
var dof = postProcessingProfile.depthOfField.settings;
counter += Time.deltaTime;
float val = Mathf.Lerp(fromVal, toVal, counter / duration);
dof.focalLength = val;
postProcessingProfile.depthOfField.settings = dof;
yield return null;
}
playerAnimator.enabled = false;
dephOfFieldFinished = true;
depthOfFieldRoutineRef = null;
}
}
But I don't understand if I instantiate right away back the Game Data when starting a new game why the variables lost references ? And is there a better way to use Find at any place many times after the Game Data have been destroyed ? And maybe the whole code in the StartNewGame is wrong ?
My main goal is when clicking the StartNewGame start over a new game. When hit the escape key pause the game and go back to the main menu.
Here is a screenshot of my Hierarchy :

Set active scene before awake is called on scene being loaded additively

I have a loader script that loads a scene additively. The issue that I am having, is that when the new scene loads, it checks to see if it is the main active scene on Awake, if it is not then it loads the scene with this script and that scene reloads the one with the Awake check thus creating an infinite loop of scene loads.
With this script, when I try to set the main scene it says it hasn't finished loading yet. If I comment out the if statement, I get a scene load loop as mentioned above.
So, how/what can I do to set the active scene before the Awake is called on the loading scene's gameobject components?
Note: LoadScene() is triggered from a UnityEvent
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.Events;
using System.Collections;
public class GSLoadComplete : MonoBehaviour {
public LoadSceneMode mode;
[Tooltip("Set the scene as the main scene (Only when mode is set to Addictive)")]
public bool addictiveSetAsMain;
public UnityEvent onAddictiveLoadCompleted;
public void LoadScene(string sceneName) {
if (mode == LoadSceneMode.Additive) {
StartCoroutine(LoadSceneAddictive(sceneName));
} else {
SceneManager.LoadScene(sceneName, mode);
}
}
IEnumerator LoadSceneAddictive(string sceneName) {
AsyncOperation asyncLoad = SceneManager.LoadSceneAsync(sceneName, mode);
asyncLoad.allowSceneActivation = false;
// Wait until the asynchronous scene fully loads
while (!asyncLoad.isDone) {
if (asyncLoad.progress >= 0.9f) {
// Commenting this out creates an infinite load loop
if (addictiveSetAsMain) {
SceneManager.SetActiveScene(SceneManager.GetSceneByName(sceneName));
}
asyncLoad.allowSceneActivation = true;
}
yield return null;
}
onAddictiveLoadCompleted.Invoke();
}
}
This is the script that gets loaded on Awake:
using UnityEngine;
using UnityEngine.SceneManagement;
public class LoadMain : MonoBehaviour {
public string scene;
void Awake() {
if (SceneManager.GetActiveScene().name != scene) {
SceneManager.LoadScene(scene, LoadSceneMode.Single);
}
}
}

Change GameObject Component on InitializeOnLoad

I created script that i want to use as main script when my game launch. Now i want to access specific button text via that script and change it's text. I have problem because my text is not changing. Here is what i was trying:
static main()
{
Debug.Log("Up and running");
GameObject devButtonText = GameObject.Find("devButtonText");
Text text = devButtonText.GetComponent<Text>();
text.text = "Test";
}
Button is devButton and text is devButtonText
full script
using UnityEngine;
using UnityEditor;
using System.Collections;
using UnityEngine.UI;
[InitializeOnLoad]
public class main : MonoBehaviour {
static main()
{
Debug.Log("Up and running");
GameObject devButton = GameObject.Find("devButton");
Text text = devButton.GetComponentInChildren<Text>();
text.text = "Test";
}
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
}
}
I created script that i want to use as main script when my game launch.
The InitializeOnLoad attribute must be used to run a function when the Unity Editor starts, not the game. Every editor script won't run when you compile your game. Unity documentation
Sometimes, it is useful to be able to run some editor script code in a project as soon as Unity launches without requiring action from the user.
Instead, create an Empty GameObject and attach the following script :
using UnityEngine;
using System.Collections;
using UnityEngine.UI;
public class ChangeText : MonoBehaviour
{
private void Awake()
{
GameObject devButton = GameObject.Find("devButton");
Text text = devButton.GetComponentInChildren<Text>();
text.text = "Test";
}
}
Even better :
using UnityEngine;
using System.Collections;
using UnityEngine.UI;
public class ChangeText : MonoBehaviour
{
// Drag & Drop the desired Text component here
public Text TextToChange ;
// Write the new content of the Text component
public string NewText ;
private void Awake()
{
TextToChange.text = NewText;
}
}
The script will automatically be called when the scene starts.
When Initializing a MonoBehaviour
You should use Start, Awake or OnEnable.
You shouldn't use constructor, static constructor or field initialization (like public GameObject go = GameObject.Find("devButton");)
This should work:
using UnityEngine;
using UnityEditor;
using System.Collections;
using UnityEngine.UI;
public class main : MonoBehaviour {
void Awake () {
Debug.Log("Up and running");
GameObject devButton = GameObject.Find("devButton");
Text text = devButton.GetComponentInChildren<Text>();
text.text = "Test";
}
}
Edit
Given the name main I guess this is the starting point of your project so if your script is not attached to any game object then Start, Awake or OnEnable would not be called. In this case you should attach it to a game object and change the unity script execution order and bring the script to the earliest point.

Categories