Playing multiple audio clips in unity - c#

There might be similar questions to this one but I couldn't find any useful.
I have a simple scrips which has a list of classes that contains audio clips, id, and event system individually for each one of those elements.
I need to make that audio clip fields to list and make them play by order until the last one then stop and fire event.
Here's my code for only one audio clip slot:
(Also i tried audio clip array but its just playing the first one on the audio clip array)
public class MainAudioManager : MonoBehaviour {
public List<soundInfo> currentsoundinfoList = new List<soundInfo>();
AudioSource audioSource;
soundInfo curentSoundInfo;
float audioLenght;
void Start () {
audioSource = gameObject.GetComponent<AudioSource> ();
}
public void SetAudioToPlay (int ID) {
for (int i = 0; i < currentsoundinfoList.Count; i++) {
if (currentsoundinfoList [i].ID == ID) {
curentSoundInfo = currentsoundinfoList[i];
audioSource.clip = curentSoundInfo.clipToPlay;
audioSource.Play ();
audioLenght = audioSource.clip.length;
StartCoroutine ("waitnigTillEbd");
return;
}
}
}
IEnumerator waitnigTillEbd () {
yield return new WaitForSeconds (audioLenght + 1f);
curentSoundInfo.eventOnSound.Invoke ();
}
[System.Serializable]
public class soundInfo {
public string name;
public int ID;
public AudioClip clipToPlay;
public UnityEvent eventOnSound;
}
}

alright i found the solution by my self. here is the final result for creating list of sound with event onstop attached to each one of them individually and also playable by ID from another event and classes ,for anyone who need it
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
[RequireComponent(typeof(AudioSource))]
public class MainAudioManager : MonoBehaviour {
public List<soundInfo> soundList = new List<soundInfo>();
AudioSource audioSource;
soundInfo curentSoundInfo;
float audioLenght;
void Start () {
audioSource = gameObject.GetComponent<AudioSource> ();
}
public void SetAudioToPlay (int ID) {
for (int i = 0; i < soundList.Count; i++) {
if (soundList [i].ID == ID) {
curentSoundInfo = soundList [i];
StartCoroutine(playSequencely());
return;
}
}
}
IEnumerator playSequencely () {
yield return null;
for (int cnt = 0; cnt < curentSoundInfo.clipsToPlay.Length; cnt++) {
audioSource.clip = curentSoundInfo.clipsToPlay [cnt];
audioSource.Play ();
while (audioSource.isPlaying) {
yield return null;
}
}
//Debug.Log ("Last Audio Is Playing");
curentSoundInfo.onStop.Invoke ();
}
}
[System.Serializable]
public class soundInfo {
public string name;
public int ID;
[TextArea (2, 8)] public string About;
public AudioClip[] clipsToPlay;
//public float delayBetween;
public UnityEvent onStop;
}

Related

How do i add multiple enemies to my unity wave system?

I have a wave script for my game and it works fine, however after a while I have realised it gets quite dull fighting against the same enemies over and over again, so I was wondering if I could make a list of some sort to store all enemies that will be in each wave.
Here is my script;
SpawnManager.cs
using System.Collections;
using UnityEngine;
[System.Serializable]
public class Wave
{
public int EnemiesPerWave;
public GameObject Enemy;
}
public class SpawnManager : MonoBehaviour
{
public Wave[] Waves; // class to hold information per wave
public Transform[] SpawnPoints;
public float TimeBetweenEnemies = 2f;
public GameObject HelpText;
public GameObject Shop;
public GameObject shopfx;
public Transform ShopL;
bool shopactive = false;
private int _totalEnemiesInCurrentWave;
private int _enemiesInWaveLeft;
private int _spawnedEnemies;
private int _currentWave;
private int _totalWaves;
void Start ()
{
_currentWave = -1; // avoid off by 1
_totalWaves = Waves.Length - 1; // adjust, because we're using 0 index
StartNextWave();
}
void Update()
{
if (_enemiesInWaveLeft <= 0 && Input.GetKeyDown(KeyCode.R))
{
StartNextWave();
}
}
void StartNextWave()
{
_currentWave++;
// win
if (_currentWave > _totalWaves)
{
return;
}
_totalEnemiesInCurrentWave = Waves[_currentWave].EnemiesPerWave;
_enemiesInWaveLeft = 0;
_spawnedEnemies = 0;
StartCoroutine(SpawnEnemies());
}
// Coroutine to spawn all of our enemies
IEnumerator SpawnEnemies()
{
GameObject enemy = Waves[_currentWave].Enemy;
while (_spawnedEnemies < _totalEnemiesInCurrentWave)
{
_spawnedEnemies++;
_enemiesInWaveLeft++;
int spawnPointIndex = Random.Range(0, SpawnPoints.Length);
// Create an instance of the enemy prefab at the randomly selected spawn point's
// position and rotation.
Instantiate(enemy, SpawnPoints[spawnPointIndex].position,
SpawnPoints[spawnPointIndex].rotation);
yield return new WaitForSeconds(TimeBetweenEnemies);
}
yield return null;
}
// called by an enemy when they're defeated
public void EnemyDefeated()
{
_enemiesInWaveLeft--;
// We start the next wave once we have spawned and defeated them all
if (_enemiesInWaveLeft == 0 && _spawnedEnemies == _totalEnemiesInCurrentWave)
{
HelpText.SetActive(true);
Invoke("SetFalse",5.0f);
Shop.SetActive(true);
Invoke("LateUpdate",1f);
Instantiate(shopfx, new Vector3(ShopL.transform.position.x, ShopL.transform.position.y, ShopL.transform.position.z), Quaternion.identity);
shopactive = true;
}
}
void SetFalse()
{
HelpText.SetActive(false);
}
void LateUpdate()
{
if(Input.GetKeyDown(KeyCode.R) && shopactive == true)
{
Shop.SetActive(false);
Instantiate(shopfx, new Vector3(Shop.transform.position.x, Shop.transform.position.y, Shop.transform.position.z), Quaternion.identity);
shopactive = false;
}
}
}

How can I start a conversation only once even if it's starting inside Update?

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ItemAction : MonoBehaviour
{
public camMouseLook mouselook;
public GameObject lockedRoomCamera;
public Camera playerCamera;
public GameObject navi;
public ConversationTrigger trigger;
public GameObject player;
private Vector3 playerposition;
private Quaternion playerrotation;
private bool torotate = false;
public void Init()
{
navi.transform.parent = null;
navi.transform.localScale = new Vector3(0.5f, 0.5f, 0.5f);
navi.transform.LookAt(lockedRoomCamera.transform);
PlayerController.disablePlayerController = true;
mouselook.enabled = false;
playerCamera.enabled = false;
lockedRoomCamera.SetActive(true);
torotate = true;
playerposition = player.transform.position;
playerrotation = player.transform.rotation;
}
private void Update()
{
if(torotate == true)
{
RotateRandom.RandomRotation(navi.transform);
PlayConversations.ConversationToPlay(3);
}
}
}
The problem is that it's calling this line many times :
PlayConversations.ConversationToPlay(3);
I don't mind it will be called many times but I want it to play for exmaple index 3 only once. If it started playing the index remember it started playing and played the index in this case 3 and don't play it again even if it will call this line in the Update over again.
This is the PlayConversations script :
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayConversations : MonoBehaviour
{
private static ConversationTrigger conversationTrigger;
private static PlayConversations instance;
private void Awake()
{
conversationTrigger = GetComponent<ConversationTrigger>();
instance = this;
}
public static void ConversationToPlay(int index)
{
ConversationTrigger.conversationsToPlay.Add(index);
instance.StartCoroutine(conversationTrigger.PlayConversations());
}
}
And the ConversationTrigger script :
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using UnityEditor;
using UnityEngine;
using UnityEditorInternal;
public class ConversationTrigger : MonoBehaviour
{
public List<Conversation> conversations = new List<Conversation>();
public static List<int> conversationsToPlay = new List<int>();
[HideInInspector]
public GameObject canvas;
[HideInInspector]
public bool conversationEnd;
[HideInInspector]
public static int conversationIndex;
private DialogueManager dialoguemanager;
private void Start()
{
conversationIndex = 0;
dialoguemanager = FindObjectOfType<DialogueManager>();
}
public IEnumerator PlayConversations()
{
conversationEnd = false;
var conversations = conversationsToPlay.ToArray(); // Copy the list
conversationsToPlay.Clear(); // Immediately clear the original list
for (int i = 0; i < conversations.Length; i++) // iterate over the array
{
// Now you also don't need to remove items anymore,
// since you already cleared the list
yield return StartCoroutine(PlayConversation(conversations[i]));
}
}
public IEnumerator PlayConversation(int index)
{
if (conversations.Count > 0 &&
conversations[index].Dialogues.Count > 0)
{
for (int i = 0; i < conversations[index].Dialogues.Count; i++)
{
if (dialoguemanager != null)
{
dialoguemanager.StartDialogue(conversations[index].Dialogues[i]);
}
while (DialogueManager.dialogueEnded == false)
{
yield return null;
}
}
conversationIndex = index;
conversationEnd = true;
canvas.SetActive(false);
Debug.Log("Conversation Ended");
}
}
public void SaveConversations()
{
string jsonTransform = JsonHelper.ToJson(conversations.ToArray(), true);
File.WriteAllText(#"d:\json.txt", jsonTransform);
}
public void LoadConversations()
{
string jsonTransform = File.ReadAllText(#"d:\json.txt");
conversations.Clear();
conversations.AddRange(JsonHelper.FromJson<Conversation>(jsonTransform));
}
}
Either to make inside the PlayConversations script or inside the ConversationTrigger that when it started playing a conversation don't play this conversation over again.
The problem is that each place in the game I'm playing a conversation like in this case I need to use flags to make it stop after start playing but inthis case for example while the conversation is playing I'm also rotating an object and that should be in the Update and be called many times :
RotateRandom.RandomRotation(navi.transform);
But the conversation should start only once.
Since I don't see a reason why to play the same conversation twice in the game then I want it to be playing only once without the need to use flags each place.

How to call array element that has bool value true unity C#

I have four buttons using the same prefab and holding 4 text elements from an array out of which only one is assigned bool value to true. I am trying to access that true element when any of false element is clicked. i want to highlight true element when the false element is clicked. can anyone please help me to achieve this functionality?
using simpleobjectpool
taking reference from unity quiz game tutorial
Thanks
Answer Button Script
public class AnswerButton : MonoBehaviour
{
public Text answerText;
private AnswerData answerData;
private GameController gameController;
private bool isCorrect;
void Start()
{
gameController = FindObjectOfType<GameController>();
}
public void Setup(AnswerData data)
{
answerData = data;
answerText.text = answerData.answerText;
}
public void HandleClick()
{
gameController.AnswerButtonClicked(answerData.isCorrect);
{
if (answerData.isCorrect)
{
}
if (!answerData.isCorrect)
{
}
}
Answer Data Script
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[System.Serializable]
public class AnswerData
{
public string answerText;
public bool isCorrect;
}
QuestionData Script
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[System.Serializable]
public class QuestionData
{
public string questionText;
public AnswerData[] answers;
}
Game Controller Script
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.SceneManagement;
using System;
public class GameController : MonoBehaviour
{
public Text questionText;
public Text scoreDisplayText;
public SimpleObjectPool answerButtonObjectPool;
public Transform answerButtonParent;
public GameObject questionPanel;
public GameObject roundOverPanel;
public GameObject levelsPanel;
private DataController dataController;
private RoundData currentRoundData;
private bool isRoundActive;
private float timeBeetweenQuestions = 3.0f;
private List<GameObject> answerButtonGameObjects = new List<GameObject>();
private QuestionData[] questionPool;
private int questionIndex;
private int qNumber = 0;
private List<int> questionIndexesChosen = new List<int>();
public int playerScore = 0;
public int totalQuestions;
private static int pointAddedForCorrectAnswer;
public AudioSource answerButtonClicked;
public AudioSource wrongAnswerClicked;
void Start ()
{
dataController = FindObjectOfType<DataController>();
currentRoundData = dataController.GetCurrentRoundData();
questionPool = currentRoundData.questions;
playerScore = 0;
questionIndex = 0;
scoreDisplayText.text = "Score: " + playerScore.ToString();
isRoundActive = true;
ShowQuestion();
}
private void ShowQuestion()
{
RemoveAnswerButtons();
QuestionData questionData = questionPool[questionIndex];
questionText.text = questionData.questionText;
for (int i = 0; i < questionData.answers.Length; i++)
{
GameObject answerButtonGameObject =
answerButtonObjectPool.GetObject();
answerButtonGameObjects.Add(answerButtonGameObject);
answerButtonGameObject.transform.SetParent(answerButtonParent);
AnswerButton answerButton =
answerButtonGameObject.GetComponent<AnswerButton>();
AnswerButton.Setup(questionData.answers[i]);
}
}
private void RemoveAnswerButtons()
{
while (answerButtonGameObjects.Count > 0)
{
answerButtonObjectPool.ReturnObject(answerButtonGameObjects[0]);
answerButtonGameObjects.RemoveAt(0);
}
}
IEnumerator TransitionToNextQuestion()
{
yield return new WaitForSeconds(timeBeetweenQuestions);
ShowQuestion();
}
IEnumerator WaitForFewSeconds()
{
yield return new WaitForSeconds(timeBeetweenQuestions);
EndRound();
}
IEnumerator ReturnCorrectButtonColor()
{
Debug.Log("im correct");
GetComponent<Button>().image.color = Color.green;
yield return new WaitForSeconds(seconds: 2.9f);
GetComponent<Button>().image.color = Color.white;
}
IEnumerator ReturnWrongButtonColor()
{
Debug.Log("im wrong");
GetComponent<Button>().image.color = Color.red;
yield return new WaitForSeconds(seconds: 2.9f);
GetComponent<Button>().image.color = Color.white;
}
public void AnswerButtonClicked (bool isCorrect)
{
if (isCorrect)
{
playerScore += currentRoundData.pointAddedForCorrectAnswer;
scoreDisplayText.text = "Score: " + playerScore.ToString();
//play coorect answer sound
answerButtonClicked.Play();
StartCoroutine(ReturnCorrectButtonColor());
}
if (!isCorrect)
{
//play wrong answer sound
answerButtonClicked = wrongAnswerClicked;
answerButtonClicked.Play();
StartCoroutine(ReturnWrongButtonColor());
// buttons = GameObject.FindGameObjectsWithTag("Answer");
// {
// foreach (GameObject button in buttons)
// {
// if (button.GetComponent<AnswerButton>
//().answerData.isCorrect)
// {
// button.GetComponent<AnswerButton>
// ().StartCoroutine(ReturnCorrectButtonColor());
// }
// }
//}
}
if (qNumber < questionPool.Length - 1) /
{
qNumber++;
StartCoroutine(TransitionToNextQuestion());
}
else
{
StartCoroutine(WaitForFewSeconds());
}
}
public void EndRound()
{
isRoundActive = false;
questionPanel.SetActive(false);
roundOverPanel.SetActive(true);
}
//on button click return to main menu
public void ReturnToMenu ()
{
SceneManager.LoadScene("MenuScreen");
}
}
In order to highlight both Wrong (the clicked one) and the Right buttons you need to have access to both buttons. This means that you can't do highlighting from HandleClick method of your Answer Button Script, as it only has access to itself, e.g. to the Wrong button.
The good thing is that this method notifies GameController that the button has been clicked. GameController knows about all the buttons, so it can easily highlight both buttons.
So, instead of launching your highlight subroutines from Answer Button's HandleClick, you should do this from GameController's AnswerButtonClicked: identify both the Right button and the clicked button there and launch appropriate subroutines for them.
Update:
For instance, your ReturnCorrectButtonColor would look like:
IEnumerator ReturnCorrectButtonColor( GameObject button )
{
Debug.Log("im correct");
button.GetComponent<Button>().image.color = Color.green;
yield return new WaitForSeconds(seconds: 2.9f);
button.GetComponent<Button>().image.color = Color.white;
}
so in AnswerButtonClicked you identify which button to highlight as a correct answer button and pass it as a paramter to this method:
StartCoroutine(ReturnCorrectButtonColor(correctButton));

Scriptable object doesn't work on build

I'm hoping this might be something simple I'm missing.
I have a ScriptableObject script that looks like this:
using UnityEngine;
using System.Collections;
[CreateAssetMenu]
public class Item: ScriptableObject
{
public string iname;
public int iindex;
public Sprite sprite;
public GameObject itemObject;
public int value;
public string description;
public itemType itemtype;
public enum itemType
{
Consumable,
Equippable,
}
}
This works great in the editor, but if I publish to Android or Windows any script that references the ScriptableObject, it does not work. What am I missing?
For example the following block of code does not seem to execute at all:
for (int i = 0; i < 3; i++)
{
int lootnum = Random.Range(0, 4);
slot1 = itemdb[lootnum];
tlist[i] = itemdb[lootnum];
slotlist[i].transform.GetChild(0).GetComponent<Image>().sprite = itemdb[lootnum].sprite;
slotlist[i].transform.GetChild(0).GetComponent<Image>().enabled = true;
}
Those lists in the code are of the type Item defined in the above script. I'm not sure how to debug this as I get no errors or warnings in the editor.
Here is the script which populates the inventory. There's a bit of junk in there but it definitley works fine pressing play in the editor. Just not on build.
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using UnityEngine.UI;
public class Inventory : MonoBehaviour {
public int invSize;
public Item slot1;
public Item tslot1;
public Item tslot2;
public Item tslot3;
public GameObject t1;
public GameObject t2;
public GameObject t3;
public Sprite itemsprite;
public List<Item> itemdb = new List<Item>();
public List<Item> items = new List<Item>();
public List<Item> tlist = new List<Item>();
public Text stext;
public Text description;
public Item selectItem;
public GameObject selectSlot;
public Object test2;
public List<GameObject> slotlist = new List<GameObject>();
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
}
public void addItem(Item itemToAdd)
{
//items.Add(itemdb[0]);
for (int i = 0; i < 5; i++)
{
if (items[i] == null)
{
items[i] = itemToAdd;
itemsprite = itemToAdd.sprite;
return;
}
}
}
public void GenTreasure()
{
for (int i = 0; i < 3; i++)
{
int lootnum = Random.Range(0, 4);
slot1 = itemdb[lootnum];
tlist[i] = itemdb[lootnum];
slotlist[i].transform.GetChild(0).GetComponent<Image>().sprite = itemdb[lootnum].sprite;
slotlist[i].transform.GetChild(0).GetComponent<Image>().enabled = true;
}
}
public void Uptext(int indexx)
{
stext.text = tlist[indexx].iname;
selectItem = tlist[indexx];
selectSlot = slotlist[indexx];
description.text = selectItem.description;
}
public void Take(int index)
{
//items.Add(selectItem);
for (int i = 0; i < invSize; i++)
{
if (items[i] == null)
{
items[i] = selectItem;
// itemsprite = itemToAdd.sprite;
selectItem = null;
// tlist[i] = null;
// slotlist[i].transform.GetChild(0).GetComponent<Image>().sprite = null;
selectSlot.transform.GetChild(0).GetComponent<Image>().enabled = false;
return;
}
}
}
}
If the script is not in the scene, how does it get loaded? Is it using an asset bundle? If that is the case, then it's possible that the class is being stripped from the build. You can include the class in your link.xml to make sure it's included. Another option is to simply reference the script in an included scene anywhere.

Unity Camera Change C#

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]);
}

Categories

Resources