DontDestroyOnLoad through scenes for Audio - c#

I can't change the volume of the music more than 1 time. The volume control is on a "Settings" scene, so when I move to this scene I can change the volume of the game sound, but when i go out of the scene and come back I can't. I guess that happens cause of my bad DontDestroyOnLoad usage.
Once I change the volume the DontDestroyOnLoad comes in so the volume doesn't change once you go out of the scene, but when you come back to Settings to change it back it doesn't work cause of the DontDestroyOnLoad.
How can I solve this so I can change it wherever I want?
public class soundCont : MonoBehaviour {
private static float musicVolume = 1f;
private static float SetVolumeFX = 1f;
private void Start()
{
musicVolume = Musicafondo.instance.musicSource.volume;
}
void Update ()
{
MusicBack.instance.musicSource.volume = musicVolume;
MusicBack.instance.efxSource.volume = SetVolumeFX;
MusicBack.instance.efxSourceEnemy.volume = SetVolumeFX;
DontDestroyOnLoad(this.gameObject);
}
public void SetVolume(float vol)
{
musicVolume = vol;
}
public void SetVolumeFx(float vol2)
{
SetVolumeFX = vol2;
}
}

Both the instance you're using for the volumes are don't destroy on load, so you do not need to store the last values of the volume on a field.
This code solve your issue:
private void Start()
{
DontDestroyOnLoad(this.gameObject);
MusicBack.instance.musicSource.volume = 1f;
MusicBack.instance.efxSource.volume = MusicBack.instance.efxSourceEnemy.volume = 1f;
}
public void SetVolume(float vol) { MusicBack.instance.musicSource.volume = 1f; }
public void SetVolumeFx(float vol2)
{
MusicBack.instance.efxSource.volume = MusicBack.instance.efxSourceEnemy.volume = vol2;
}
SetVolume will change the music volume and SetVolumeFx will change the volume of the sound effects.
Make also sure SetVolume and SetVolumeFx are called properly.

Related

How Can I Make This Debounce Script Work? Unity3D

This is my script, when I click the debounce works at first, but after the wait you can just spam click and shoot many bullets at once. How can I fix this? I am a beginner so any help will be nice :) I had to get rid of some because stack overflow wasn't happy
using Photon.Pun;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SingleShotGun : Gun
{
[SerializeField] Camera cam;
PhotonView PV;
public bool debounce = false;
public float debounce2;
public AudioSource audioSource;
public AudioClip audioClip;
private float timeStarted;
private float audioTime;
private void Start()
{
if (PV.IsMine)
{
audioSource.clip = audioClip;
float timeStarted = (float)PhotonNetwork.Time;
audioTime = 0;
}
}
private void Update()
{
if (PV.IsMine)
{
float audioTime = (float)PhotonNetwork.Time - timeStarted;
}
else
{
audioSource.time = audioTime;
}
}
void Awake()
{
PV = GetComponent<PhotonView>();
}
public override void Use()
{
if (debounce)
{
StartCoroutine(Deb());
return;
}
if (PV.IsMine)
{
audioSource.clip = audioClip;
float timeStarted = (float)PhotonNetwork.Time;
}
debounce = true;
Shoot();
}
private IEnumerator Deb()
{
Debug.Log("Debouncing");
yield return new WaitForSeconds(debounce2);
debounce = false;
}
}
I Tried to make a debounce script for unity3d, but it didn't work?
As a first note: in
float audioTime = (float)PhotonNetwork.Time - timeStarted;
you are creating a new local variable => the class field audioTime is not assigned in that case
same also for all occurrences of
float timeStarted = (float)PhotonNetwork.Time;
you want to remove the float in all three places in order to rather assign your class fields instead of over shadowing them with same named local variables, otherwise they will always have the default value 0.
Then within Use you are starting multiple concurrent routines that will all finish eventually and reset your debounce in unexpected moments. You probably rather want to wrap it in order to start only a single routine like
if(!debounce)
{
debounce = true;
Shoot();
StartCoroutine(Deb()):
}
Also again in general I would expect the entire Use should be wrapped in / start with
if (!PV.IsMine) return;
as it looks and sounds like none of those things should happen at all if this is not your gun ;)
And after setting
audioSource.clip = audioClip;
you also need to
audioSource.Play();

Unity Input.GetKeyDown() event frequently missed

I'm working on a test project to experiment with Rigidbody in Unity. I worked on horizontal movement and jump actions, but I have a problem. Input.GetKeyDown() seems to not catch my key down event most of the time. I tried looking at possible solutions that suggested catching key inputs in Update() and corresponding with Rigidbody interactions with FixedUpdate(). When I tried this, I saw little to no improvement. Here is the script I'm working on right now:
public class PlayerScript : MonoBehaviour
{
[SerializeField] private float jumpConstant = 5.0f;
[SerializeField] private int walkSpeed = 10;
private bool jumpDown;
private float horizontalInput;
private Rigidbody rbComponent;
// Start is called before the first frame update
void Start()
{
rbComponent = GetComponent<Rigidbody>();
}
// Update is called once per frame
void Update()
{
CheckIfJumpKeyPressed();
GetHorizontalInput();
}
void FixedUpdate()
{
JumpIfKeyPressed();
MoveHorizontal();
}
void CheckIfJumpKeyPressed()
{
jumpDown = Input.GetKeyDown(KeyCode.Space);
}
void JumpIfKeyPressed()
{
if (jumpDown)
{
jumpDown = false;
rbComponent.AddForce(Vector3.up * jumpConstant, ForceMode.VelocityChange);
Debug.Log("Jumped!");
}
}
void GetHorizontalInput()
{
horizontalInput = Input.GetAxis("Horizontal");
}
void MoveHorizontal()
{
rbComponent.velocity = new Vector3(horizontalInput * walkSpeed, rbComponent.velocity.y, 0);
}
}
Thank you in advance.
You are overwriting your input if an extra frame occurs between your input and the physics frame. You should make sure a lack of input does not overwrite a detected input:
void CheckIfJumpKeyPressed()
{
jumpDown |= Input.GetKeyDown(KeyCode.Space);
}
or, equivalently:
void CheckIfJumpKeyPressed()
{
if (!jumpDown)
{
jumpDown = Input.GetKeyDown(KeyCode.Space);
}
}
Or even:
void CheckIfJumpKeyPressed()
{
if (Input.GetKeyDown(KeyCode.Space))
{
jumpDown = true;
}
}
Whichever you find most readable.

my variable doesn't change even that in the log its say the variable change [unity]

I have 1 script to PlayerMovement and one for powerUp I the power-up code I reference player movement to change the speed and change the bool named timer to true and I write that in log and when I touch the paper the speed doesn't change and the timer don't turn to true but in the log, its say that is yes
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerMovement : MonoBehaviour
{
private float TargetPos;
public float Speed;
void Start()
{
}
// Update is called once per frame
void Update()
{
transform.position = new Vector2(TargetPos, transform.position.y);
}
public void right()
{
TargetPos = transform.position.x + Speed * Time.deltaTime;
}
public void left()
{
TargetPos = transform.position.x - Speed * Time.deltaTime;
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Powrups : MonoBehaviour
{
public PlayerMovement pm;
public float PowerUpActiveTime;
public float StartPowerUpActiveTime;
public float peperSpeed;
float NormalSpeed;
bool timer;
bool timerover;
private void OnTriggerEnter2D(Collider2D col)
{
if (col.name == "peper")
{
pm.Speed = peperSpeed;
timer = true;
Debug.Log("timerOn");
Debug.Log(pm.Speed);
Debug.Log(timer);
}
}
private void Update()
{
while(timer)
{
GameObject Pause = GameObject.Find("Pause");
PauseScript pausescript = Pause.GetComponent<PauseScript>();
if (!pausescript.pause)
{
PowerUpActiveTime -= Time.deltaTime;
if(PowerUpActiveTime <= 0 )
{
timerover = true;
}
if (timerover)
{
timer = false;
}
}
}
if (timerover)
{
PowerUpActiveTime = StartPowerUpActiveTime;
pm.Speed = NormalSpeed;
}
}
private void Start()
{
PowerUpActiveTime = StartPowerUpActiveTime;
timerover = false;
NormalSpeed = pm.Speed;
}
}
Your mistake is that while loop.
You are lucky that until now you probably have tested this always while not being in pause mode ;)
This while would completely freeze your app and the entire Unity Editor!
In general be extremely careful with while loops and nested conditions like here, where the exit condition might never be fulfilled!
What happens currently is that you are not in pause mode so this while loop gets activated and runs until timer is set to false .. completely within one single frame. That is the reason why to you it seems that the value is never true.
What you rather want anyway is that code block be executed once per frame.
And in particular in a frame based application like Unity also have some performance impacts in mind.
You shouldn't use Find and GetComponent repeatedly within Update but store and re-use the results.
So your code should rather be
// If possible already drag this in via the Inspector
[SerializeField] private PauseScript _pauseScript;
private void Start()
{
PowerUpActiveTime = StartPowerUpActiveTime;
timerover = false;
NormalSpeed = pm.Speed;
// Get this ONCE as fallback on runtime
if(!_pauseScript)
{
_pauseScript = GameObject.Find("Pause"). GetComponent<PauseScript>();
// Or simply use
//_pauseScript = FindObjectOfType<_pauseScript>();
}
}
private void Update()
{
if(timer)
{
if (!_pauseScript.pause)
{
PowerUpActiveTime -= Time.deltaTime;
if(PowerUpActiveTime <= 0 )
{
timer = false:
PowerUpActiveTime = StartPowerUpActiveTime;
pm.Speed = NormalSpeed;
}
}
}
}
Besides all that, you should rather not let an external power-up control your player values. I would rather go the other way round and have your player object have a component which checks into which power-up items you run and react to it accordingly.
So your power-up itself would actually only be a trigger without any clue if or how exactly the player will be influenced by it.

can't reset specfic playerprefs in unity3d

I have made 2 codes to manage my interstitial ads by making the ads show every 5 mins when the player losses but the problem is that I tried to reset them when the player passes the 5 mins and press the button when he losses but it didn't work so how to reset the timer when the player presses the button?
this is the 1st code :
public int LastShownIntTime = 300;
void Start()
{
#if UNITY_ANDROID
Advertisement.Initialize(androidID);
#endif
}
public void Update()
{
LastShownIntTime = PlayerPrefs.GetInt("LastShownIntTime");
}
public void showInterstitial()
{
if (LastShownIntTime <=0)
{
showInterstitialwith5mint();
}
}
public void showInterstitialwith5mint()
{
Advertisement.Show("video");
PlayerPrefs.SetInt("LastShownIntTime", 300);
}
and the 2nd one :
public float LastShownIntTimefloat;
public int LastShownIntTime = 300;
void Start()
{
LastShownIntTime = PlayerPrefs.GetInt("LastShownIntTime");
LastShownIntTimefloat = LastShownIntTime;
}
public void Update()
{
LastShownIntTimefloat -= Time.deltaTime;
LastShownIntTime = (int)LastShownIntTimefloat;
PlayerPrefs.SetInt("LastShownIntTime", LastShownIntTime);
}
}
The main issue here:
You would have to reset the LastShownIntTimefloat in your script2!
Otherwise you simply continue overwriting it with new values reducing the value more and write it back to PlayerPrefs
→ the next time your script1 polls the value it is not reset but already overwritten by script2!
In general: You should not use PlayerPrefs in order to make two components communicate!
In your case here I wouldn't even separate the logic and bother with implementing the communication between them but rather merge them into one single component.
Then it is not necessary to read and write PlayerPrefs every frame but rather only on certain checkpoints like
Read once in Start
Write once in OnApplicationQuit
Write once in OnDestroy (This is for the case you e.g. switch Scene but don't quit the app)
Write once ever time your user loses (showInterstitial is called)
Write once when resetting the value after showing the advertisement
I would also simply directly use a float and GetFloat and SetFloat instead of converting it from and to an int.
public class MergedClass : MonoBehaviour
{
// Rather sue a FLOAT for time!
public float LastShownTime = 300;
void Start()
{
#if UNITY_ANDROID
Advertisement.Initialize(androidID);
#endif
// use 300 as default value if no PlayerPrefs found
LastShownTime = PlayerPrefs.GetFloat("LastShownTime", 300f);
}
public void Update()
{
if(LastShownTime > 0f) LastShownTime -= Time.deltaTime;
}
public void showInterstitial()
{
PlayerPrefs.SetFloat("LastShownTime", LastShownTime);
PlayerPrefs.Save();
if (LastShownTime <= 0f)
{
showInterstitialwith5mint();
}
}
public void showInterstitialwith5mint()
{
#if UNITY_ANDROID
Advertisement.Show("video");
#else
LastShownTime = 300f;
PlayerPrefs.SetFloat("LastShownTime", LastShownTime);
PlayerPrefs.Save();
}
private void OnApplicationQuit()
{
PlayerPrefs.SetFloat("LastShownTime", LastShownTime);
PlayerPrefs.Save();
}
private void OnDestroy()
{
PlayerPrefs.SetFloat("LastShownTime", LastShownTime);
PlayerPrefs.Save();
}
}

Start Game from the Last Scene Unity 3D

After closing the game and reopening it, I want to load the last scene, so the player can continue from the level he reached. I've tried using PlayerPrefs as you see in the code bellow, but the game crash on startup.
GameManager Script:
bool GameHasEnded = false;
public float RestartDelay = 2f;
public float NextLevelDelay = 5f;
int level_index;
private void Start()
{
level_index = PlayerPrefs.GetInt("Last_Level");
SceneManager.LoadScene(level_index);
}
public void CompleteLevel()
{
Invoke("NextLevel", NextLevelDelay);
level_index = level_index++;
PlayerPrefs.SetInt("Last_Level", level_index);
}
public void EndGame()
{
if (GameHasEnded == false)
{
GameHasEnded = true;
Invoke("Restart", RestartDelay);
}
}
void NextLevel()
{
SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex +1);
}
void Restart()
{
SceneManager.LoadScene(SceneManager.GetActiveScene().path);
}
}
All scenes are linked with GameManager, they all have the same code that load the next scene:
FindObjectOfType<GameManager>().CompleteLevel();
You need to specify a defaultValue for GetInt so that the first time it starts up, it can pick the appropriate scene to start at.
Also, you need to track whether or not you have already loaded at scene start, and only do this load if it hasn't yet happened. GameManager seems like it should be made into a singleton but since it's not, you can just make this loaded flag static, so that is common among all GameManager instances.
Altogether, this might look like this:
public readonly int defaultLastLevel = 1; // Set as appropriate
private static bool loaded = false;
void Start()
{
if (!loaded) {
loaded = true;
level_index = PlayerPrefs.GetInt("Last_Level", defaultLastLevel);
SceneManager.LoadScene(level_index);
}
}
Also, your CompleteLevel has some very strange code where you post-increment and assign in the same line:
level_index = level_index++;
This is extremely hard to read and actually doesn't do what you want it to do. You can do level_index = SceneManager.GetActiveScene().buildIndex + 1; instead.
Also, you need to Save changes you make to PlayerPrefs with PlayerPrefs.Save();
Altogether, this makes your CompleteLevel look like this:
public void CompleteLevel()
{
Invoke("NextLevel", NextLevelDelay);
level_index = SceneManager.GetActiveScene().buildIndex + 1; // use this instead
PlayerPrefs.SetInt("Last_Level", level_index);
PlayerPrefs.Save();
}

Categories

Resources