How to cancel an AudioSource.PlayScheduled() call - c#

Title is pretty self explanatory. But here's a brief description of my problem:
I'm using playscheduled to switch tracks on beat/bar. The issue is that sometimes, some event happens and I would like to cancel a specific queued track from being played.
Using AudioSource.Stop() will only stop the currently played track, the scheduled tracked will still play afterwards.
Any ideas? Is there a method to call that I am missing?

You can use a coroutine to control when and if the next clip should be played. There is a simple example in the AudioSource.clip reference that needs to be extended a little bit.
The following code snippet plays the clip attached to AudioSource and ToggleAudioClips.otherClip alternating in an endless loop until ToggleAudioClips.StopPlaying () is called. A new coroutine is started whenever the current clip is finished.
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
[RequireComponent(typeof(AudioSource))]
public class ToggleAudioClips : MonoBehaviour
{
public AudioClip otherClip;
bool stopPlaying = false;
AudioClip currentClip;
AudioClip nextClip;
void Start () {
currentClip = audio.clip;
nextClip = otherClip;
StartCoroutine (PlayClip ());
}
IEnumerator PlayClip () {
audio.clip = currentClip;
audio.Play ();
yield return new WaitForSeconds(audio.clip.length);
if (!stopPlaying) {
AudioClip c = nextClip;
nextClip = currentClip;
currentClip = c;
StartCoroutine (PlayClip ());
}
}
public bool StopPlaying () {
stopPlaying = true;
audio.Stop ();
}
}

After much trial and error, I after creating a whole new scene to test the various options I have realized that AudioSource.Stop() WILL stop a scheduled track from playing. It was my error that was causing my queueing system to schedule a new track after I have sent a cancel call.
To see this working just schedule a track and print AudioSource.isPlaying. You will notice that even thou the source is not currently playing, scheduling it will set this variable to "true". Once you call AudioSource.Stop(), the variable will be set back to false and the scheduled track will never play.

Related

.setActive(true) not working after second time calling it Unitiy

I am fairly new to Unity and I really don't know how to directly ask this question. Basicaly I am just playing around with Unitiy in general and I am trying to create script that initially has it set to active then disappear after 3 seconds but then come back as active again, can anyone explain anything specific that has to do with this problem? This is my code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ScriptTwo : MonoBehaviour
{
public GameObject theBall;
// Start is called before the first frame update
void Start()
{
theBall.SetActive(true);
StartCoroutine(BallCode());
}
IEnumerator BallCode() {
yield return new WaitForSeconds(3);
theBall.SetActive(false);
Debug.Log("Set Active: False");
Debug.Log("Test Before");
yield return new WaitForSeconds(3);
theBall.SetActive(true);
}
}
What your code should be doing is:
-activating
-waiting 3 seconds
-disabling
-waiting 3 seconds
-activating
If it is doing anything other than that it is a problem with another code you wrote. If you wanted it to continue infinitly than:
IEnumerator BallCode()
{
while (true)
{
yield return new WaitForSeconds(3);
theBall.SetActive(!theBall.activeInHierarchy);
Debug.Log("Set Active: " + theBall.activeInHierarchy);
}
}
If your script is in the gameObject you are deactivating (in this instance the ball) you should move it to another gameObject.
All Unity Monobehaviour methods and by extent coroutines, are halted/stopped when the gameObject is deactivated.

How to play the audio one by one in unity

I want to achieve playing the music one by one in Unity. I tried to create an audioClip array and want to play them one by one. And i also tried to use StartCoroutine to wait one song finished and then playing the next.
I tried to create an audioClip array and want to play them one by one. And i also tried to use StartCoroutine to wait one song finished and then playing the next.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Audio;
[RequireComponent(typeof(AudioSource))]
public class AudioManager : MonoBehaviour {
public AudioClip[] _audioClips;
private AudioSource _audioSource;
void Awake(){
_audioSource = GetComponent<AudioSource>();
}
// Use this for initialization
void Start () {
for (int i = 0; i < _audioClips.Length;i++){
_audioSource.PlayOneShot(_audioClips[i]);
StartCoroutine("WaitForMusicEnd");
}
}
IEnumerator WaitForMusicEnd()
{
while (_audioSource.isPlaying)
{
yield return null;
}
}
}
However, the music will play at the same time. Please help!
you are not too far off, but this is not how coroutine work - you need to be insde a coroutine to be able to wait, here's how you do it
void Start ()
{
StartCoroutine(PlayMusic());
}
IEnumerator PlayMusic()
{
for (int i = 0; i < _audioClips.Length;i++)
{
_audioSource.PlayOneShot(_audioClips[i]);
while (_audioSource.isPlaying)
yield return null;
}
}
The control flow goes like this:
void Start()
{
StartCoroutine(Foo());
StartCoroutine(Bar());
}
IEnumerator Foo()
{
Debug.Log("FOO");
yield return null;
Debug.Log("foo");
}
IEnumerator Bar()
{
Debug.Log("BAR");
yield return null;
Debug.Log("bar");
}
// FOO
// BAR
// foo
// bar
If you watch what happens: With each coroutine start, the control goes into the coroutine, up until the first yield return. At this point we rewind the instruction pointer back to Start(), and start the second coroutine. Then Start() finishes, the frame is drawn. Unity keeps track of running coroutines, and before next frame will return control to where you left within your coroutine.
Its quite clever, as it makes spreading things in time much easier.

I'm really struggling to pass the Oculus VRC TestSubmitWhenNotVisible test

I'm stuck on the last test for my app before submission to the Oculus store. I've tried all sorts with no avail. I need to do is pass the frames when not visible test.
Effectively, the app needs to go into pause mode when the user clicks the menu button on the oculus touch controller.
I need to stop all frames submitting from Unity. For example, things I've tried, turn off cameras, audio, ovrplayercontroller etc but frames being submitted when the menu button is pressed on the rift so it would appear to freeze the application.
I've tried disabling cameras in a foreach loop, disabling the player gameobject, ovr controllers all sorts.
I have a gameobject with a script attached to try and detect when the test will fire based on the HMD losing tracking.
Here's where I'm currently at (gone back to the basics again), any help would be greatly appreciated.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class HMDCheck : MonoBehaviour
{
public GameObject OVRCameraRig;
private void Update()
{
if (!OVRManager.isHmdPresent)
{
OVRCameraRig.SetActive(false);
Time.timeScale = 0f;
}
else
{
OVRCameraRig.SetActive(true);
Time.timeScale = 1f;
}
}
}
Additionally their docs say the test performs this action:
TestSubmitFramesWhenNotVisible
Tests if your app stops submitting frames when the Universal Menu is open.
Note :
My most recent command line response for the test is the following output :
Starting TestSubmitFramesWhenNotVisible
Waiting for the application to run for 5 seconds before testing begins...
Starting test...
Requesting the void...
Number of texture swap chains committed when visible 68
Number of texture swap chains committed when not visible 4
ERROR: Committed a texture swap chain (called ovr_CommitTextureSwapChain) when application not visible
Please refer to VRC Guidelines:
https://developer.oculus.com/distribute/latest/concepts/vrc-pc-input-1/
Cleaning up...
Test FAILED
To get around it, I created a script with public gameobjects. I then dragged my player into the slots inside Unity but left the camera on the scene alone. Here's a screenshot and the code. I initially added the camera to turn off frames sending but Oculus rejected it as it froze in the background upon pressing the menu button.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class HMDCheck : MonoBehaviour
{
public GameObject target, scripts, LocalAvatar;
private void Update()
{
if (!OVRManager.hasVrFocus || !OVRManager.isHmdPresent || !OVRManager.hasInputFocus)
{
//target.SetActive(false);
scripts.SetActive(false);
LocalAvatar.SetActive(false);
Time.timeScale = 0f;
AudioListener.pause = true;
}
else
{
//target.SetActive(true);
scripts.SetActive(true);
LocalAvatar.SetActive(true);
Time.timeScale = 1f;
AudioListener.pause = false;
}
}
}
I have almost the same code, but this detects the Universal Menu correctly and does freeze the output, but it still won't pass the test. I think it may pass if you disable splash screens, but can't know since I have the free version of Unity.
Edit: I saw your answer Diego, but cannot comment. Did your action resolve your original question? And what license do you have for Unity?
Camera cam;
bool bPause = false;
void Update()
{
//install 'Oculus Integration' for this to work
bool bPauseNow = !(OVRManager.hasInputFocus && OVRManager.hasVrFocus);
//pause state change
if (Camera.main != null) cam = Camera.main;
if (bPause != bPauseNow)
{
bPause = bPauseNow;
if (bPauseNow)
{
Time.timeScale = 0.0f; //stops FixedUpdate
//Update keeps running, but
// rendering must also be paused to pass vrc
cam.enabled = false;
}
else
{
Time.timeScale = 1.0f;
//
cam.enabled = true;
}
}
//...
}
I am using the "Oculus XR Plugin", and got this issue,
but I found that the results of the "VRC Validator" are not so accurate,
then I submitted the App for review and passed the test.

How to check if a certain animation state from an animator is running?

I created an animator called "m4a4animator". Inside it, the main function is called "idle" (nothing), and other 2 states: "shoot" (mouse0) and "reload" (R). These 2 animation states are transitioned to "idle". Now, everything is working... but the only problem I have is this: if I am in the middle of reloading and and press mouse0 (shoot), the animation running state immediately changes to shoot... but I want to block that.
Now, the question: How can I stop CERTAIN animation changes while an animation is running?
Here is my animator
And here is my script:
using UnityEngine;
using System.Collections;
public class m4a4 : MonoBehaviour {
public Animator m4a4animator;
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
if (Input.GetKeyDown (KeyCode.R)) {
m4a4animator.Play("reload");
}
if (Input.GetMouseButton(0)) {
m4a4animator.Play("shoot");
}
}
}
For the legacy Animation system, Animation.IsPlaying("TheAnimatonClipName) is used to check if the animation clip is playing.
For the new Mechanim Animator system, you have to check if both anim.GetCurrentAnimatorStateInfo(animLayer).IsName(stateName) and anim.GetCurrentAnimatorStateInfo(animLayer).normalizedTime < 1.0f) are true. If they are then animation name is currently playing.
This can be simplified like the function like the Animation.IsPlaying function above.
bool isPlaying(Animator anim, string stateName)
{
if (anim.GetCurrentAnimatorStateInfo(animLayer).IsName(stateName) &&
anim.GetCurrentAnimatorStateInfo(animLayer).normalizedTime < 1.0f)
return true;
else
return false;
}
Now, everything is working... but the only problem I have is this: if
I am in the middle of reloading and and press mouse0 (shoot), the
animation running state immediately changes to shoot... but I want to
block that.
When the shoot button is pressed, check if the "reload" animation is playing. If it is, don't shoot.
public Animator m4a4animator;
int animLayer = 0;
// Update is called once per frame
void Update()
{
if (Input.GetKeyDown(KeyCode.R))
{
m4a4animator.Play("reload");
}
//Make sure we're not reloading before playing "shoot" animation
if (Input.GetMouseButton(0) && !isPlaying(m4a4animator, "reload"))
{
m4a4animator.Play("shoot");
}
}
bool isPlaying(Animator anim, string stateName)
{
if (anim.GetCurrentAnimatorStateInfo(animLayer).IsName(stateName) &&
anim.GetCurrentAnimatorStateInfo(animLayer).normalizedTime < 1.0f)
return true;
else
return false;
}
If you need to wait for the "reload" animation to finish playing before playing the "shoot" animation then use a coroutine. This post described how to do so.
There are other threads about that: https://answers.unity.com/questions/362629/how-can-i-check-if-an-animation-is-being-played-or.html
if (this.animator.GetCurrentAnimatorStateInfo(0).IsName("YourAnimationName"))
{
//your code here
}
this tells you if you are in a certain state.
Animator.GetCurrentAnimatorStateInfo(0).normalizedTime
this give you the normalized time of the animation: https://docs.unity3d.com/ScriptReference/AnimationState-normalizedTime.html
Try to play with those function, I hope that solve your problem

WaitforSeconds not exactly waiting for the time specified in Unity

I searched for using WaitforSeconds and used as it was mentioned(using a return type of IEnumeration and using coroutines instead of update). but it did not work. Initially it showed Waitfor Seconds and IEnumerator were not "present in the current context". I had to r-install unity to get it fixed but this problem still remains. The following is my code. Am I using WaitforSeconds in correct way? Is it the 'if' code block that ruin my complete work(I mean have I used it in wrong place)?
using UnityEngine;
using System.Collections;
public class EnemyCreeator : MonoBehaviour
{
public float xmin,xmax,zmin,zmax;
public GameObject enemy;
public float spawnWait,StartWait,waveWait;
public int turretCount;
public int enemyCount;
int i=0;
void Update()
{
StartCoroutine (TurretWaves());
}
IEnumerator TurretWaves()
{
while (true)
{
Vector3 pos=new Vector3(Random.Range (xmin,xmax),0.5f,Random.Range(zmin,zmax));
yield return new WaitForSeconds(StartWait);
while(i<turretCount)
{
//Debug.Log ("The vaue of game time in spawnwaiting is: "+Time.time);
Instantiate (enemy, pos, Quaternion.identity);
enemyCount++;
i++;
//Debug.Log ("value of i in loop is: "+i);
//Debug.Log ("The vaue of game time is: "+Time.time);
yield return new WaitForSeconds (spawnWait);
}
//Debug.Log("cHECKing Before WAVE WAIT(value of time )is: "+Time.time);
yield return new WaitForSeconds(waveWait);
if(i>=turretCount)
{
i=0;
}
//Debug.Log("cHECKing AFTER WAVE WAIT and value of time is: "+Time.time);
//Debug.Log ("value of i outside the while loop is: "+i);
}
}
}
The code needs to wait until the spawnWait before spawning each turret and wait till the wavewait before spawning the next wave. even though the Startwait works fine, I still am unable to find the problem with others...
Thanks in advance.
Your code is correct, except
void Update()
{
StartCoroutine (TurretWaves());
}
By doing this, you are creating a new coroutine in every frame. Therefore, after Startwait seconds, each running coroutine will spawn an enemy in the following frames, making the overall script works incorrectly. But, in fact, each coroutine works as you expected.
To solve your problem, change
void Update()
to
void Awake()
or
void Start()
such that only one coroutine is started. It should work as expected.
Edit #1:
I think you misunderstood how coroutine works in Unity, because it seems that you are using Update to manually execute the code inside coroutine frame by frame.
When a coroutine is started by StartCoroutine, a "task" will be registered inside Unity. This task is like another thread (except it is still in the main thread). Unity will execute the code inside this task automatically. When it meets a yield statement, it pauses and waits for the statement to end. (Internally, it checks the state continuously and determine when it should resume.) For example, yield return new WaitForSeconds(n) will make Unity temporarily stops executing the code inside the coroutine for n seconds. And after that, it will keep going on.
The task will be deregistered until no more code should be executed (in your case, it never ends because of the while-loop), or the game object that started the coroutine is destroyed or deactivated.

Categories