Trying to get user input while yield return new waitforseconds is running in a coroutine
So, I have a camera that moves to a new location every 5 seconds. I'm making the camera move in the update function using Vector3.MoveTowards. I have an IEnumerator function that disables the script once the camera reaches its ideal location, does a "yield return new waitforseconds(5)" then re-enables the script. However, I also want to include user input in order for them to skip the camera ahead to its next location without having it move every frame in the update function, the user can simply press a key and the camera instantly jumps to that location. The problem is that when the yield statement runs, it does not register user input until it returns for the full 5 seconds. I'm looking for a way to somehow break the yield statement as it is executing, but so far I haven't had any luck. I've tried multiple different implementations and looked at answers on these forums but I haven't come across any related to my particular problem. Here's my code, feel free to ignore anything that's commented out:
using System.Collections; using System.Collections.Generic; using UnityEngine; using System.Linq; // import statement required for the OrderBy function
public class CameraController : MonoBehaviour {
public Camera[] myArray; // an array that holds all cameras in the scene
public Vector3[] camLocs; // an array that holds locations of all cameras in the scene EXCEPT for the main camera
//int disabled = 0; // keeps track of how many cameras were disabled, used for debugging purposes in the console
private int counter = 0; // external counter used for iterating through the camLocs array
private bool hasMoved = false; // checks if the main camera has moved to the new position yet or not
private bool start = true; // checks if the main camera started its movement yet or not
public float cameraSpeed = 0.6f; // the speed at which the main camera moves, alter it for faster/slower movement
private float waitTime = 5f; // how long the main camera waits before moving to the next location
private bool keyPressed = false; // a lock to determine if the user has pressed a key, if true, then move camera immediately to next location
// Use this for initialization
void Start()
{
myArray = Camera.allCameras; //get all instances of enabled cameras in the scene and put them into an array
myArray = myArray.OrderBy(x => x.name).ToArray(); //sorts the array of cameras by their name
camLocs = new Vector3[myArray.Length - 1]; //holds the location of all cameras except the main camera (because we start at the main camera)
camTransition();
}
// Update is called once per frame
void Update()
{
camMove();
}
/* disables all cameras except the main camera, fills the camera locations array with the locations from the camera array */
void camTransition()
{
if (myArray != null)
{
foreach (Camera myCamera in myArray)
{
if (myCamera.tag != "MainCamera")
{
myCamera.enabled = false;
/*
Debug.Log(myCamera.name + " is disabled.\n"); // uncomment for debugging
disabled++;
*/
}
}
//Debug.Log("Disabled cameras: " + disabled); // uncomment for debugging
}
// fill camLocs array with camera locations
for (int i = 0; i < camLocs.Length; i++)
{
if (myArray[i].tag != "MainCamera")
{
camLocs[i] = myArray[i].transform.position;
}
}
//Debug.Log("CamLocs length: " + camLocs.Length); // checks if camLocs has all the required camera locations, uncomment for debugging
}
/* Moves the main camera */
void camMove()
{
//Debug.Log("Counter value: " + counter); // checks if counter iterates properly, uncomment for debugging
if (start)
{
/* IGNORE FOR NOW
if (Input.GetKeyDown(KeyCode.E))
{
Debug.Log("E key pressed.");
transform.position = Vector3.MoveTowards(transform.position, camLocs[counter], Time.deltaTime * cameraSpeed);
if (transform.position == camLocs[counter])
{
transform.position = camLocs[counter];
}
counter++;
}
else
{
StartCoroutine(waitFunction());
}
start = false;
*/
//Invoke("movementOnly", waitTime);
StartCoroutine(waitFunction());
start = false;
}
else if (counter < camLocs.Length)
{
transform.position = Vector3.MoveTowards(transform.position, camLocs[counter], Time.deltaTime * cameraSpeed);
if (transform.position == camLocs[counter])
{
transform.position = camLocs[counter];
hasMoved = true;
}
else if (keyPressed) // attempted to have user input move camera immediately to next location, doesn't jump immediately
{
transform.position = camLocs[counter];
hasMoved = true;
keyPressed = false;
}
if (hasMoved)
{
counter++;
hasMoved = false;
StartCoroutine(waitFunction());
}
//Invoke("movementOnly", waitTime);
}
}
/* Makes the main camera wait for a specified amount of time before moving to the next location */
IEnumerator waitFunction()
{
this.GetComponent<CameraController>().enabled = false;
for (int i = 0; i < waitTime; i++)
{
yield return new WaitForSeconds(1); // attempted to yield only for 1 second in hopes of user input getting through
if (Input.GetKey(KeyCode.E)) // does not register Input.GetKeyDown sometimes, only Input.GetKey works consistently
{
Debug.Log("E pressed.");
keyPressed = true;
yield break;
}
}
//yield return new WaitForSeconds(waitTime); //when this statement runs, stops everything else, even user input
this.GetComponent<CameraController>().enabled = true;
}
/* IGNORE FOR NOW
void movementOnly()
{
transform.position = Vector3.MoveTowards(transform.position, camLocs[counter], Time.deltaTime * cameraSpeed);
if (transform.position == camLocs[counter])
{
transform.position = camLocs[counter];
hasMoved = true;
}
if (hasMoved)
{
counter++;
hasMoved = false;
}
}
*/
}
Answer by apollov · Nov 09, 2017 at 01:48 AM
Since you're waiting for some time, there are no callbacks coming to the coroutines during that sleep. For your logic to work, you should have key press detection logic to be running in Update every frame, and in case of successful detection stop coroutine and do whatever you want to for that case. And for that you can save a reference to coroutine to be able to interrupt it from the outside at any moment. Like:
private Coroutine _coroutine;
private void Update(){
if (KeyPressed && _coroutine != null){
StopCoroutine(_coroutine);
...
}
}
private void camMove(){
...
_coroutine = StartCoroutine(waitFunction());
...
}