How would I keep a value between scenes? - c#

I've been trying for a little bit here to set a muteSound boolean in my SoundManager and then to switch to a new scene and keep the value previously stored in muteSound but I'm unsuccessful.
I tried the DontDestroyOnLoad(this); in hopes that it'd bring it to the new scene but for some reason it isn't.
Would any of you know what my problem could be? Am I using the correct function?
Thanks,

Some would say use static. That would work but avoid doing that as you will run into other problems. What you need is the PlayerPrefs. Save the value on exit. Read the value when game starts. You can do that in your SoundManager script.
bool muteSound = false;
//Load the value when game starts (default is false)
void Start()
{
muteSound = intToBool(PlayerPrefs.GetInt("muteSound", 0));
}
int boolToInt(bool val)
{
if (val)
return 1;
else
return 0;
}
bool intToBool(int val)
{
if (val != 0)
return true;
else
return false;
}
//Save on Exit
void OnDisable()
{
PlayerPrefs.SetInt("muteSound", boolToInt(muteSound));
}

You can actually pass a game object from scene to scene and all the values for classes assigned to that game object are maintained between scenes.
You basically just create an empty game object that stores your manager scripts and then pass the game object from scene to scene when you load them. This should preserve the values for the scripts attached to the empty game object.
EDIT: Fixed some spelling/grammatical errors.

Instead of typing
DontDestroyOnLoad(this); type
void Awake() {
DontDestroyOnLoad(gameObject);
}
If this doesn't work I suggest you check out the PlayerPrefs.
When the level is finished usePlayerPrefs.SetFloat("pref name", variable); or .SetInt(); or .SetBool();
And when the next level loads usevalue = PlayerPrefs.GetFloat("pref name"); or .GetInt(); or .GetBool();
This player prefs can also be used as saves because they are stored inside the computers registry.
If you use this for loading things such as money and you are loading it for the first time, do this:
if(PlayerPrefs.HasKey("money")) {
money = PlayerPrefs.GetFloat("money");
} else {
PlayerPrefs.SetFloat("money", 0);
money = 0;
}
If you don't do this the values can get messy a lot. I had big problems when I didn't use PlayerPrefs.HasKey();

Here was my solution:
public class SoundManager : MonoBehaviour {
public static SoundManager instance = null;
protected virtual void Awake()
{
if (instance == null)
instance = this;
else if (instance != this)
Destroy(gameObject);
DontDestroyOnLoad(gameObject);
}
}
While I thought my problem was because of my changing the class to a singleton the actual problem was that there was another class that needed to be a singleton.
Since the rest of the code wouldn't be mine to share, here is a tutorial which helped me understand what I was doing much better if anyone else ever runs into this type of problem.

Related

Why variable is not increased when collision is detected?

I have a float named kill and I want to increase it every time when a bullet collide with an enemy.
This is my code but it isn't working. It always remains zero.
public float kill = 0;
Text killed;
void OnCollisionEnter2D(Collision2D bullet)
{
if(bullet.collider.tag == "bullet")
{
Destroy(gameObject);
kill++;
}
}
void Update()
{
killed = GameObject.Find("killed").GetComponent<Text>();
killed.text = kill.ToString();
}
First, don't call GameObject.Find or GetComponent frequently, like in Update, as they are expensive operations. Instead, call them as few times as you can (such as calling them once in Start) and cache the result.
Also, your kill count should by all means be an integer, unless you have a very good reason it shouldn't be.
Also, when you call Destroy(gameObject) you're basically getting rid of all of the values in the fields of the component. - So you should be storing your kill count elsewhere. You could use a singleton for this, or a static class member, or even simply just using the text component to keep track of the value - you can use int.TryParse for that.
Finally, to set the inital value of the text, you can check if it's already a number and if it isn't, set it to zero. int.TryParse can also be used for that.
Altogether:
Text killed;
void Start()
{
killed = GameObject.Find("killed").GetComponent<Text>();
int curKilled;
if (!int.TryParse(killed.text, out curKilled))
{
// Does not already contain a number, set it to zero
killed.text = "0";
}
}
void OnCollisionEnter2D(Collision2D bullet)
{
if(bullet.collider.tag == "bullet")
{
int curKilled;
if (int.TryParse(killed.text, out curKilled))
{
killed.text = (curKilled+1).ToString();
}
else
{
// assume it should have been zero
killed.text = "1";
}
Destroy(gameObject);
}
}
The script is attached to enemy?Seems like it should be attached to the player object. Since the kill counter should belong to the player? If it is attached to the enemy object ,you also have to add to kills before destroying the game object

Unity: Audio source gets destroyed after scene reloads

I just introduced a pause option to my little testing game. I have audio in background (that plays throughout the whole game, even when the scene changes), so I've decided to make the music stop while the game is paused. For some reason, it works just fine UNTIL the game reloads/changes scene.
Then an error pops up
"The object of type 'AudioSource' has been destroyed."
Can anybody help? Also.. I thought it's a component, not an object! Might be both, I'm not sure.
How I make my music continuous:
void Awake()
{
if (instance != null)
{
Destroy(gameObject);
}
else
{
instance = this;
GameObject.DontDestroyOnLoad(gameObject);
}
}
My pause menu:
public static bool GameIsPaused = false;
public GameObject pauseMenuUI;
public AudioSource song;
void Update()
{
if (Input.GetKeyDown(KeyCode.Escape))
{
if (GameIsPaused)
{
Resume();
}
else
{
Pause();
}
}
}
void Resume()
{
pauseMenuUI.SetActive(false);
Time.timeScale = 1f;
GameIsPaused = false;
song.mute = false;
}
void Pause()
{
pauseMenuUI.SetActive(true);
Time.timeScale = 0f;
GameIsPaused = true;
song.mute = true;
}
Thank you!
Your problem could be with this line here
if (instance != null)
This is a singleton pattern and the purpose of this line is to prevent two instances of your singleton class from existing, which is a big no-no. This means if you try make a new object but one already exists, it will destroy the new object.
However, if Awake()is called again for any reason on the first singleton then it will see that the member instance is not null and promptly destroy itself.
A fix would be to change it to this:
if (instance != null && instance != this)
This would prevent your singleton from destroying itself.
Pretty sure it's your audio variable in the pause menu script. I think the pause menu dissociates itself from that AudioSrouce on a new scene load. To check this, go into game mode and load a new scene. When in the new scene, go over to the hierarchy and click the GameObject with the pause menu script on it. Now check to see if the public var AudioSource has anything associate with it.

Efficient way to not delete gameobject reference of singleton after scene reload

I have a singleton class and everytime I reload a scene the object reference I store in variable is destroyed
public class GameManager : MonoBehaviour
{
private void Awake()
{
if (instance == null)
{
instance = this;
}
else
{
Destroy(this.gameObject);
}
DontDestroyOnLoad(this);
Debug.Log("Scene reloaded");
}
void Start()
{
shapeSpawnerGO = GameObject.Find("SpawnShapesObj");
scoreGO = GameObject.Find("ScoreText");
lifeGo = GameObject.Find("LifeText");
}
public bool RedShapeStatus(int rcv_RedShapeIndex)
{
if (shapeSpawnerGO == null)
{
shapeSpawnerGO = GameObject.Find("SpawnShapesObj");
}
return shapeSpawnerGO.GetComponent<ShapeSpawnerChild>().listofRedShape[rcv_RedShapeIndex].activeSelf;
}
}
What I've done is check if shapeSpawnerGO is null then reference again the gameobject. And I think this is not efficient. Is there other way to solve this issue?
There are certainly other ways to accomplish this, but my official answer is "You're already doing it an acceptable way." You specifically said this:
"What I've done is check if shapeSpawnerGO is null then reference
again the gameobject. And I think this is not efficient. Is there
other way to solve this issue?"
You said the only time your code reinitializes the variables is whenever the scene reloads.That operation time doesn't even matter. You're literally talking about optimizing something completely irrelevant. Reinitializing scene data during a reload is what normal scene loading is all about.
The only exception to this would be if your idea of a scene reload is something you're doing every few seconds. If you're talking about the normal idea of a scene reload where you load the game scene once and then proceed to run the game for many minutes before a new scene reloads, then there's no reason to be worried about this code doing its normal initialization behavior.

C# Unity Script is there a way to speed up Instantiate

I have 2 scripts that talk to each other basically my spawn script grabs a boolean from a simple script. The boolean is set to true at the end of the if statement to show the light is on. Now I have a collider script that Instantiates 2 prefabs. But it's very slow. This game is supposed to be fast since it's being demoed to a lot of people is there a way to speed this up.
private bool Once = true;
public Transform Spawnpoint1;
public Transform Spawnpoint2;
public GameObject Prefab1;
public GameObject Prefab2;
void OnTriggerEnter2D(Collider2D collision)
{
if (Once == true)
{
Debug.Log("It's true");
if (LightOnOff.torchLit == true)
{
Debug.Log(" It's Lit");
Instantiate(Prefab1, Spawnpoint1.position, Spawnpoint1.rotation);
Instantiate(Prefab2, Spawnpoint2.position, Spawnpoint2.rotation);
Once = false;
}
}
}
If you are wondering the simple script looks like this
public static bool torchLit;
public void lightOn()
{
this.GetComponent<Light>().enabled = true;
torchLit = true;
}
You could use Object Pooling. It is common practise for fast Instantiate objects.
How it works?
Instantiate objects on scene loading.
Set objects acitve to falseand position to 0, 0, 0.
When you need object just set active to true and set position to desired position.
When object is no longer needed, instead of destroying, just again set acitve to falseand position to 0, 0, 0.
I really recommend you to watch that tutorial. Maybe do deeper research about it or... just use free Asset from Asset Store.

Particle System not working on Play()

I have a particle effect that I would like to trigger to play, and then stop. I'm sure this is an easy fix that I am over looking.
The particle can instantiate and play, but this obviously leaves overhead and particles that are active in the hierarchy when they don't need to be.
public void EmitFX(ParticleSystem particle)
{
Instantiate(particle, particlePos, Qauternion.identity)
}
I would like to use methods within ParticleSystem but am running into some problems. I have been using the manual and am still running into a block. I've googled this up and down, based on problems others had I changed my code to the following. It still does not work and is now a monster based on hacks other people found useful :/
public void EmitFX(ParticleSystem particle)
{
particle = particle.GetComponent<ParticleSystem>();
particle.transform.position = ballPos;
var em = particle.emission;
em.enabled = true;
particle.Play();
}
Here is a s/c of a particle in the Inspector.
First of all, not sure what the particle = particle.GetComponent<ParticleSystem>(); line is supposed to do? The particle variable is the ParticleSystem provide to your EmitFX() method so no need to call for this.
My guess is you have some references problems in your script (some variables referring to your prefab, then what you instantiate overrides this reference, ...) so I wrote you a "cleaner" version of your code (by merging your two scripts):
#region Attributes
[SerializeField]
private ParticleSystem particle;
private ParticleSystem generatedParticle;
#endregion
#region MonoBehaviour
protected void Start()
{
generatedParticle = null;
}
protected void Update()
{
if(Input.GetKeyDown(KeyCode.Space))
{
EmitFX(particle);
}
}
#endregion
public void EmitFX(ParticleSystem a_Particle)
{
if(generatedParticle == null)
{
generatedParticle = Instantiate(particle, particlePos, Qauternion.identity);
}
generatedParticle.transform.position = ballPos;
generatedParticle.Play();
// You can set a fixed duration here if your particle system is looping
// (I assumed it was not so I used the duration of the particle system to detect the end of it)
StartCoroutine(StopFXAfterDelay(generatedParticle.main.duration));
}
private IEnumerator StopFXAfterDelay(float a_Delay)
{
yield return new WaitForSeconds(a_Delay);
generatedParticle.Stop();
}
What it does is it store the instantiated particle in a variable so it can access it later and remember it has been generated. Also I added a coroutine to deactivate it at the end of the effect.
Hope this helps,
I know this is an old question but I am going to place here what worked for me since the problem persists in the later versions of Unity.
Instead of just calling: particle.Play() first stop the particle system like the code below.
particles.Stop();
if (particles.isStopped)
{
particles.Play();
}
Where 'particles' is a component of type "ParticleSystem".

Categories

Resources