Unity Playlist in order - unity3d

I've got a playlist which shuffles the songs, however I want to either play them in order or shuffle.
Any help will be appreciated :)
public class Music : MonoBehaviour {
public AudioClip[] clips;
private AudioSource audiosource;
void Start()
{
audiosource = FindObjectOfType<AudioSource>();
audiosource.loop = false;
}
void Update()
{
if(!audiosource.isPlaying)
{
audiosource.clip = GetRandomClip();
audiosource.Play();
}
}
private AudioClip GetRandomClip()
{
return clips[Random.Range(0, clips.Length)];
}
private void Awake()
{
DontDestroyOnLoad(transform.gameObject);
}
}

I don't get your problem, isn't it as simple as that ?
public class Music : MonoBehaviour
{
public AudioClip[] clips;
private AudioSource audiosource;
public bool randomPlay = false;
private int currentClipIndex = 0;
void Start()
{
audiosource = FindObjectOfType<AudioSource>();
audiosource.loop = false;
}
void Update()
{
if(!audiosource.isPlaying)
{
AudioClip nextClip;
if (randomPlay)
{
nextClip = GetRandomClip();
}
else
{
nextClip = GetNextClip();
}
currentClipIndex = clips.IndexOf(nextClip);
audiosource.clip = nextClip;
audiosource.Play();
}
}
private AudioClip GetRandomClip()
{
return clips[Random.Range(0, clips.Length)];
}
private AudioClip GetNextClip()
{
return clips[(currentClipIndex + 1) % clips.Length)];
}
private void Awake()
{
DontDestroyOnLoad(transform.gameObject);
}
}

The previous answer did not work. It returned several errors.
I have revised your script and tested it with Unity 2018.2.12f1.
This should be added to an empty game object, with an audio source component.
Drag and drop audio clips to the clips field to create the list.
public bool randomPlay = false; // checkbox for random play
public AudioClip[] clips;
private AudioSource audioSource;
int clipOrder = 0; // for ordered playlist
void Start () {
audioSource = GetComponent<AudioSource> ();
audioSource.loop = false;
}
void Update () {
if (!audioSource.isPlaying) {
// if random play is selected
if (randomPlay == true) {
audioSource.clip = GetRandomClip ();
audioSource.Play ();
// if random play is not selected
} else {
audioSource.clip = GetNextClip ();
audioSource.Play ();
}
}
}
// function to get a random clip
private AudioClip GetRandomClip () {
return clips[Random.Range (0, clips.Length)];
}
// function to get the next clip in order, then repeat from the beginning of the list.
private AudioClip GetNextClip () {
if (clipOrder >= clips.Length - 1) {
clipOrder = 0;
} else {
clipOrder += 1;
}
return clips[clipOrder];
}
void Awake () {
DontDestroyOnLoad (transform.gameObject);
}

Related

why does the cooldown on my gun not work?

I have this gun that's supposed to have a cooldown after every shot by using time between shots += Time.deltaTime. The problem is that it's like timeBeetweenShots dosen't increase. Here's the part of my code that I think matters:
private void Update()
{
timeSinceLastShot += Time.deltaTime;
}
public bool CanShoot() => !gunData.reloading && timeSinceLastShot > 1 / (gunData.fireRate / 60);
public void Shoot()
{
if (gunData.currentAmmo > 0)
{
if (CanShoot())
{
Debug.Log("shooting");
gunData.currentAmmo--;
timeSinceLastShot = 0;
OnGunShot();
}
} else
{
StartCoroutine(Reload());
}
}
what did I do wrong?
I think we might need to use Time.time.
The doc link is just perfect for gun fire interval.
However I had edit your code.
using System.Collections;
using UnityEngine;
public class Gun : MonoBehaviour
{
GunData gunData; // gundata might be access from player
float timeSinceLastShot = 0;
private void Update()
{
}
public bool CanShoot() => !gunData.reloading && (timeSinceLastShot + gunData.fireRate) < Time.time;
public void Shoot()
{
if (gunData.currentAmmo > 0)
{
if (CanShoot())
{
Debug.Log("shooting");
gunData.currentAmmo--;
timeSinceLastShot = Time.time;// save the last time that shot
OnGunShot();
}
}
else
{
StartCoroutine(Reload());
}
}
public void OnGunShot()
{ // animation might be here
}
IEnumerator Reload()
{ // relaoding might be here
yield return new WaitForEndOfFrame();
}
}
public class GunData // just dummy data
{
public int currentAmmo = 10;
public float fireRate = 0.2f;
public bool reloading = false;
}

I have a problem programming with unity to paste the image of footsteps on the ground when the player moves

public class FootPaint : MonoBehaviour
{
#region --- helpers --
public enum enumFoot
{
Left,
Right,
}
#endregion
public GameObject LeftPrefab;
public GameObject RightPrefab;
public float FootprintSpacer = 1.0f;
private Vector3 LastFootprint;
private enumFoot WhichFoot;
public GameObject[] intdexPos;
private void Start()
{
LastFootprint = this.transform.position;
}
private void Update()
{
FootPaints();
}
public void FootPaints()
{
if (CheckPaintItem.instance.isPaint == true && gameObject.name == "PlayerSeek")
{
float DistanceSinceLastFootprint = Vector3.Distance(transform.position, LastFootprint);
if (DistanceSinceLastFootprint >= FootprintSpacer)
{
LastFootprint = this.transform.position;
if (WhichFoot == enumFoot.Left)
{
SpawnFootDecal(LeftPrefab);
WhichFoot = enumFoot.Right;
}
else if (WhichFoot == enumFoot.Right)
{
SpawnFootDecal(RightPrefab);
WhichFoot = enumFoot.Left;
}
LastFootprint = new Vector3(this.transform.position.x, this.transform.position.y + 1f, this.transform.position.z);
}
}
}
public void SpawnFootDecal(GameObject prefab)
{
int index = Random.Range(0, intdexPos.Length);
//where the ray hits the ground we will place a footprint
GameObject decal = Instantiate(prefab);
decal.transform.position = intdexPos[index].transform.position;
decal.transform.Rotate(Vector3.forward, intdexPos[index].transform.eulerAngles.y);
//turn the footprint to match the direction the player is facing
}
}
public class CheckPaintItem : MonoBehaviour
{
public static CheckPaintItem instance;
#region boolPaint
[HideInInspector]
public bool isPaint = false;
[HideInInspector]
public bool isPaintYellow = false;
[HideInInspector]
public bool isPaintRed = false;
[HideInInspector]
public bool isPaintBlue = false;
[HideInInspector]
public bool isPaintGreen = false;
#endregion
private float currentMoveSpeed;
private void Awake()
{
instance = this;
}
private void Start()
{
currentMoveSpeed = 2.5f;
}
private void OnTriggerEnter(Collider other)
{
if (other.gameObject.tag == "PaintsYellow")
{
Debug.Log("Yellow");
isPaint = true;
isPaintYellow = true;
StartCoroutine(timePaint());
}
else if (other.gameObject.tag == "PaintsRed")
{
isPaint = true;
isPaintRed = true;
StartCoroutine(timePaint());
}
else if (other.gameObject.tag == "PaintsBlue")
{
isPaint = true;
isPaintBlue = true;
StartCoroutine(timePaint());
}
else if (other.gameObject.tag == "PaintsGreen")
{
isPaint = true;
isPaintGreen = true;
StartCoroutine(timePaint());
}
else
{
isPaint = false;
isPaintYellow = false;
isPaintRed = false;
isPaintBlue = false;
isPaintGreen = false;
}
if (other.gameObject.tag == "PaintsGlue")
{
StartCoroutine(changeMoveSpeedSlow());
}
if (other.gameObject.tag == "Speed")
{
StartCoroutine(changeMoveSpeedFast());
Destroy(other.gameObject);
}
}
IEnumerator changeMoveSpeedSlow()
{
PlayerController.instance._moveSpeed = PlayerController.instance._moveSpeed / 2 + 0.2f;
yield return new WaitForSeconds(5f);
PlayerController.instance._moveSpeed = currentMoveSpeed;
}
IEnumerator changeMoveSpeedFast()
{
PlayerController.instance._moveSpeed = PlayerController.instance._moveSpeed + 1f;
yield return new WaitForSeconds(5f);
PlayerController.instance._moveSpeed = currentMoveSpeed;
}
IEnumerator timePaint()
{
yield return new WaitForSeconds(15f);
isPaint = false;
}
}
I want to code the game "hide and seek", but I had a problem when the character stepped into a puddle of paint.
My game has 1 character finding and 5 moving AIs, I want whether the AI or the player steps into the puddle, it will leave footprints. But I have problem with above scripts tag, I add script tag with all characters.
So one character walks in, all the other characters show their footsteps. I don't know if my direction is correct. If anyone wants to understand more about the game I'm coding, you can go to youtube and type hide and seek and it will come out.
Thank you.
It seems to me, that the problem lies within CheckPaintItem.instance.isPaint. I don't know what that script is exactly, but it's probably a singleton pattern as there is an instance call. If that's the case, then you're checking if any CheckPaintItem is true globally as it's fields are pretty much static. So make changes to CheckPaintItem or show us what it does exactly.

Handling different functions calls using New Input System

I'm new to Unity New Input System, I have recently migrated my project from the old input system to the new input system but I'm facing some difficulties using it.
I have a class called InputManager that manages all the input actions for my game, and another class called PlayerInteractions that holds all the player's interactions, such as picking up objects, opening and closing drawers/doors and so on.
playerInteraction class was working perfectly using the old input system, but when I changed it to the new input system I couldn't handle the function calls properly so it didn't work completely. I managed to make picking up objects work but the opening/closing part didn't work because for that I'm calling another function from a class called InteractiveObjects that is attached to all objects that can be opened/closed such as door/drawers...
and it was hard for me to handle these function calls since I'm not good enough at the New Input System.
Here is a snippet of the PlayerInteractions Class
private InteractiveObjects interactiveObjects;
bool PickingUp, Open;
void Update()
{
raycastPos = mainCamera.ScreenToWorldPoint(new Vector3(Screen.width / 2, Screen.height / 2, 0));
RaycastHit hit;
if (Physics.SphereCast(raycastPos, sphereCastRadius, mainCamera.transform.forward, out hit, maxDistance, 1 << interactableLayerIndex))
{
lookObject = hit.collider.transform.gameObject;
}
else
{
lookObject=null;
}
if (PickingUp)
{
if (currentlyPickedUpObject == null)
{
if (lookObject != null)
{
PickupObject();
}
}
else
{
BreakConnection();
}
}
PickingUp = false;
if (hit.transform)
{
interactiveObjects = hit.transform.GetComponent<InteractiveObjects>();
}
else
{
lookObject = null;
interactiveObjects = null;
}
if (Open)
{
if (interactiveObjects)
{
interactiveObjects.PerformAction();
}
}
Open = false;
}
public void OnPickingUp()
{
PickingUp = true;
}
public void OnOpen()
{
Open = true;
}
This part from above:
if (Open)
{
if (interactiveObjects)
{
interactiveObjects.PerformAction();
}
}
was like this:
if (Input.GetKeyDown(KeyCode.E))
{
if (interactiveObjects)
{
interactiveObjects.PerformAction();
}
}
and the InteractiveObject class was something like this:
public void PerformAction()
{
if (aSource)
{
aSource.Play();
}
if (Input.GetKeyDown(KeyCode.E))
{
if (isOpen)
{
iTweenArgs["position"] = closedPosition;
iTweenArgs["rotation"] = closedPosition;
}
else
{
iTweenArgs["position"] = openPosition;
iTweenArgs["rotation"] = openPosition;
}
isOpen = !isOpen;
switch(movementType)
{
case MovementType.Slide:
iTween.MoveTo(gameObject, iTweenArgs);
break;
case MovementType.Rotate:
iTween.RotateTo(gameObject, iTweenArgs);
break;
}
}
if(Input.GetKeyDown(KeyCode.E)) was changed to if (Open).
Here is the full IneractiveObject class after the change:
public class InteractiveObjects : MonoBehaviour
{
[SerializeField] private Vector3 openPosition, closedPosition;
[SerializeField] private float animationTime;
[SerializeField] private bool isOpen = false;
[SerializeField] private MovementType movementType;
bool Open;
private enum MovementType { Slide, Rotate };
private Hashtable iTweenArgs;
private AudioSource aSource;
void Start()
{
iTweenArgs = iTween.Hash();
iTweenArgs.Add("position", openPosition);
iTweenArgs.Add("time", animationTime);
iTweenArgs.Add("islocal", true);
aSource = GetComponent<AudioSource>();
}
public void PerformAction()
{
if (aSource)
{
aSource.Play();
}
if (Open)
{
if (isOpen)
{
iTweenArgs["position"] = closedPosition;
iTweenArgs["rotation"] = closedPosition;
}
else
{
iTweenArgs["position"] = openPosition;
iTweenArgs["rotation"] = openPosition;
}
isOpen = !isOpen;
switch (movementType)
{
case MovementType.Slide:
iTween.MoveTo(gameObject, iTweenArgs);
break;
case MovementType.Rotate:
iTween.RotateTo(gameObject, iTweenArgs);
break;
}
}
Open = false;
}
public void OnOpen()
{
Open = true;
}
}
Here is my Input Action:
InputManager class:
public class InputManager : MonoBehaviour
{
[SerializeField] Movement movement;
[SerializeField] PlayerInteractions playerInteractions;
[SerializeField] MouseLook mouseLook;
PlayerControls controls;
PlayerControls.GroundMovementActions groundMovement;
Vector2 horizontalInput;
Vector2 mouseInput;
private void Awake()
{
controls = new PlayerControls();
groundMovement = controls.GroundMovement;
// groundMovement.[action].performed += context => do something
groundMovement.HorizontalMovement.performed += ctx => horizontalInput = ctx.ReadValue<Vector2>();
groundMovement.Jump.performed += _ => movement.OnJumpPressed();
groundMovement.Running.performed += _ => movement.OnRunning();
groundMovement.PickingUp.performed += _ => playerInteractions.OnPickingUp();
groundMovement.MouseX.performed += ctx => mouseInput.x = ctx.ReadValue<float>();
groundMovement.MouseY.performed += ctx => mouseInput.y = ctx.ReadValue<float>();
groundMovement.Open.performed += _ => playerInteractions.OnOpen();
}
private void Update()
{
movement.ReceiveInput(horizontalInput);
mouseLook.ReceiveInput(mouseInput);
}
private void OnEnable()
{
controls.Enable();
}
private void OnDestroy()
{
controls.Disable();
}
}
So the InputManager class is calling a function from playerInteractions which is also calling a function from InteractiveObjects for opening/closing feature. that didn't work for me but I don't know how to fix this. please help.
The solution is here, you no longer need Open Boolean and you can call the function directly through the new input system. for this job; Enter only the contents of if on OnOpen.
public void OnOpen()
{
if (isOpen)
{
iTweenArgs["position"] = closedPosition;
iTweenArgs["rotation"] = closedPosition;
}
else
{
iTweenArgs["position"] = openPosition;
iTweenArgs["rotation"] = openPosition;
}
isOpen = !isOpen;
switch(movementType)
{
case MovementType.Slide:
iTween.MoveTo(gameObject, iTweenArgs);
break;
case MovementType.Rotate:
iTween.RotateTo(gameObject, iTweenArgs);
break;
}
}
This not only simplifies the structure of the code. Rather, it prevents the if condition from being repeated in the update. Now run OnOpen as you set it by pressing the B key:
controls.GroundMovement.Open.performed += _ => playerInteraction.OnOpen();
How do conditions work continuously?
There are several ways to build functions that work continuously. This is an example that works by defining Func<bool> in the target object script:
public class PlayerInteraction : MonoBehaviour
{
public Func<bool> IsPressingOpen;
public void Update()
{
if (IsPressingOpen())
{
// do something...
}
}
}
And it is enough to set the function according to the result of key pressing:
playerInteraction.IsPressingOpen = () => controls.GroundMovement.Open.IsPressed();
Hope this answer is comprehensive and practical.

How can I check FindGameObjectsWithTag during runtime?

I'm having troubles with my enemy NavMeshAgent AI, First it searches for GameObjects tagged with "Defenses", then I set the first destination based in how close the defense is by my enemy, when the defense is destroyed, the array value is increased by one and it changes the destination to the next defense.
My problem is, my player can create (Instance) new defenses during the game, when all my defenses are destroyed, my enemy turns crazy, so, i need a way to add those new defenses to my array, below is my enemy script.
public class NavMeshEnemy : MonoBehaviour
{
[Header("References", order = 0)]
[SerializeField] NavMeshAgent enemyAgent;
[Space(10)]
[SerializeField] GameObject[] destinations;
[Space(10)]
[SerializeField] float distanceObjects;
[SerializeField] int arrayElements = 0;
void Awake()
{
enemyAgent = GetComponent<NavMeshAgent>();
destinations = GameObject.FindGameObjectsWithTag("Defenses");
}
void Start()
{
destinations = destinations.OrderBy((d) => (d.transform.position - this.transform.position).sqrMagnitude).ToArray();
}
void Update()
{
distanceObjects = Vector3.Distance(this.transform.position, destinations[arrayElements].transform.position);
enemyAgent.destination = destinations[arrayElements].transform.position;
CheckArray();
}
void CheckArray()
{
if (destinations[arrayElements].gameObject.activeInHierarchy == false)
{
if (arrayElements < destinations.Length - 1)
{
arrayElements++;
enemyAgent.destination = destinations[arrayElements].transform.position;
}
else
{
arrayElements = 0;
}
}
}
}
Thank you for reading! :)
I would rather implement a component on your prefab with a static list like e.g.
public class NavMeshDestination : MonoBehaviour
{
public static HashSet<Transform> existingDestinations = new HashSet<Transform>();
private void Awake()
{
if(!existingDestinations.Contains(this)) existingDestinations.Add(this);
}
private void OnDestroy()
{
if(existingDestinations.Contains(this)) existingDestinations.Remove(this);
}
}
And then don't even go by tag but simply do
public class NavMeshEnemy : MonoBehaviour
{
[Header("References", order = 0)]
[SerializeField] NavMeshAgent enemyAgent;
[Space(10)]
[SerializeField] float distanceObjects;
[SerializeField] int arrayElements = 0;
void Awake()
{
if(!enemyAgent) enemyAgent = GetComponent<NavMeshAgent>();
}
void Update()
{
destinations = NavMeshDestination.existingDestinations.OrderBy(d => (d.transform.position - this.transform.position).sqrMagnitude).ToArray();
distanceObjects = Vector3.Distance(this.transform.position, destinations[arrayElements].transform.position);
enemyAgent.destination = destinations[arrayElements].transform.position;
CheckArray();
}
void CheckArray()
{
if (destinations[arrayElements].gameObject.activeInHierarchy == false)
{
if (arrayElements < destinations.Length - 1)
{
arrayElements++;
enemyAgent.destination = destinos[arrayElements].transform.position;
}
else
{
arrayElements = 0;
}
}
}
}
Note however that this still won't fix your issues with no available destination. So you probably should stop the execution if the array is empty like
if(NavMeshDestination.existingDestinations.Count == 0)
{
// Stop the enemyAgent
return;
}

unity piano game trouble with playback of recorded notes

i have a piano app in unity and i am using an ArrayList to add notes and then playing it back and it workes mostly but the playback method since it activates when playMode is true, it continues on a loop, i need to know how i can use an if statement to check if all the recorded notes have been played and if they have then the loop should break. i really need help.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class NotePlay: MonoBehaviour
{
public Button record, play;
public bool recordMode = false;
public bool playMode = false;
Animator anim;
public AudioClip noteA;
public AudioClip noteB;
public AudioClip noteC;
public AudioClip noteD;
public AudioClip noteE;
public AudioClip noteF;
public AudioClip noteG;
public AudioSource audio;
public int count = 0;
// Use this for initialization
public ArrayList notes;
void Start()
{
anim = gameObject.GetComponent<Animator>();
audio = GetComponent<AudioSource>();
notes = new ArrayList();
Button rec = record.GetComponent<Button>();
Button pl = play.GetComponent<Button>();
record.onClick.AddListener(()=>{ recordMode = !recordMode; });
play.onClick.AddListener(() => { playMode = !playMode; recordMode =
false; });
}
void Playback()
{
bool flag = true;
for (int i = 0; i < notes.Count && flag; i++)
{
char c = (char)notes[i];
System.Threading.Thread.Sleep(1000);
PlayNote(c);
print(c);
// i want the recorded notes to only play once
if ()
{
}
}
}
void PlayNote(char note)
{
if (note == 'a')
{
//anim.SetTrigger("A");
GameObject.Find("Sphere_A").GetComponent<AudioSource>
().PlayOneShot(noteA);
GameObject.Find("Sphere_A").GetComponent<Animator>
().SetTrigger("A");
print("a");
}
if (note == 'b')
{
//anim.SetTrigger("B");
GameObject.Find("Sphere_B").GetComponent<AudioSource>
().PlayOneShot(noteB);
GameObject.Find("Sphere_B").GetComponent<Animator>
().SetTrigger("B");
print("b");
}
if (note == 'c')
{
///anim.SetTrigger("C");
GameObject.Find("Sphere_C").GetComponent<AudioSource>
().PlayOneShot(noteC);
GameObject.Find("Sphere_C").GetComponent<Animator>
().SetTrigger("C");
}
if (note == 'd')
{
//anim.SetTrigger("D");
GameObject.Find("Sphere_D").GetComponent<AudioSource>
().PlayOneShot(noteD);
GameObject.Find("Sphere_D").GetComponent<Animator>
().SetTrigger("D");
}
if (note == 'e')
{
//anim.SetTrigger("E");
GameObject.Find("Sphere_E").GetComponent<AudioSource>
().PlayOneShot(noteE);
GameObject.Find("Sphere_E").GetComponent<Animator>
().SetTrigger("E");
}
else if (note == 'f')
{
// anim.SetTrigger("F");
GameObject.Find("Sphere_F").GetComponent<AudioSource>
().PlayOneShot(noteF);
GameObject.Find("Sphere_F").GetComponent<Animator>
().SetTrigger("F");
}
else if (note == 'g')
{
//anim.SetTrigger("G");
GameObject.Find("Sphere_G").GetComponent<AudioSource>
().PlayOneShot(noteG);
GameObject.Find("Sphere_G").GetComponent<Animator>
().SetTrigger("G");
}
}
// Update is called once per frame
void Update()
{
if (recordMode == true)
{
if (Input.GetKeyDown(KeyCode.A)) {notes.Add('a'); PlayNote('a'); }
if (Input.GetKeyDown(KeyCode.B)) {notes.Add('b'); PlayNote('b'); }
if (Input.GetKeyDown(KeyCode.C)) {notes.Add('c'); PlayNote('c'); }
if (Input.GetKeyDown(KeyCode.D)) {notes.Add('d'); PlayNote('d'); }
if (Input.GetKeyDown(KeyCode.E)) {notes.Add('e'); PlayNote('e'); }
if (Input.GetKeyDown(KeyCode.F)) {notes.Add('f'); PlayNote('f'); }
if (Input.GetKeyDown(KeyCode.G)) {notes.Add('g'); PlayNote('g'); }
}
else
{
if (Input.GetKeyDown(KeyCode.A)) { PlayNote('a'); }
if (Input.GetKeyDown(KeyCode.B)) { PlayNote('b'); }
if (Input.GetKeyDown(KeyCode.C)) { PlayNote('c'); }
if (Input.GetKeyDown(KeyCode.D)) { PlayNote('d'); }
if (Input.GetKeyDown(KeyCode.E)) { PlayNote('e'); }
if (Input.GetKeyDown(KeyCode.F)) { PlayNote('f'); }
if (Input.GetKeyDown(KeyCode.G)) { PlayNote('g'); }
}
if (playMode == true )
{
Playback();
}
}
}
You may want to specify that the AudioSource don't have to loop the sound. Add this line to your Start method :
void Start()
{
anim = gameObject.GetComponent<Animator>();
audio = GetComponent<AudioSource>();
audio.loop = false; // <--------------- HERE
notes = new ArrayList();
Button rec = record.GetComponent<Button>();
Button pl = play.GetComponent<Button>();
record.onClick.AddListener(()=>{ recordMode = !recordMode; });
play.onClick.AddListener(() => { playMode = !playMode; recordMode = false; });
}
You can either do this in code as above or directly in the inspector of your AudioSource. I think the default value of the loop property is set to true, that's why it's looping when you play a note.