Stop audio when is playing another one in Unity3D - c#

How can I stop one audio when is playing another one?
public AudioSource audioSource;
public AudioClip audioClip;
private string[,] levelWords;
//public static string glupiaZmienna;
public Text txt;
public void OnPointerClick(PointerEventData eventData)
{
Debug.Log(gameObject.name);
WordsLoader w = new WordsLoader();
levelWords = w.wLevelWords;
SetCategoryButton scb = new SetCategoryButton();
int a = scb.Kategoria;
zupa();
AudioSource audio = gameObject.AddComponent<AudioSource>();
audio.Stop();
audio.clip = (AudioClip)Resources.Load(a+ "/" + WordsLoader.language + "/Sounds/" + gameObject.name);
audio.Play();
// txt = gameObject.GetComponent<Text>();
}
public void zupa()
{ WordsLoader w = new WordsLoader();
SetCategoryButton scb = new SetCategoryButton();
int a = scb.Kategoria;
print(w.wHowManyWords);
for (int i = 0; i < w.wHowManyWords; i++)
{
print("jestem tu");
if (levelWords[i, 0] == gameObject.name)
{
txt = GameObject.Find("Text").GetComponent<Text>(); ;
txt.text = levelWords[i, 2];
}
}
}
This code is in a script which start playing a sound after click a button. I would like stop earlier started audio.

EDIT: Answer has to change a-lot due to comments left under it. It makes sense to overwrite everything.
You need a reference that points to all the AudioSource. Store every ClickAction instance in a List, in another script(PlayerManager). Before you play the audio, loop through that List and stop playing it if it is playing. After that, you can then play your audio.
Also, you currently call Resources.Load each time you want to play the Audio. This is not good in terms of performance. You should do this once in the Start() function, then play it when it is needed. Also, your gameObject.AddComponent<AudioSource>() code should only be called once too.
Your new ClickAction script:
public class ClickAction : MonoBehaviour
{
public AudioSource audioSource;
public AudioClip audioClip;
private string[,] levelWords;
//public static string glupiaZmienna;
public Text txt;
public AudioSource clickAudioSource;
PlayerManager playerManager = null;
void Start()
{
//Get PlayerManager from PlayerManager GameObject
playerManager = GameObject.Find("PlayerManager").GetComponent<PlayerManager>();
clickAudioSource = gameObject.AddComponent<AudioSource>();
clickAudioSource.clip = (AudioClip)Resources.Load(a + "/" + WordsLoader.language + "/Sounds/" + gameObject.name);
//Register this Script to the PlayerManager
playerManager.addClickAction(this);
}
public void OnPointerClick(PointerEventData eventData)
{
Debug.Log(gameObject.name);
WordsLoader w = new WordsLoader();
levelWords = w.wLevelWords;
SetCategoryButton scb = new SetCategoryButton();
int a = scb.Kategoria;
zupa();
//Play Audio
playAudio();
// txt = gameObject.GetComponent<Text>();
}
private void playAudio()
{
playerManager.playAudio(clickAudioSource);
}
public void stopAudio()
{
//If not null and playing then stop audio
if (clickAudioSource != null && clickAudioSource.isPlaying)
{
clickAudioSource.Stop();
}
}
public void zupa()
{
WordsLoader w = new WordsLoader();
SetCategoryButton scb = new SetCategoryButton();
int a = scb.Kategoria;
print(w.wHowManyWords);
for (int i = 0; i < w.wHowManyWords; i++)
{
print("jestem tu");
if (levelWords[i, 0] == gameObject.name)
{
txt = GameObject.Find("Text").GetComponent<Text>(); ;
txt.text = levelWords[i, 2];
}
}
}
void OnDisable()
{
//Un-Register this Script from the PlayerManager when it is destroyed
playerManager.removeClickAction(this);
}
}
PlayerManager script. Create a GameObject called PlayerManager, then attach the script below to it:
public class PlayerManager : MonoBehaviour
{
List<ClickAction> clickActions = new List<ClickAction>();
public void addClickAction(ClickAction clickActionToAdd)
{
//Adds clickActionToAdd if it does not exist in the List already
if (!clickActions.Contains(clickActionToAdd))
{
clickActions.Add(clickActionToAdd);
}
}
public void removeClickAction(ClickAction clickActionToAdd)
{
//Removes clickActionToAdd if it exist in the List
if (clickActions.Contains(clickActionToAdd))
{
clickActions.Add(clickActionToAdd);
}
}
public bool clickActionExist(ClickAction clickActionToAdd)
{
//Removes clickActionToAdd if it exist in the List
return clickActions.Contains(clickActionToAdd);
}
public void playAudio(AudioSource audSource)
{
//Stop Other Audio
stopAllClickActionAudio();
//Now, play the one that was passed in
audSource.Play();
}
void stopAllClickActionAudio()
{
//Stop Audio on every ClickAction script
for (int i = 0; i < clickActions.Count; i++)
{
clickActions[i].stopAudio();
}
}
}
When the playAudio function is called, it will stop all audio from AudioSource that is stored in the List, then it will play the current AudioSource that is passed into it.

Related

Sound is not playing After Gameover Unity

I don't know why the sound is not playing when the game is over. I am able to play sounds on the game menu and when the game start but when the game is over the sound doesn't play.
I am sure I am making some mistake, but I am not able to find where i am doing wrong.
Any help will be appreciated.
Here is the code
public static GameManager instance;
public GameObject PlatformSpawner;
public GameObject GameTapTextPanel;
public GameObject GameIntroPanel;
public GameObject scoreManagerUI;
public bool gameStarted;
public bool gameEnd;
// Start is called before the first frame update
public Text scoreText;
public Text highScoreText;
int score = 0;
int highScore;
AudioSource audioSource;
public AudioClip[] GameMusic;
void Awake()
{
if (instance == null)
{
instance = this;
}
audioSource = GetComponent<AudioSource>();
}
void Start()
{
highScore = PlayerPrefs.GetInt("HighScore");
highScoreText.text = "Best Score : " + highScore;
}
// Update is called once per frame
void Update()
{
if (!gameStarted)
{
if (Input.GetMouseButtonDown(0))
{
GameStart();
audioSource.clip = GameMusic[1];
audioSource.Play();
}
}
}
public void GameStart()
{
gameStarted = true;
PlatformSpawner.SetActive(true);
StartCoroutine(ScoreUp()); // to update the score we need to add this code;
GameTapTextPanel.SetActive(false);
scoreManagerUI.SetActive(true);
GameIntroPanel.GetComponent<Animator>().Play("TwistTurnAnim");
}
public void GameOver()
{
audioSource.clip = GameMusic[3]; //Here is the problem the sound doesn't play
audioSource.Play();
gameStarted = false;
gameEnd = true;
PlatformSpawner.SetActive(false);
scoreManagerUI.SetActive(true);
GameIntroPanel.SetActive(false);
SaveHighScore();
Invoke("ReloadLevel",1f);
StopCoroutine(ScoreUp());
}
void ReloadLevel()
{
SceneManager.LoadScene("Game");
}
//this one is used to increase score per second of the game;
IEnumerator ScoreUp()
{
while (true)
{
yield return new WaitForSeconds(1.5f);
score ++;
scoreText.text = score.ToString();
}
}
public void IncrementScore()
{
audioSource.PlayOneShot(GameMusic[2],0.4f);
score += 4; //Bonus points for game
scoreText.text = score.ToString();
}
void SaveHighScore()
{
if (PlayerPrefs.HasKey("HighScore"))
{
if (score > PlayerPrefs.GetInt("HighScore"))
{
PlayerPrefs.SetInt("HighScore", score);
}
}
else
{
PlayerPrefs.SetInt("HighScore",score);
}
}
Is the audio source part of an object that is deleted upon the Game Over condition?
The scripting API mentions that AudioSource is attached to an object.
In your method, GameOver, you are calling Invoke("ReloadLevel",1f) which will reload the scene in 1.0 seconds. Your GameMusic[3] is not playing because it doesn't have a chance to play before the scene is reloaded.

Trying to find all of the objects in my scene by a custom tag script I have created, however i continue getting a null reference exception

The Tag script has been working for me so far as an alternate to the unity tags up to this point, allowing me to assign multiple tags to an object at once. Now I want to create a method that will get all of the objects in the scene, filter them by the tag, and then return it as an array. The null reference exception refers to line 41 of the Tag.cs script. How do I fix this?
Tags.cs file
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Tags : MonoBehaviour
{
public string[] startTags;
private string[] tags;
private void Start()
{
tags = startTags;
}
public bool FindTag(string search)
{
bool results = false;
for (int i = 0; i < tags.Length; i++)
{
if(search == tags[i])
{
results = true;
break;
}
}
return results;
}
//Find objects by custom script tags
//HERE IS WHERE THE METHOD IS CREATED
public static GameObject[] ObjectsByTag(string search)
{
//Get all objects in scene
GameObject[] allObjects = FindObjectsOfType<GameObject>();
GameObject[] storedObjects = new GameObject[allObjects.Length];
GameObject[] finalObjects;
//Filter
int count = 0;
for (int i = 0; i < allObjects.Length; i++)
{
if (allObjects[i].GetComponent<Tags>().FindTag(search)) //line 41
{
storedObjects[count] = allObjects[i];
count++;
}
}
//Assign final length
finalObjects = new GameObject[count];
//Reassign to final array
for (int i = 0; i < count; i++)
{
finalObjects[i] = storedObjects[i];
}
return finalObjects;
}
}
GameController.cs file (How it is being used
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GameController : MonoBehaviour
{
//SCREEN START
//Get Screen Size
private float sHeight;
private float sWidth;
//Intended Screen Size
readonly float iH = 695f;
readonly float iW = 1540f;
//Convert
private float cH;
private float cW;
public float ConvertedHeight => cH;
public float ConvertedWidth => cW;
//SCREEN END
//MOUSE CAM START
//mousePostion
private float mX;
private float mZ;
public float MouseX => mX;
public float MouseZ => mZ;
//MOUSE CAM END
//EnemySpeedModifier
private float esm;
public float ESM
{
get { return esm; }
set { esm = value; }
}
//GameOver
private bool gameOver = false;
public bool GameOver
{
get { return gameOver; }
set { gameOver = value; }
}
//game speed
public float speed;
/*
//projectile list
private GameObject[] projectiles;
public GameObject[] Projectiles()
{
return projectiles;
}
public void Projectiles(GameObject value)
{
GameObject[] tempArray = projectiles;
tempArray[projectiles.Length] = value;
projectiles = tempArray;
Debug.Log("Projectile Count: " + projectiles.Length);
}
*/
//HERE IS WHERE IT IS USED
public GameObject[] ProjectilesInScene
{
get
{
return Tags.ObjectsByTag("projectile");
}
}
// Start is called before the first frame update
void Start()
{
//CONVERT SCREEN SIZES START
sHeight = Screen.height;
sWidth = Screen.width;
cH = iH / sHeight;
cW = iW / sWidth;
//CONVERT SCREEN SIZES END
}
// Update is called once per frame
void Update()
{
if (gameOver)
{
speed /= 1 + 0.5f * Time.deltaTime;
}
//Update mose position
mX = Input.mousePosition.x;
mZ = Input.mousePosition.y;
}
}
It seems that not all of your GameObject objects have the Tags component. Per the GameObject.GetComponent documentation
Returns the component of Type type if the game object has one attached, null if it doesn't.
If you know that some objects won't have the Tags component, your line 41 can use a simple null conditional operator:
if (allObjects[i].GetComponent<Tags>()?.FindTag(search) == true)
{
...
}
Note the ? after GetComponent<Tags>().

How to read the data from AudioClip using PCMReaderCallback when the former is created by Microphone.Start in Unity?

How can I change the code below to use the PCMReaderCallback to read data from the audio clip created by Microphone.Start?
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class NewBehaviourScript : MonoBehaviour
{
private string m_deviceName = null;
private AudioClip m_audioClip;
void Start()
{
bool loop = true;
int lengthSec = 10;
int frequency = 44100;
m_audioClip = Microphone.Start(m_deviceName, loop, lengthSec, frequency);
}
void Stop()
{
Microphone.End(m_deviceName);
m_audioClip = null;
}
}
You cannot use the Microphone API with PCMReaderCallback. This is because PCMReaderCallback is registered by passing the PCMReaderCallback function to the AudioClip.Create function which returns a new AudioClip. Unity's Microphone.Start function returns AudioClip but it doesn't take PCMReaderCallback as argument.
If this is not a Microphone but a simple audio clip then you could with the example below:
public int lengthSamples = 400;
public int channels = 2;
public int frequency = 16000;
AudioSource adSource;
void Start()
{
adSource = GetComponent<AudioSource>();
AudioClip clip = AudioClip.Create("clip", lengthSamples, channels, frequency, true, OnAudioRead);
adSource.clip = clip;
adSource.Play();
}
//PCMReaderCallback callback
void OnAudioRead(float[] data)
{
}
If you just want to read the audio data from the Mic, you can still use OnAudioFilterRead or the AudioSource.clip.GetData function. Below is an example for OnAudioFilterRead.
string m_deviceName = null;
AudioSource adSource;
void Start()
{
adSource = GetComponent<AudioSource>();
bool loop = true;
int lengthSec = 10;
int frequency = 44100;
AudioClip clip = Microphone.Start(m_deviceName, loop, lengthSec, frequency);
adSource.clip = clip;
while (!(Microphone.GetPosition(null) > 0)) { }
adSource.Play();
}
void OnAudioFilterRead(float[] data, int channels)
{
}

Loading random scene by scene name works in editor but not in standalone app

I'm writing a manager script that stores a collection of strings in a List that are coming from an array of Object names. These objects are actually my game scenes that I'm dragging into a public array in the unity editor. From there I'm picking one of these strings in GetRandomScene() and then passing the string name through LoadScene() to SceneManager.LoadScene(sceneName, LoadSceneMode.Single);.
This script is attached to a blank scene (first scene in build settings) with the purpose of loading one of the scenes that I've dragged into the Object[] forestScenes array. This works fine in the editor, but it doesn't work when I build as a standalone app. What do I need to modify for this to function correctly?
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using UnityEngine.SceneManagement;
public class ForestGameManager : MonoBehaviour
{
public static ForestGameManager fgm = null; // create singleton
[Header("Network Prefab")]
public GameObject serverNetworkManagerPrefab;
[Header("Forest Scenes")]
public Object[] forestScenes;
private List<string> forestSceneNames;
private int randomSceneIndex;
private string randomSceneName;
[Header("Forest Animal Management")]
public int maxAnimalCount = 4;
public List<ForestAnimal> forestAnimals;
private void Awake()
{
StartCoroutine(InitForestGameManager());
StartCoroutine(InitServerNetworking());
}
private void Start()
{
forestAnimals = new List<ForestAnimal>();
forestSceneNames = new List<string>();
StartCoroutine(InitSceneNameList());
StartCoroutine(GetRandomScene());
}
private void LateUpdate()
{
// Destroy oldest animal gameobject based on maxAnimalCount
if (forestAnimals.Count >= maxAnimalCount)
{
GameObject toDestroy = forestAnimals[0].forestAnimalGO;
forestAnimals.RemoveAt(0);
Destroy(toDestroy);
}
}
private void OnEnable()
{
SceneManager.sceneLoaded += OnSceneLoaded;
}
private void OnDisable()
{
SceneManager.sceneLoaded -= OnSceneLoaded;
}
void OnSceneLoaded(Scene scene, LoadSceneMode mode)
{
// If all forest scenes have been used, re-initialize scene name list and pick the next random scene
if (forestSceneNames != null)
{
if (forestSceneNames.Count == 0)
{
StartCoroutine(InitSceneNameList());
StartCoroutine(GetRandomScene());
}
}
// Start scene change countdown timer
}
private IEnumerator InitSceneNameList()
{
// Initialize scene name list
for (int i = 0; i < forestScenes.Length; i++)
{
forestSceneNames.Add(forestScenes[i].name);
// Debug.Log("Scene " + forestSceneNames[i].ToString() + " added to string list.");
}
yield return new WaitForEndOfFrame();
}
private IEnumerator InitForestGameManager()
{
if (fgm == null)
fgm = this;
else if (fgm != null)
Destroy(gameObject);
DontDestroyOnLoad(gameObject);
yield return new WaitForEndOfFrame();
}
private IEnumerator InitServerNetworking()
{
Instantiate(serverNetworkManagerPrefab);
yield return new WaitForEndOfFrame();
}
private IEnumerator GetRandomScene()
{
// Get scene name from random pick
randomSceneName = forestSceneNames[Random.Range(0, forestSceneNames.Count)];
// Remove selected scene from list
forestSceneNames.Remove(randomSceneName);
yield return new WaitForEndOfFrame();
// Load selected scene
StartCoroutine(LoadScene(randomSceneName, 1.0f));
//Debug.Log("randomSceneName is " + randomSceneName);
for (int i = 0; i < forestSceneNames.Count; i++)
{
//Debug.Log("Scene " + forestSceneNames[i].ToString() + " is still left in the list");
}
}
private IEnumerator LoadScene(string sceneName, float loadDelay)
{
yield return new WaitForSeconds(loadDelay);
SceneManager.LoadScene(sceneName, LoadSceneMode.Single);
}
}

Unity C# 2d Breakout Clone, Null Reference Exception

Okay so before I begin, Yes I have looked online to find this answer. I've followed advice on multiple other questions, gone through the unity documentation, and done more than a few web searches and nothing I've found so far has solved the error. I'm sure someone will take one look at this and know immediately what's wrong, but as for me, I just can't find it.
Now that that's out of the way Here's the problem. I'm making a Breakout clone and I had everything done, everything working properly. I've got one static class that takes care of the scoring and score related variables, so that other scripts can access them easily. I wanted to practice some basic saving and loading with PlayerPrefs, so I added something for highscores. It's pretty much independent of the other classes, but once I finished that, I started getting a Null Reference Exception in a script that has been done for hours, and was working fine.
I appreciate any help you might have, and any tips for preventing this kind of error the next time around. Sorry It's such a long question.
Here's the full error:
NullReferenceException: Object reference not set to an instance of an object
MenuManager.ActivateLose () (at Assets/Scripts/MenuScripts/MenuManager.cs:31)
Scoring.CheckGameOver () (at Assets/Scripts/Scoring.cs:64)
Scoring.LifeLost () (at Assets/Scripts/Scoring.cs:51)
DeadZone.OnTriggerEnter2D (UnityEngine.Collider2D other) (at Assets/Scripts/DeadZone.cs:22)
And here are the three scripts listed in said error:
using UnityEngine;
using System.Collections;
public class DeadZone : MonoBehaviour
{
public GameObject ballPrefab;
public Transform paddleObj;
GameObject ball;
void Update ()
{
ball = GameObject.FindGameObjectWithTag("Ball");
}
void OnTriggerEnter2D(Collider2D other)
{
//if the object that entered the trigger is the ball
if(other.tag == "Ball")
{
Scoring.LifeLost();
//destroy it, and instantiate a new one above where the paddle currently is
Destroy(ball);
paddleObj.transform.position = new Vector2(0, -2.5f);
(Instantiate(ballPrefab, new Vector2(paddleObj.transform.position.x, paddleObj.transform.position.y + 0.3f), Quaternion.identity) as GameObject).transform.parent = paddleObj;
}
}
}
using UnityEngine;
using System.Collections;
public static class Scoring
{
public static GameObject scoreValue;
public static TextMesh scoreText;
public static int score;
static int multiplier = 0;
static int consecutiveBreaks = 0;
static int lives = 3;
static int totalBricks;
static int remainingBricks;
public static GameObject menuManagerObj;
public static MenuManager menuManager = new MenuManager();
static void Awake()
{
scoreValue = GameObject.FindGameObjectWithTag("Scoring");
scoreText = scoreValue.GetComponent<TextMesh>();
menuManagerObj = GameObject.FindGameObjectWithTag("MenuManager");
//menuManager = menuManagerObj.GetComponent<MenuManager>();
}
public static void BrickDestroyed()
{
if(scoreValue == null && scoreText == null)
{
scoreValue = GameObject.FindGameObjectWithTag("Scoring");
scoreText = scoreValue.GetComponent<TextMesh>();
}
remainingBricks--;
consecutiveBreaks++;
multiplier = 1 + (consecutiveBreaks % 5);
score += 10 * multiplier;
CheckGameOver();
scoreText.text = score + "";
}
public static void LifeLost()
{
consecutiveBreaks = 0;
multiplier = 1;
score -= 100;
lives--;
LivesDisplay.SetLives(lives);
CheckGameOver();
scoreText.text = score + "";
}
public static void SetBrickCount(int brickCount)
{
totalBricks = brickCount;
remainingBricks = totalBricks;
}
public static void CheckGameOver()
{
//lose condition
if(lives < 0) menuManager.ActivateLose();
//win condition
if(remainingBricks == 0) menuManager.ActivateWin();
}
}
using UnityEngine;
using System.Collections;
public class MenuManager : MonoBehaviour
{
public GameObject winMenu;
public GameObject loseMenu;
public GameObject pauseMenu;
public HighScoreManager highScores;
bool isGamePaused = true;
void Awake()
{
winMenu = GameObject.FindGameObjectWithTag("Win");
loseMenu = GameObject.FindGameObjectWithTag("Lose");
pauseMenu = GameObject.FindGameObjectWithTag("Pause");
}
public void ActivateWin()
{
Time.timeScale = 0f;
winMenu.transform.position = new Vector3(0, 0, -1);
highScores.CompareToHighScores(Scoring.score);
}
public void ActivateLose()
{
Time.timeScale = 0f;
loseMenu.transform.position = new Vector3(0, 0, -1);
}
void ActivatePause()
{
if(isGamePaused)
{
Time.timeScale = 0f;
pauseMenu.transform.position = new Vector3(0, 0, -1);
}
else
{
Time.timeScale = 1f;
pauseMenu.transform.position = new Vector3(35, 0, -1);
}
isGamePaused = !isGamePaused;
}
void Update()
{
if(Input.GetKeyDown(KeyCode.Escape))
{
ActivatePause();
}
}
}
The problem is that Scoring is not a MonoBehaviour so the Awake method never gets called. You can try to initialize the fields in a static constructor
static Scoring()
{
scoreValue = GameObject.FindGameObjectWithTag("Scoring");
scoreText = scoreValue.GetComponent<TextMesh>();
menuManagerObj = GameObject.FindGameObjectWithTag("MenuManager");
//menuManager = menuManagerObj.GetComponent<MenuManager>();
}
or make the Awake method public and call it from another MonoBehaviour.

Categories