Background music destroyed when build index is 0 - c#

I've got this background sound playing throughout the game.
The problem is that I want it to stop when the scene index is 0.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public class BackgroundMusic : MonoBehaviour
{
void Start()
{
GameObject[] objs = GameObject.FindGameObjectsWithTag("music");
if (objs.Length > 1)
{
Destroy(this.gameObject);
}
if (SceneManager.GetActiveScene().buildIndex != 0)
{
DontDestroyOnLoad(this.gameObject);
}
}
I've been using this script but still doesn't work
Any suggestions?

If you use DontDestroyOnLoad it is "forever" you don't have to do it for each scene again.
And then note that after that the Start is not called for every scene load again only the first time!
Rather use SceneManager.sceneLoaded like e.g.
public class BackgroundMusic : MonoBehaviour
{
private static BackgroundMusic _instace;
private void Awake()
{
if (_instance && _instance != this)
{
Destroy(this.gameObject);
return;
}
DontDestroyOnLoaddd(this.gameObject);
_instance = this;
SceneManager.sceneLoaded += OnSceneLoaded;
}
private void OnDestroy ()
{
SceneManager.sceneLoaded -= OnSceneLoaded;
}
private void OnSceneLoaded (Scene scene, LoadSceneMode mode)
{
if(scene.buildIndex == 0)
{
// TODO disable música
// NOTE: I wouldn't Destroy this though but rather only stop the music
}
else
{
// TODO enable the music
}
}
}

The SIMPLEST way to do this with success :
Check the name of the music and if the name is different, you destroy the Game Object, else you keep it playing and don't destroy it ( exemple : if you die when you are playing, the music continue to play and do not start from the beggining when the level restart, but if you go to the menu or change the level, the music will change without any problem and the old music is destroyed safely. )
Here my own code to solve this problem :
private void Awake() {
GameObject[] audioSource = GameObject.FindGameObjectsWithTag("Music");
if (audioSource.Length>1)
{
if (audioSource[0].GetComponent<AudioSource>().clip.name != _audioSource.clip.name)
{
Destroy(audioSource[0].gameObject);
} else {
Destroy(this.gameObject);
}
}
DontDestroyOnLoad(this.gameObject);
}

Related

Trying to get death and respawn working in my game

I can't get my respawning in my game to work, I have followed numerous tutorials, but have been failing to change one small aspect of them. I have a health variable in my game, and all the tutorials have the player die and respawn right upon touching the enemy. I want it to be so you take damage when touching an enemy, and when your health reaches 0 you are brought to a game over scene. I just can't seem to figure it out. I am new to game dev so I have tried my absolute hardest to solve the problem on my own, but to no avail. This is pretty much my last resort. I appreciate any help I can get.
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public class PlayerHealth : MonoBehaviour
{
public static PlayerHealth instance;
public int maxHealth;
public int health;
public int GameOver;
public event Action DamageTaken;
public int Health
{
get
{
return health;
}
}
// Start is called before the first frame update
void Awake()
{
if(instance == null)
{
instance = this;
}
}
private void Start()
{
health = maxHealth;
}
// Update is called once per frame
public void TakeDamage()
{
if(health <= 0)
{
return;
}
health -= 1;
if(DamageTaken != null)
{
DamageTaken();
}
}
public void heal()
{
if (health >= maxHealth)
{
return;
}
health += 1;
if (DamageTaken != null)
{
DamageTaken();
}
}
void OnCollisionEnter(Collision other)
{
if(other.gameObject.tag == "Enemy")
{
TakeDamage();
}
if(health <= 0)
{
SceneManager.LoadScene(GameOver);
}
}
}
I don't see anything wrong with your code specifically. It could be a different issue in the editor, which is usually the case. To debug try one of the following:
Check that the OnCollisionEnter is actually being triggered
Check that the Enemey has the Enemy Tag.
You can check 1 & 2 with the following code:
void OnCollisionEnter(Collision other)
{
// Check what the tag is
print(other.gameObject.tag);
...
}
There should now be messages when you run the program. Other things to check:
Make sure the player object has the PlayerHealth script attached to it.
Make sure that the player has a RigidBody
Make sure the enemy has a collider
Make sure the enemy collider is not set to Trigger

How can I destroy a game object forever?

So, I have this potion. When the player in my game comes in contact with the potion, I want to destroy the potion. However, if the player dies, the scene will reload and the potion will still be in the level. If the player collides with the potion, I don't want them to obtain it. They should only be able to collect the potion once.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class DestroyPotionForever : MonoBehaviour
{
public bool potionCollide = false;
// Start is called before the first frame update
void Start()
{
}
void OnTriggerEnter(){
if(potionCollide == false){
Destroy(gameObject);
bool potionCollide = true;
}
}
// Update is called once per frame
void Update()
{
}
}
But...this code doesn't work. Any help is appreciated.
A simple way would be to store whether you had picked up the potion in PlayerPrefs.
Then you could do something like:
void OnTriggerEnter()
{
if(PlayerPrefs.GetInt("GotPotion", 0) == 0)
{
// You didn't get the potion yet, so get it
Destroy(gameObject);
PlayerPrefs.SetInt("GotPotion", 1); // got the potion
}
}
Then wherever you spawn your potion, you could have:
if(PlayerPrefs.GetInt("GotPotion", 0) == 1)
{
// Got the potion already, so don't spawn the potion
}
Or, you if you put the potion in the scene directly, you could do:
void Start()
{
if(PlayerPrefs.GetInt("GotPotion", 0) == 1)
{
// If the potion is already picked up, destroy it
Destroy(gameObject);
}
}
A much better way would be to write your own save system as Antnio Pedro Gonalves Ferreira suggested, but this will get you through the demo phase at least.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class DestroyPotionForever : MonoBehaviour
{
public bool potionCollide = false;
// Start is called before the first frame update
void Start()
{
}
void OnTriggerEnter(){
if(potionCollide == false){
Destroy(gameObject);
potionCollide = true;
}
}
// Update is called once per frame
void Update()
{
}
}
Just using global var. potionCollide . You created local variable instead of using variable global.
Is your potion on the scene before you start the game? Whatever happens during runtime does not permanently change the scene, if you want the potion to disappear forever it cannot be on the hierarchy of the scene before the game runs. When you reload a scene it resets to the state it was before you ran the game.

How to fade out and destroy audio on load

I have got a basic script that allows the music to continue playing from scene 2 until scene 6, but I have been trying most of the day to get it where the audio starts to fade out and then is destroyed on scene 6, before loading into scene 7.
I have searched on here, google , youtube and even the Unity forums, but nothing works as all i am getting in my search is how to make the music continue
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public class Music : MonoBehaviour
{
// Start is called before the first frame update
public GameObject theManager;
public AudioSource music;
public string loadLevel;
public float fadeOutTime = 3f;
bool fading;
float fadePerSec;
void Awake()
{
DontDestroyOnLoad(theManager);
SceneManager.sceneLoaded += OnSceneLoaded;
}
void OnDestroy()
{
SceneManager.sceneLoaded -= OnSceneLoaded;
}
void Update()
{
if (fading)
{
music.volume = Mathf.MoveTowards(
music.volume, 0, fadePerSec * Time.deltaTime);
}
}
void OnSceneLoaded(Scene scene, LoadSceneMode mode)
{
if ((scene.buildIndex != 2) && (scene.buildIndex != 3) && (scene.buildIndex != 4) && (scene.buildIndex != 5) && (scene.buildIndex != 6))
{
fading = true;
fadePerSec = music.volume / fadeOutTime;
Destroy(theManager, fadeOutTime);
}
}
}
Here are 2 screenshots of my project
Project layout
Inspector of ScriptManager
Any help appreciated and thanks in advance
Main Issue: The GameObject with this script attached is detroyed
The ScriptManager GameObject having this script attached itself is destroyed when you switch a Scene! Therefore OnSceneLoaded is probably never even called at all.
Then I would not use the field public GameObject theManager; ... you already have the field public AudioSource music; so I would rather use music.gameObject whenever a GameObject reference is required. Just to avoid mistakes.
Finally I would not use the Update method for checking a bool flag constantly just for a one-time event. I would recommend to rather use a Coroutine:
so something like
public class Music : MonoBehaviour
{
public AudioSource music;
public string loadLevel;
public float fadeOutTime = 3f;
private static Music _instance;
void Awake()
{
DontDestroyOnLoad(music.gameObject);
// If necessary use a singleton pattern to make sure this exists only once
if(_instance)
{
Destroy(gameObject);
return;
}
_instance = this;
// Also don't destroy yourself!
DontDestroyOnLoad(gameObject);
// Just to be sure before adding a callback you should always remove it
// This is valid even if it wasn't added yet
// but it makes sure it is only added exactly once
SceneManager.sceneLoaded -= OnSceneLoaded;
SceneManager.sceneLoaded += OnSceneLoaded;
}
void OnDestroy()
{
SceneManager.sceneLoaded -= OnSceneLoaded;
}
private IEnumerator FadeOutAndDestroy()
{
var fadePerSec = music.volume / fadeOutTime;
while(music.volume > 0)
{
music.volume = Mathf.MoveTowards(music.volume, 0, fadePerSec * Time.deltaTime);
// yield says: Interupt the routine here, render this frame
// and continue from here in the next frame
// In other words: Coroutines are like small temporary Update methods
yield return null;
}
// Now the volume is 0
Destroy(music.gameObject);
// and since no longer needed also destroy this object
Destroy(gameObject);
}
void OnSceneLoaded(Scene scene, LoadSceneMode mode)
{
if (scene.buildIndex != 7) return;
StartCoroutine(FadeOutAndDestroy());
}
}

Why is OnLevelWasLoaded () called twice and why are my variable's values different in each call?

I currently have 2 scenes, house scene and overworld scene. Whenever the player enters the house scene, I will save the previous scene with currentArea and position with currentPosition in that scene the player was at. After exiting the house scene, the player will go back to where they last were by checking if the loaded scene's name is currentArea. However, I am currently encountering a problem as I notice that OnLevelWasLoaded() is called twice when a scene is loaded. Furthermore, currentArea will contain the previous scene's data in one call but be empty in another call.
I have tried putting the code in Awake() or Start().
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public class SceneTransition : MonoBehaviour
{
public static SceneTransition instance = null;
[SerializeField] string currentArea = "";
[SerializeField] Vector3 currentPosition;
void Awake()
{
if (instance == null)
instance = this;
else if (instance != this)
{
Destroy(gameObject);
return;
}
DontDestroyOnLoad(gameObject);
if (SceneManager.GetActiveScene().name == currentArea)
{
BasePlayer.instance.transform.position = currentPosition;
CameraController.instance.transform.position = new Vector3(currentPosition.x, currentPosition.y, CameraController.instance.transform.position.z);
}
else if (GameObject.FindGameObjectWithTag("StartingPoint"))
{
BasePlayer.instance.transform.position = GameObject.FindGameObjectWithTag("StartingPoint").transform.position;
CameraController.instance.transform.position = new Vector3(GameObject.FindGameObjectWithTag("StartingPoint").transform.position.x, GameObject.FindGameObjectWithTag("StartingPoint").transform.position.y, CameraController.instance.transform.position.z);
}
}
private void OnLevelWasLoaded(int level)
{
if (SceneManager.GetActiveScene().name == currentArea)
{
BasePlayer.instance.transform.position = currentPosition;
CameraController.instance.transform.position = new Vector3(currentPosition.x, currentPosition.y, CameraController.instance.transform.position.z);
}
else if (GameObject.FindGameObjectWithTag("StartingPoint"))
{
BasePlayer.instance.transform.position = GameObject.FindGameObjectWithTag("StartingPoint").transform.position;
CameraController.instance.transform.position = new Vector3(GameObject.FindGameObjectWithTag("StartingPoint").transform.position.x, GameObject.FindGameObjectWithTag("StartingPoint").transform.position.y, CameraController.instance.transform.position.z);
}
}
public void LoadArea(string nextArea)
{
SceneManager.LoadScene(nextArea);
}
public void LoadEvent(string nextScene)
{
SaveCurrentArea();
SceneManager.LoadScene(nextScene);
}
public void SaveCurrentArea()
{
currentArea = SceneManager.GetActiveScene().name;
currentPosition = BasePlayer.instance.transform.position;
}
public void LoadCurrentArea()
{
SceneManager.LoadScene(currentArea);
}
}
I expected currentArea to be the name of the previous area the player was at but it is sometimes null
You can try out the alternative approach described here
using UnityEngine.SceneManagement;
void OnEnable()
{
//Tell our 'OnLevelFinishedLoading' function to start listening for a scene change as soon as this script is enabled.
SceneManager.sceneLoaded += OnLevelFinishedLoading;
}
void OnDisable()
{
//Tell our 'OnLevelFinishedLoading' function to stop listening for a scene change as soon as this script is disabled. Remember to always have an unsubscription for every delegate you subscribe to!
SceneManager.sceneLoaded -= OnLevelFinishedLoading;
}
void OnLevelFinishedLoading(Scene scene, LoadSceneMode mode)
{
Debug.Log("Level Loaded");
Debug.Log(scene.name);
Debug.Log(mode);
}

enable onCollitionEnter by a variable

I have 2 script and i am try to make one trigger for the first script to enable other script trigger, i have try to investigate but i still stuck on my code.
my first code is
using UnityEngine;
using System.Collections;
public class endpoint10 : MonoBehaviour {
public static int IsColliderEnabled;
endpoint10.IsColliderEnabled = 0;
void OnTriggerEnter(Collider other)
{
if (IsColliderEnabled = 1) {
//do stuff here
// The switch statement checks what tag the other gameobject is, and reacts accordingly.
switch (other.gameObject.tag) {
case "end":
Debug.Log (other.gameObject.tag);
PlayerPrefs.SetInt ("moneyPref", scoreManager.money);
PlayerPrefs.SetInt ("scorePref", scoreManager.score);
ScoreSystem.level += 1;
PlayerPrefs.SetInt ("levelPref", ScoreSystem.level);
Debug.Log ("values stored");
Application.LoadLevel ("level_11");
break;
}
}
// Finally, this line destroys the gameObject the player collided with.
//Destroy(other.gameObject);
}
}
and my second code is
using UnityEngine;
using System.Collections;
public class trigguercubex : MonoBehaviour {
public GameObject[] objects;
void OnTriggerEnter(Collider other)
{
endpoint10.IsColliderEnabled = 1;
Debug.Log (other.gameObject.tag);
}
// Finally, this line destroys the gameObject the player collided with.
//Destroy(other.gameObject);
}
Do you have a game manager script?
You could use setters and getters
using UnityEngine;
using System.Collections;
public class GameManager : MonoBehaviour {
public static GameManager instance = null;
private bool triggerredOccurred = false;
public bool IsTriggerredOccurred {
get { return triggerredOccurred;}
}
public void TriggerredOccurred() {
triggerredOccurred = true;
}
void Awake(){
if (instance == null) { //check if an instance of Game Manager is created
instance = this; //if not create one
} else if (instance != this) {
Destroy(gameObject); //if already exists destroy the new one trying to be created
}
DontDestroyOnLoad(gameObject); //Unity function allows a game object to persist between scenes
}
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
}
}
In your endpoint class, when the collision was detected
GameManager.instance.TriggerOccurred ();
In your trigguercubex class
if (GameManager.instance.IsTriggerOccurred) {
do some stuff ();
}
I attach the GameManager script to my game's Main Camera

Categories