This is how I currently have my Sound Manager setup for a small RPG and I was just wondering if this was good practice.
What I was trying to go for was to have my two methods (PlayBGMusic and PlaySound) to be "public static" so I would not have to grab the GameObject by tag and the Script in almost all of my other scripts that want to play a sound. If I do make them "public static" then I would have to make my variables the same which takes away from placing anything in the inspector.
public class Sound_Manager : MonoBehaviour {
// Allow the user to decide if music should be on or off.
public bool backgroundMusicOn;
// Allow the user to decide if sound should be on or off.
public bool soundOn;
// The music volume.
[Range(0,1)]
public float musicVolume;
// The sound volume.
[Range(0,1)]
public float soundVolume;
// The Background music to be used for any manipulations.
private AudioSource _bgMusic;
// Play a background song.
public void PlayBGMusic(AudioSource music){
...
}
// Mute Current Background Song.
public void MuteUnMuteBGMusic(){
....
}
// Play a sound.
public void PlaySound(AudioClip sfx, Vector3 location){
...
}
}
Is this a proper way of handling something like this or is there another way that could be more simple?
I think you can't play BGmusic and sound in one GameObject,because every audio must play whith single audion source,this is my code of AudioManager.
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class AudioManager : MonoBehaviour
{
public AudioClip[] audioSources;
public GameObject audioPrefabSource;
public Dictionary<string,AudioClip> audioClips;
static GameObject audioPrefab;
static GameObject instance;
static AudioSource musicPlayer;
public static AudioManager audioManager;
Dictionary<string,Audio> aliveSounds;
AudioListener al;
void Awake ()
{
audioManager = this;
al = GetComponent<AudioListener> ();
audioClips = new Dictionary<string, AudioClip> ();
foreach (AudioClip a in audioSources) {
audioClips.Add (a.name, a);
}
instance = this.gameObject;
audioPrefab = audioPrefabSource;
musicPlayer = audio;
aliveSounds = new Dictionary<string, Audio> ();
//DontDestroyOnLoad(gameObject);
}
void Update ()
{
if (!GameSetting.hasMusic) {
musicPlayer.Pause ();
} else {
if (!musicPlayer.isPlaying) {
musicPlayer.Play ();
}
}
if (!GameSetting.hasSound && aliveSounds.Count > 0) {
foreach (Audio a in aliveSounds.Values) {
a.StopSound ();
}
aliveSounds.Clear ();
}
if (!al.enabled) {
al.enabled = true;
}
}
public static void PlaySoundOnce (string name)
{
if (!GameSetting.hasSound) {
return;
}
if (!audioManager.audioClips.ContainsKey (name)) {
return;
}
GameObject go = GameObject.Instantiate (audioPrefab) as GameObject;
go.transform.parent = instance.transform;
Audio a = go.GetComponent<Audio> ();
a.PlaySoundOnce (audioManager.audioClips [name]);
}
public static void PlayMusic (string name)
{
if (!GameSetting.hasMusic) {
return;
}
if (musicPlayer.clip == null || musicPlayer.clip.name != name) {
// musicPlayer.clip = audioManager.audioClips [name];
musicPlayer.clip = Resources.Load ("Audio/" + name, typeof(AudioClip)) as AudioClip;
musicPlayer.Stop ();
musicPlayer.loop = true;
musicPlayer.Play ();
} else {
musicPlayer.loop = true;
musicPlayer.Play ();
}
}
}
There is another class for Audio,it must be made to a audioprefab.
using UnityEngine;
using System.Collections;
public class Audio : MonoBehaviour
{
public void PlaySoundOnce (AudioClip audioClip)
{
StartCoroutine (PlaySoundCoroutine (audioClip));
}
IEnumerator PlaySoundCoroutine (AudioClip audioClip)
{
audio.PlayOneShot (audioClip);
yield return new WaitForSeconds (audioClip.length);
Destroy (gameObject);
}
public void PlaySoundLoop (AudioClip audioClip)
{
audio.clip = audioClip;
audio.loop = true;
audio.Play ();
}
public void StopSound ()
{
audio.Stop ();
Destroy (gameObject);
}
}
Like this,and I ignored audio's position,I used 2D sound.
I recommend turning the sound manager into a singleton class. In that way you can have all the properties in the inspector and use the singleton object to globally access all your properties, even destroying the gameobject completely when not needed .
Related
I'm trying to keep the player skin, even when reloading the scene, or moving on to a new one. The player object changes every scene.
The player skin is chosen in a pause menu, with three buttons. Each one of those buttons calls a function in the script below. I'm trying to call one of these functions, based on what value the PlayerPrefs int holds, and the function does get called; But throws the error MissingReferenceException: The object of type 'GameObject' has been destroyed but you are still trying to access it
Below is what i have already tried, but this throws an error on scene reload (death)
i don't know what i'm doing wrong here.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public class Pausemenu : MonoBehaviour
{
public static bool gameIsPaused = false;
public GameObject pauseMenuUI;
public Material BelgianMat;
public Material Ball2;
public Material Rainbow;
public GameObject Player;
// Update is called once per frame
void Update()
{
if (Input.GetKeyDown(KeyCode.Escape)) {
if (gameIsPaused) {
Resume();
} else {
Pause();
}
}
}
public void Resume(){
pauseMenuUI.SetActive(false);
Time.timeScale = 1f;
gameIsPaused = false;
}
void Pause() {
pauseMenuUI.SetActive(true);
Time.timeScale = 0f;
gameIsPaused = true;
}
public void LoadMenu() {
Time.timeScale = 1f;
gameIsPaused = false;
SceneManager.LoadScene("Menu");
}
public void QuitGame() {
Debug.Log("Quitting");
Application.Quit();
}
public void ApplyBelgian() {
Player.GetComponent<Renderer>().material = BelgianMat;
PlayerPrefs.SetInt("playerMat", 0);
}
public void ApplyBall2() {
Player.GetComponent<Renderer>().material = Ball2;
Debug.Log("Applied ball two");
PlayerPrefs.SetInt("playerMat", 1);
}
public void ApplyRainbow() {
Player.GetComponent<Renderer>().material = Rainbow;
PlayerPrefs.SetInt("playerMat", 2);
}
void OnEnable()
{
Debug.Log("OnEnable called");
SceneManager.sceneLoaded += OnSceneLoaded;
}
// called second
void OnSceneLoaded(Scene scene, LoadSceneMode mode)
{
Debug.Log("OnSceneLoaded: " + scene.name);
Debug.Log(mode);
if (PlayerPrefs.GetInt("playerMat") == 0) {
ApplyBelgian();
}
else if (PlayerPrefs.GetInt("playerMat") == 1) {
Debug.Log("gonna apply ball 2");
ApplyBall2();
}
else if (PlayerPrefs.GetInt("playerMat") == 2) {
ApplyRainbow();
}
}
}
I don't know why but it seems like the reference to the Player object may be broken after loading the scene. If this is the case, add a "Player" tag to your Player object and add this to the OnEnable function: Player = GameObject.FindWithTag("Player");
The title pretty much sums up my problem. I am using an AudioManager script to play one of the sounds from an array. It seems that all variables have been assigned correctly and I am able to access all the properties of AudioSource. However, when I call Play(), no sound is heard in the editor. I have also tested this by manually adding a single AudioSource component and calling AudioSource.Play() from my play method (commented line in Play()), and it works just fine. Below are the two scripts in question. I am getting no errors or warnings.
I'm using Unity 2019.3.3f
Update: So I have found the problem, the pitch variable of Sound class was uninitialized so it was defaulting to 0.
public class AudioManager : MonoBehaviour
{
public Sound[] sounds;
public static AudioManager instance;
void Awake()
{
DontDestroyOnLoad(gameObject);
if (instance == null) instance = this;
else
{
Destroy(gameObject);
return;
}
foreach (Sound s in sounds)
{
s.audioSource = gameObject.AddComponent<AudioSource>();
s.audioSource.clip = s.clip;
s.audioSource.volume = s.volume;
s.audioSource.pitch = s.pitch;
s.audioSource.playOnAwake = s.playOnAwake;
}
}
public void Play(string name)
{
Sound s = Array.Find(sounds, sound => sound.name == name);
s.audioSource.Play();
// gameObject.GetComponent<AudioSource>().Play();
}
}
[System.Serializable]
public class Sound
{
public string name;
public AudioClip clip;
[Range(0f, 1f)] public float volume;
[Range(-3f, 3f)] public float pitch;
public bool playOnAwake = false;
[HideInInspector] public AudioSource audioSource;
}
When the object is a DontDestroyOnLoad object, it loses all of its variables.
Your Solution:
(This is also a whole lot simpler and easy to understand and you also don't need another script)
public class AudioManager : MonoBehaviour
{
public static AudioSource;
public static AudioClip clip1, clip2;
void Awake()
{
GameObject[] obj = GameObject.FindGameObjectsWithTag(gameobject.tag);
if (obj.length > 1)
Destroy(gameobject);
DontDestroyOnLoad(gameObject);
//Make sure your audioClips are in a folder called "Resources"!!!
//Put the name of the audio clip in the ""
clip1 = Resources.Load<AudioClip>("clip1");
clip2 = Resources.Load<AudioClip>("clip2");
}
void Update()
{
//Make sure that the gameObject with this script has an audioSource component!
if (!audioSource)
audioSource = GetComponent<AudioSource>();
}
//now you can simply call this function from another script using AudioManager.Play("name")
public static void Play(string name)
{
switch (name)
{
case "clip1":
audioSource.PlayOneShot(clip1);
break;
case "clip2":
audioSource.PlayOneShot(clip2);
break;
}
}
}
I want to show the player name and the team name in multiple levels. I've created a gameobject with a singleton that calls servidor to load this variables. It works in the first level but not in the others. The gameobject persists but doesn't save the PlayerName and TeamName. How can I get it?
This is my code:
using UnityEngine;
using UnityEngine.UI;
using System.Collections;
public class GlobalControl2 : MonoBehaviour
{
private static GlobalControl2 instance = null;
private Text PlayerName;
private Text TeamName;
void Awake ()
{
if (instance == null)
{
instance = this;
DontDestroyOnLoad(this.gameObject);
}
else
{
Destroy(this.gameObject);
}
}
public void Start()
{
PlayerName = GameObject.Find ("PlayerName").GetComponent<Text> ();
TeamName = GameObject.Find ("TeamName").GetComponent<Text> ();
new GameSparks.Api.Requests.AccountDetailsRequest ()
.SetDurable(true)
.Send ((response) => {
PlayerName.text = response.DisplayName;
} );
new GameSparks.Api.Requests.GetMyTeamsRequest()
.SetDurable(true)
.Send(teamResp => {
if(!teamResp.HasErrors)
{
foreach(var teams in teamResp.Teams)
{
Debug.Log("Equipo: " + teams.TeamId);
TeamName.text = teams.TeamId;
}
}
} );
}
}
Currently you are trying to persist Scene specific objects over multiple scenes
The data type "Text" is a unity object.
Try using "String" instead, in the example bellow you will see that PersistentPlayerName and PersistentTeamName will be available in the next scene and PlayerName and TeamName will not. This is always the risk that multi-scene singletons have.
Please do take note, that this should not required if PlayerName and TeamName is on the same gameobject as GlobalControl2 (the one you call DontDestroyOnLoad on)
I am not suggesting that you do it exactly as it is typed bellow, but this should be enough to set you on the right path, if not, add a comment and i will explain further.
using UnityEngine;
using UnityEngine.UI;
using System.Collections;
public class GlobalControl2 : MonoBehaviour
{
private static GlobalControl2 instance = null;
private string PersistentPlayerName;
private string PersistentTeamName;
private Text PlayerName;
private Text TeamName;
void Awake ()
{
if (instance == null)
{
instance = this;
DontDestroyOnLoad(this.gameObject);
}
else
{
Destroy(this.gameObject);
}
}
public void Start()
{
PlayerName = GameObject.Find ("PlayerName").GetComponent<Text> ();
TeamName = GameObject.Find ("TeamName").GetComponent<Text> ();
new GameSparks.Api.Requests.AccountDetailsRequest ()
.SetDurable(true)
.Send ((response) => {
PlayerName.text = PersistentPlayerName = response.DisplayName;
} );
new GameSparks.Api.Requests.GetMyTeamsRequest()
.SetDurable(true)
.Send(teamResp => {
if(!teamResp.HasErrors)
{
foreach(var teams in teamResp.Teams)
{
Debug.Log("Equipo: " + teams.TeamId);
TeamName.text = PersistentTeamName = teams.TeamId;
}
}
} );
}
}
I have a list of enemys. so i want each enemy have their turn.
First of all :
Player turn --> enemys turn ("here each enemy move one by one untill the end then player move again"). how do i making some waiting time here and forcus on enemy turn?
Any help would be appreciated.
void Start()
{
// find list enemy
enemy = GameObject.FindGameObjectsWithTag("Enemy");
}
void Update()
{
//enemy turn reference to player. after move all enemy we change it to false to change the player turn.
if(StaticClass.enemyTurn == true )
{
for(int i=0;i<enemy.length;i++)
{
// how do i making some waiting time here and forcus on enemy turn?
EnemyTurn(i);
}
}
}
public void EnemyTurn(int id)
{
ChessMoveMent chessMoveScript = enemy[id].GetComponent<ChessMoveMent>();
chessMoveScript.ProcessMove();
id++;
if(id>=enemy.Length)
{
isMove = false;
}
}
I usually use StartCoroutine in this case.
Please try the below code:
public IEnumerator EnemyTurn(int id)
{
yield return null;
ChessMoveMent chessMoveScript = enemy[id].GetComponent<ChessMoveMent>();
chessMoveScript.ProcessMove();
id++;
if(id>=enemy.Length)
{
isMove = false;
}
}
When you want to use it, please use with "StartCoroutine()"
StartCoroutine(EnemyTurn(i));
More details here
You might have a coordinator, who tells the participants when it's their turn.
public class GameCoordinator : MonoBehaviour
{
public List<Participant> participants;
private int currentParticipantIdx = -1;
private Participant CurrentParticipant
{
get { return participants[currentParticipantIdx]; }
}
private void Start()
{
PlayNextMove();
}
private void PlayNextMove()
{
ProceedToNextParticipant();
CurrentParticipant.OnMoveCompleted += OnMoveCompleted;
CurrentParticipant.BeginMove();
}
private void OnMoveCompleted()
{
CurrentParticipant.OnMoveCompleted -= OnMoveCompleted;
StartCoroutine(PlayNextMoveIn(2.0f));
}
private IEnumerator PlayNextMoveIn(float countdown)
{
yield return new WaitForSeconds(countdown);
PlayNextMove();
}
private void ProceedToNextParticipant()
{
++currentParticipantIdx;
if (currentParticipantIdx == participants.Count)
currentParticipantIdx = 0;
}
}
public class Participant : MonoBehaviour
{
public event Action OnMoveCompleted;
public void BeginMove()
{
//
}
}
I have three characters and each of them has a camera attached to them.By default they are disabled but one.I made a character selector which is supposed to change them.I have a problem where I can move the selected one but the camera stays at the last one.
Here is the script:
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using UnityStandardAssets.Utility;
public class GameManageer : MonoBehaviour {
public Camera[] cams = new Camera[3];
public Character CurrentCharacter;
public List<Character> Characters = new List<Character>();
public List<Item> AllItems;
bool ShowCharWheel;
public int SelectedCharacter;
public int lastCharacter;
public static GameManageer Instance;
void Awake(){
Instance = this;
foreach (Character c in Characters){
c.Instance = Instantiate(c.PlayerPrefab, c.HomeSpawn.position, c.HomeSpawn.rotation) as GameObject;
c.Instance.GetComponent<PlayerController>().LocalCharacter = c;
}
ChangeCharacter(Characters[PlayerPrefs.GetInt("SelectedChar")]);
}
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
if (Input.GetKey (KeyCode.C)) {
ShowCharWheel = true;
} else {
ShowCharWheel = false;
}
}
void ChangeCharacter(Character c){
lastCharacter = SelectedCharacter;
SelectedCharacter = Characters.IndexOf (c);
cams [SelectedCharacter].gameObject.SetActive (true);
cams [lastCharacter].gameObject.SetActive (false);
CurrentCharacter = c;
Characters [lastCharacter].Instance.GetComponent<PlayerController> ().CanPlay = false;
Characters [SelectedCharacter].Instance.GetComponent<PlayerController> ().CanPlay = true;
PlayerPrefs.SetInt ("SelectedChar", SelectedCharacter);
}
void OnGUI(){
if (ShowCharWheel) {
GUILayout.BeginArea(new Rect(Screen.width - 64, Screen.height - 192,64,192));
foreach (Character c in Characters){
if (GUILayout.Button(c.Icon,GUILayout.Width(64),GUILayout.Height(64))){
ChangeCharacter(c);
}
}
GUILayout.EndArea();
}
}
}
[System.Serializable]
public class Character {
public string Name;
public Texture2D Icon;
public GameObject PlayerPrefab;
public GameObject Instance;
public Transform HomeSpawn;
}
[System.Serializable]
public class Item{
public string Name;
public Texture2D Icon;
public ItemInstance InstancePrefab;
}
This should do the job:
cams[SelectedCharacter].enabled = true;
cams[lastCharacter].enabled = false;
Use Depth:
foreach (Camera cam in cams)
{
cam.depth = cam == cams[SelectedCharacter] ? 10 : 0;
}
I think the real problem here though, is that you have more cameras in the scene which you have to manage as well, other then only the last and current selected character... in which case:
foreach (Camera cam in cams)
{
cam.SetActive(cam == cams[SelectedCharacter]);
}