Unity 2d Random Music start only on first time - c#

im programming an simple Unity 2d Game and I want to add some Background music, I have an script that random starts an Title from an playlist, but when im rejoining the MainMenu Scene where the Music GameObject is located, another Song starts playing (2 Songs Playing), but I want that only one song is playing, the song that was first. I saw when I rejoined the MainMenu scene that another music object where created.
here my code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class MusicRandom : MonoBehaviour
{
[SerializeField] AudioClip[] myAudioClips;
AudioSource audioSource;
void Start()
{
DontDestroyOnLoad(gameObject);
audioSource = gameObject.GetComponent<AudioSource> ();
}
AudioClip RandomClip()
{
int randomNumber = Random.Range(0,myAudioClips.Length);
AudioClip randomClip = myAudioClips[randomNumber];
return randomClip;
}
void Update()
{
if (!audioSource.isPlaying) {
audioSource.clip = RandomClip();
audioSource.Play();
}
}
}
I tried to add an value that start the "audioSource = gameObject.GetComponent ();" function only one time, but it doesn't worked.

I found an way that the GameObject not duplicate
private static GameObject instance;
void Start()
{
DontDestroyOnLoad(gameObject);
if (instance == null)
instance = gameObject;
else
Destroy(gameObject);
}

Related

Unity 2D: Making the Player Gameobject Reactivate after being turned off

I can't get the Player Gameobject to reappear. The player deactivates the moment the Timeline starts when they touch the box collider, but the player never reactivates. I have tried using Player.SetActive(true) in the coroutine and even using an Invoke method with no luck. Any ideas on how to fix this? Please help.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Playables;
public class TimelineTrigger : MonoBehaviour
{
// calling items for Unity
public PlayableDirector timeline;
public GameObject Player;
public GameObject CutsceneCollider;
public GameObject CutsceneMC;
// Start is called before the first frame update
void Start()
{
// calls the playable director and turns off the MC for the scene
timeline = timeline.GetComponent<PlayableDirector>();
CutsceneMC.SetActive(false);
}
void Update()
{
Player = GameObject.FindGameObjectWithTag("Player");
}
private void EnableAfterTimeline()
{
Player = GameObject.FindGameObjectWithTag("Player");
Player.SetActive(true);
}
public void OnTriggerEnter2D (Collider2D col)
{
if (col.CompareTag("Player"))
{
// plays the cutscene and starts the timer
timeline.Play();
Player.SetActive(false);
Invoke("EnableAfterTimeline", 18);
CutsceneMC.SetActive(true);
StartCoroutine(FinishCut());
}
IEnumerator FinishCut()
{
// once the cutscene is over using the duration, turns off the collider and the MC.
yield return new WaitForSeconds(17);
CutsceneMC.SetActive(false);
CutsceneCollider.SetActive(false);
}
}
}
The issue here is that coroutines in Unity can't be run on inactive GameObjects, so FinishCut never gets executed.
This can be worked around by having a separate MonoBehaviour in the scene to which the responsibility of running a coroutine can be off-loaded. This even makes it possible to start static coroutines from static methods.
using System.Collections;
using UnityEngine;
[AddComponentMenu("")] // Hide in the Add Component menu to avoid cluttering it
public class CoroutineHandler : MonoBehaviour
{
private static MonoBehaviour monoBehaviour;
private static MonoBehaviour MonoBehaviour
{
get
{
var gameObject = new GameObject("CoroutineHandler");
gameObject.hideFlags = HideFlags.HideAndDontSave; // hide in the hierarchy
DontDestroyOnLoad(gameObject); // have the object persist from one scene to the next
monoBehaviour = gameObject.AddComponent<CoroutineHandler>();
return monoBehaviour;
}
}
public static new Coroutine StartCoroutine(IEnumerator coroutine)
{
return MonoBehaviour.StartCoroutine(coroutine);
}
}
Then you just need to tweak your code a little bit to use this CoroutineHandler to run the coroutine instead of your inactive GameObject.
public void OnTriggerEnter2D (Collider2D col)
{
if (col.CompareTag("Player"))
{
// plays the cutscene and starts the timer
timeline.Play();
Player.SetActive(false);
Invoke("EnableAfterTimeline", 18);
CutsceneMC.SetActive(true);
CoroutineHandler.StartCoroutine(FinishCut()); // <- Changed
}
IEnumerator FinishCut()
{
// once the cutscene is over using the duration, turns off the collider and the MC.
yield return new WaitForSeconds(17);
CutsceneMC.SetActive(false);
CutsceneCollider.SetActive(false);
}
}
If you have only one Player instance and it's accessible from the start, you'd better set it once in the Start method.
void Start()
{
// calls the playable director and turns off the MC for the scene
timeline = timeline.GetComponent<PlayableDirector>();
CutsceneMC.SetActive(false);
Player = GameObject.FindGameObjectWithTag("Player");
}
void Update()
{
}
private void EnableAfterTimeline()
{
Player.SetActive(true);
}

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();

Unity3D Shooter: Using tags to switch level after killing all enemies

I am new to Unity and was trying, after some suggestions, to use tags to know the number of enemies i have in each level and move to the next scene right after eliminating all enemies. This is the script i use on enemy gameobjects. I've also tagged each of them with the "enemy" tag in unity inspector but it still doesn't work when i run the game. After killing all the enemies, it didnĀ“t change to next scene (Success!). Any ideas on what I'm doing wrong? Any other suggestions?
Thanks a lot for the help.
Enemies Script
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public class BadguyScript : MonoBehaviour
{
public GameObject[] enemies;
public int maxHealth;
public int curHealth;
private Animator myAnimator;
private bool isDead;
[SerializeField]
private float DespawnTime = 2.5f;
[SerializeField]
private string DeathAnimHash = "isDead";
void Start()
{
myAnimator = GetComponent<Animator>();
myAnimator.enabled =true;
myAnimator.SetBool (DeathAnimHash ,isDead);
maxHealth = 1;
curHealth = maxHealth;
}
void Update()
{
if (curHealth < 1)
{
isDead = true;
myAnimator.SetBool (DeathAnimHash ,isDead);
Destroy(gameObject,DespawnTime);
}
enemies = GameObject.FindGameObjectsWithTag("enemy"); // Checks if enemies are available with tag "Enemy".
if (enemies.Length == 0)
{
SceneManager.LoadScene("SucessScene"); // Load the scene with name "SucessScene"
}
}
void OnTriggerEnter2D(Collider2D col)
{
if (isDead)
return;
if (col.tag == "bullet")
{
curHealth -= 1;
Destroy(col.gameObject);
}
}
}
I would create a script holder gameobject for this and put a GameManager script inside it. And inside GameManager.cs which should be a singleton class you can have a property like this:
int _enemyNumber;
public int EnemyNumber{
get{
return _enemyNumber;
}
set{
_enemyNumber = value;
}
}
And when you need to change these values, use some functions you will create inside this game controller such as:
public void DecreaseEnemyCount(){
//do the logic here
}
public void SetEnemyCount(){
//do the logic here
}
Also you can find information about creating a singleton class here
You create a list with all enemies, its a good practice, cause you'll gain performance. But you're verifing if enemies.Lenght == 0, what will never occur, because before you are adding the gameObject in the list enemies = GameObject.FindGameObjectsWithTag("enemy");
In the start method, you can search for all enemies and add then in your array, and in the update or onTriggerEnter you remove it from your array and validate the array lenght. I think it'll be more easy.
Instead of adding the script to a new gameManager script attached to an empty game object cause now once all enemies are killed the script will not be in work but if added to an empty gameobject it will be working always.

Unity AudioSource unable to play clip

I am attempting to play a sound when a user drags a 2D gameobject.
The gameobject will have to play many sounds, so I am programmatically creating Audio Sources and assigning clips through the inspector.
public class Card: Monobehavior, IDraggable {
public AudioClip tokenGrabClip;
public AudioClip tokenReleaseClip;
public AudioSource tokenGrab;
public AudioSource tokenRelease;
public AudioSource TokenGrab {
get{ return tokenGrab; }
}
public virtual void Start() {
tokenGrab = AddAudio (tokenGrabClip, false, false, 1f);
tokenRelease = AddAudio (tokenReleaseClip, false, false, 1f);
}
public virtual AudioSource AddAudio(AudioClip clip, bool isLoop, bool isPlayAwake, float vol) {
AudioSource newAudio = gameObject.AddComponent<AudioSource>();
newAudio.clip = clip;
newAudio.loop = isLoop;
newAudio.playOnAwake = isPlayAwake;
newAudio.volume = vol;
return newAudio;
}
}
This will create two audio sources for every gameobject of type Card in my game.
Now in my game manager, I simply try to play a sound...
void CheckDragStart(GameObject go, Draggable d) {
if (go.GetComponent<Card> () != null) {
print("check drag start");
go.GetComponent<Card> ().TokenGrab.Play ();
}
}
No sound is played, but I am able to to see my console with the "check drag start message".
Any idea what I could be doing wrong? I can see audio sources assigned to my game objects with my methods in the first code block, but my audio clips are not being assigned to them...
This can't be that go GameObject, GetComponent<Card>() or TokenGrab is null. If any of them is null, you will get the NullException error. I am ruling those out.
Looking at your code, these are the possible reasons why your audio is not playing. Ordered from very likely to least.
1.Your AudioClip is not assigned from the Editor.
Check that both of these variables are assigned fom the Editor.
public AudioClip tokenGrabClip;
public AudioClip tokenReleaseClip;
This must be done for each Card script that is attached to any GameObject in the scene.
You won't get any error if they are not assigned or null. It simply won't play.
2.Your Card script and the GameObject it is attached to is destroyed after Play() is called. Try to comment anywhere you have the Destroy function. Try it again. If problem is solved you have to fix that by moving your AudioSource to an empty GameObject that does not need to be destroyed.
I am just putting up my suggestion here. I see that you don't want to loop the clips. So why don't you try something like this?
public AudioClip tokenGrabClip;
public AudioClip tokenReleaseClip;
AudioSource audioSrc;
void Start () {
audioSrc = GetComponent<AudioSource> ();
}
public void PlayTokenGrabClip () {
audioSrc.PlayOneShot (tokenGrabClip);
}
public void PlayTokenReleaseClip () {
audioSrc.PlayOneShot (tokenReleaseClip);
}
Add an AudioSource component to your GameObject. Assign the audio files (mp3, wav) to tokenGranClip and tokenReleaseClip through the editor. Then call the functions PlayTokenGrabClip () and PlayTokenReleaseClip () to play the respective sounds as and when required.
The problem came from not updating my prefabs. I was assigning AudioClips in my C# script publicly, however, this change was not immediately reflected in my prefabs. Rather than having to update many prefabs, I reorganized how my resources load on startup:
I define my audio clips with Resources.Load(...) as AudioClip; in Awake() and create AudioSource Components in Start(). From this, I am able to add AudioClips to my AudioSources via C#.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Card : MonoBehaviour, IDraggable {
public bool counter;
private AudioClip tokenGrabClip;
private AudioClip tokenReleaseClip;
private AudioSource tokenGrab;
private AudioSource tokenRelease;
public AudioSource TokenGrab {
get{ return tokenGrab; }
}
public AudioSource TokenRelease {
get{ return tokenRelease; }
}
public virtual void Start() {
tokenGrab = AddAudio (tokenGrabClip, false, false, 0.5f);
tokenRelease = AddAudio (tokenReleaseClip, false, false, 0.5f);
}
public virtual void Awake () {
tokenGrabClip = Resources.Load ("Audio/tokenGrab") as AudioClip;
tokenReleaseClip = Resources.Load ("Audio/tokenRelease") as AudioClip;
}
public virtual AudioSource AddAudio(AudioClip clip, bool isLoop, bool isPlayAwake, float vol) {
AudioSource newAudio = gameObject.AddComponent<AudioSource> () as AudioSource;
newAudio.clip = clip;
newAudio.loop = isLoop;
newAudio.playOnAwake = isPlayAwake;
newAudio.volume = vol;
return newAudio;
}
}

Audio not playing when ball enters the trigger

i have a game like the Rooltheball in unitys tutorials, and i want to play a sound when my ball hits the peaks, the thing is that i already tried everything, i basicly added a audioListener in my mainCamera, and added a audioSource and audioClip in the gameobject i want to detect the trigger, here is the code i did:
using UnityEngine;
using System.Collections;
[RequireComponent(typeof(AudioSource))]
public class colisaoPicos : MonoBehaviour {
Manager gameManager;
public AudioClip impact;
private AudioSource audio;
void Start()
{
gameManager = GameObject.Find ("GameController").GetComponent<Manager> ();
}
void OnTriggerEnter(Collider c)
{
if (c.gameObject.tag == "Player") {
AudioSource.PlayClipAtPoint (impact, transform.position);
gameManager.LifeDown();
}
}
}
One solution would be to add an audiosource on the ball, and make it play the clip each time you enter the trigger.
Just make the audio variable public, and drag the AudioSource into it in the inspector.
Second, but in my book, and uglier solution would be to make a gameobject spesifically for that purpose. Then put the gameobject at the spot, and play the sound.
But, as programmer said, you should check the trigger. Remember, one of the Game Objects has to contain a Rigidbody. (Is Kinematic can be active tho)
As my experience, the following code is always working:
public AudioSource soundToPlay;
void OnTriggerEnter(Collider c)
{
if (c.gameObject.tag == "Player") {
soundToPlay.Play ();
}
}
Do not try to use playOneShot, or play clip. Error happens.
Try this it will play the sound once if Trigger function is working in your case and do add AudioListener component with the trigger gameobject.
using UnityEngine;
using System.Collections;
[RequireComponent(typeof(AudioSource))]
public class colisaoPicos : MonoBehaviour {
Manager gameManager;
public AudioClip impact;
private AudioSource audio;
void Start()
{
audio=GetComponent<AudioSource>();
gameManager = GameObject.Find ("GameController").GetComponent<Manager();
}
void OnTriggerEnter(Collider c)
{
if (c.gameObject.tag == "Player") {
audio.PlayOneShoot(impact);
gameManager.LifeDown();
}
}
}

Categories