I have been working on a object movement along a path Which i have been geting from Navmesh Unity3d
I am using coroutine in which i controled it with while loop as i can show
public void DrawPath(NavMeshPath pathParameter, GameObject go)
{
Debug.Log("path Parameter" + pathParameter.corners.Length);
if (agent == null || agent.path == null)
{
Debug.Log("Returning");
return;
}
line.material = matToApplyOnLineRenderer;
line.SetWidth(1f, 1f);
line.SetVertexCount(pathParameter.corners.Length);
allPoints = new Vector3[pathParameter.corners.Length];
for (int i = 0; i < pathParameter.corners.Length; i++)
{
allPoints[i] = pathParameter.corners[i];
line.SetPosition(i, pathParameter.corners[i]);
}
StartCoroutine(AnimateArrow(pathParameter));
//StartCoroutine(AnimateArrowHigh(pathParameter));
}
#endregion
#region AnimateArrows
void RunAgain()
{
StartCoroutine(AnimateArrow(Navpath));
}
IEnumerator AnimateArrow(NavMeshPath path)
{
Vector3 start;
Vector3 end;
while (true)
{
if (index > 0)
{
if (index != path.corners.Length - 1)
{
start = allPoints[index];
index += 1;
end = allPoints[index];
StopCoroutine("MoveObject");
StartCoroutine(MoveObject(arrow.transform, start, end, 3.0f));
yield return null;
}
else
{
index = 0;
RunAgain();
}
}
else if (index == 0)
{
start = allPoints[index];
arrow.transform.position = allPoints[index];
index += 1;
end = allPoints[index];
StopCoroutine("MoveObject");
StartCoroutine(MoveObject(arrow.transform, start, end, 3.0f));
yield return null;
}
}
}
IEnumerator MoveObject(Transform arrow, Vector3 startPos, Vector3 endPos, float time)
{
float i = 0.0f;
float rate = 1.0f / time;
journeyLength = Vector3.Distance(startPos, endPos);
float distCovered = (Time.time - startTime) * speed;
float fracJourney = distCovered / journeyLength;
while (i < 1.0f)
{
// Debug.Log("fracJourney In While" + fracJourney);
arrow.position = Vector3.LerpUnclamped(startPos, endPos, fracJourney);
yield return endPos;
}
Debug.LogError("Outside While");
}
But the problem is i have to move object on a constant speed but my object is gaining speed at every loop as i have to make movement in a loop so it tends to move until user wants to end it by input
guys plz help i dont understand what i am doing wrong in Coroutines that the speed of my objects is rising i wat it to stay constant but somehow its not working that way
thanks
As an alternative, you could utilize Unity's AnimationCurve class to map out all kinds of super smooth animation types easily:
You can define the curves in the inspector, or in code
public AnimationCurve Linear
{
get
{
return new AnimationCurve(new Keyframe(0, 0, 1, 1), new Keyframe(1, 1, 1, 1));
}
}
And you can define useage in a coroutine as such:
Vector2.Lerp (startPos, targetPos, aCurve.Evaluate(percentCompleted));
Where "percentCompleted" is your timer/TotalTimeToComplete.
A full example of lerping can be seen from this function:
IEnumerator CoTween(RectTransform aRect, float aTime, Vector2 aDistance, AnimationCurve aCurve, System.Action aCallback = null)
{
float startTime = Time.time;
Vector2 startPos = aRect.anchoredPosition;
Vector2 targetPos = aRect.anchoredPosition + aDistance;
float percentCompleted = 0;
while(Vector2.Distance(aRect.anchoredPosition,targetPos) > .5f && percentCompleted < 1){
percentCompleted = (Time.time - startTime) / aTime;
aRect.anchoredPosition = Vector2.Lerp (startPos, targetPos, aCurve.Evaluate(percentCompleted));
yield return new WaitForEndOfFrame();
if (aRect == null)
{
DeregisterObject(aRect);
yield break;
}
}
DeregisterObject(aRect);
mCallbacks.Add(aCallback);
yield break;
}
Check out this Tween library for more code examples: https://github.com/James9074/Unity-Juice-UI/blob/master/Juice.cs
while (i < 1.0f) will run forever because i is 0.0f and 0.0f is always < 1.0f and there is no place inside your while loop, where you increement i so that it will >= 1.0f. You need a way to exit that while loop. It should have looked like something below:
while (i < 1.0f){
i++ or i= Time.detaTime..... so that this loop will exist at some point.
}
Also your moving function is bad. The function below should do what you are trying to do:
bool isMoving = false;
IEnumerator MoveObject(Transform arrow, Vector3 startPos, Vector3 endPos, float time = 3)
{
//Make sure there is only one instance of this function running
if (isMoving)
{
yield break; ///exit if this is still running
}
isMoving = true;
float counter = 0;
while (counter < time)
{
counter += Time.deltaTime;
arrow.position = Vector3.Lerp(startPos, endPos, counter / time);
yield return null;
}
isMoving = false;
}
Also, in your AnimateArrow(NavMeshPath path) function, replace these three lines of code:
StopCoroutine("MoveObject");
StartCoroutine(MoveObject(arrow.transform, start, end, 3.0f));
yield return null;
with
yield return StartCoroutine(MoveObject(arrow.transform, start, end, 3.0f));
Doing this will wait the MoveObject function to finish before returning and running again in the while loop. You have to replace these inside if (index != path.corners.Length - 1) and else if (index == 0)
Maybe you could multiply the velocity by, say, 0.95f. This will make it accelerate, then stay at a constant speed, and then when you want it to stop it will gradually decelerate. Increasing the 0.95f will cause it to accelerate/decelerate faster.
Related
I want to lerp a projectile through a list of positions in a certain time.
My code works when the positions count is not very high, but when the list contains a thousand points, the time that projectile takes is not the same, it clearly increases.
My code to move the projectile is the following:
public float timeToMove = 3;
public GameObject projectile;
public ParabolaDrawer parabolaDrawer; //this class generates the list of the points
private int currentPosIndex = 0;
private Vector3 currentPos = new Vector3();
private List<Vector3> parabolaPointsPositions = new List<Vector3>();
private bool hasArrivedToPos = false;
private bool hasArrivedToLastPos = false;
[ContextMenu("LaunchProjectile")]
private void LaunchProjectile()
{
currentPosIndex = 0;
hasArrivedToPos = false;
hasArrivedToLastPos = false;
parabolaPointsPositions = parabolaDrawer.parabolaPoints; //returns a List<Vector3>
projectile.transform.position = parabolaDrawer.parabolaPoints.ElementAt(0);
currentPos = projectile.transform.position;
StartCoroutine(MoveProjectileThroughTheParabola(timeToMove));
}
private IEnumerator MoveProjectileThroughTheParabola(float timeToMove)
{
float timeToMoveToNextPosition = timeToMove / (parabolaPointsPositions.Count - 1);
float step = 0f;
Vector3 initialPos = parabolaDrawer.initialPosition;
while (step < 1 && !hasArrivedToLastPos)
{
// Move our position a step closer to the target.
step += Time.smoothDeltaTime / timeToMoveToNextPosition; // calculate step distance to move in stablished time
projectile.transform.position = Vector3.Lerp(initialPos, currentPos, step);
//Check if arrived to the point
if (Vector3.Distance(projectile.transform.position, currentPos) <= 0.001f)
hasArrivedToPos = true;
if (hasArrivedToPos)
{
//Check which is the next point or if has arrived to the last one
if (currentPosIndex == (parabolaPointsPositions.Count - 1))
hasArrivedToLastPos = true;
else
{
initialPos = currentPos;
step = 0f;
currentPosIndex++;
currentPos = parabolaPointsPositions.ElementAt(currentPosIndex);
hasArrivedToPos = false;
}
}
yield return null;
}
}
My test was to set timeToMove to 3, and try to move the object through 1000 points in this 3 seconds, it takes 10.
I guess the problem lies on deltaTime being greater than timeToMoveToNextPosition, but I'm not sure how to fix it, should I calculate currentPosIndex on a different way?
Edit: Providing info on #NSJacob1 answer
If I'm understanding correctly, this is the final code using your method?
private IEnumerator MoveProjectileThroughTheParabola(float timeToMove)
{
Vector3[] points = parabolaDrawer.GetParabolaPoints().ToArray();
float elapsedTime = 0;
float duration = timeToMove;
while(elapsedTime < duration)
{
elapsedTime += Time.deltaTime;
float pathPosition = Time.deltaTime / duration;
float indexPosition = pathPosition * points.Length;
int left = Mathf.FloorToInt(indexPosition);
int right = Mathf.CeilToInt(indexPosition);
float t = indexPosition - left;
Vector3 position = Vector3.Lerp(points[left], points[right], t);
projectile.transform.position = position;
yield return new WaitForEndOfFrame();
}
}
Edit2: Keep improving with info from #NSJacob1 comments
private IEnumerator MoveProjectileThroughTheParabola(float timeToMove)
{
Vector3[] points = parabolaDrawer.GetParabolaPoints().ToArray();
elapsedTime = 0;
duration = timeToMove;
indexPosition = 0;
pathPosition = 0;
right = 0;
left = 0;
while (right < points.Length)
{
pathPosition = elapsedTime / duration;
indexPosition = pathPosition * points.Length;
left = Mathf.FloorToInt(indexPosition);
right = Mathf.CeilToInt(indexPosition);
if(right < points.Length)
{
t = indexPosition - left;
position = Vector3.LerpUnclamped(points[left], points[right], t);
projectile.transform.position = position;
}
else
{
projectile.transform.position = points.Last();
}
elapsedTime += Time.smoothDeltaTime;
yield return null;
}
}
If you move from point to point, you will get a lag based on the number of times you need to change sets of points.Try thinking instead about the whole set of points as a path and decide where, as a float from [0, 1], you are along that path.
If the whole path is traversed in 3 seconds, and there are 1000 points, 1.5 seconds is 0.5f along our path, or point 500.
Here is an example:
private IEnumerator MoveProjectileAlongPath(float timeToMove)
{
Vector3[] points = path;
float duration = timeToMove;
float start = Time.time;
float elapsed = 0;
// Time.deltaTime is the duration of a single frame,
// so this will set position to "after one frame of movement"
while (elapsed < duration )
{
elapsed = Time.time - start;
float pathPosition = elapsed / duration; // our [0, 1] value
float indexPosition = pathPosition * points.Length; // position in path
int left = Mathf.FloorToInt(indexPosition);
int right = Mathf.CeilToInt(indexPosition);
if (right >= segments)
{
transform.position = path[points.Length - 1];
break;
}
float t = indexPosition - left; // percent between left and right position
transform.position = Vector3.Lerp(points[left], points[right], t);
yield return null;
}
}
If you track your previous path position or duration of time seen so far, you can build on each frame. Not by stepping toward a goal, but by figuring out where in the whole traversal you are and going there directly.
The main goal is to move the object between the positions with a delay. This is working fine but other things are not working.
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
public class Waypoints : MonoBehaviour
{
public GameObject objectPrefab;
public LineRenderer lineRenderer;
public List<Transform> pointsToMove;
public float speed;
public bool go = false;
private List<GameObject> objectsToMove;
private List<Vector3> positions;
void Start()
{
objectsToMove = new List<GameObject>();
//Spawn objects and start movement
}
private void Update()
{
if (go && lineRenderer.positionCount > 0 && CurvedLineRenderer.linesSet)
{
positions = GetLinePointsInWorldSpace();
foreach(Transform objToMove in pointsToMove)
{
positions.Add(objToMove.position);
}
for (int i = 0; i < 3; i++)
{
objectsToMove.Add(Instantiate(objectPrefab, transform.parent));
StartCoroutine(Move(i));
}
go = false;
}
}
private IEnumerator Move(int i)
{
//Wait interval
yield return new WaitForSeconds(i * 10);
bool stillTraveling = true;
int pointIndex = 0;
float threshold = 0.1f;
while (stillTraveling)
{
//Check if close to point
if (Vector3.Distance(objectsToMove[i].transform.position, positions[pointIndex]) < threshold)
{
//Check if can move next point
if (pointIndex + 1 > positions.Count - 1)
{
stillTraveling = false;
}
//If not stop moving
else
{
pointIndex++;
}
}
//Move towards point
objectsToMove[i].transform.position = Vector3.MoveTowards(objectsToMove[i].transform.position, positions[pointIndex], speed * Time.deltaTime);
yield return null;
}
}
List<Vector3> GetLinePointsInWorldSpace()
{
var pointsToMove = new Vector3[lineRenderer.positionCount];
//Get the positions which are shown in the inspector
lineRenderer.GetPositions(pointsToMove);
//the points returned are in world space
return pointsToMove.ToList();
}
}
I'm updating once the positions in the Update.
I'm using StartCoroutine once in the Update to send the objects to move between the waypoints with a delay.
private void Update()
{
if (go && lineRenderer.positionCount > 0 && CurvedLineRenderer.linesSet)
{
positions = GetLinePointsInWorldSpace();
foreach(Transform objToMove in pointsToMove)
{
positions.Add(objToMove.position);
}
for (int i = 0; i < 3; i++)
{
objectsToMove.Add(Instantiate(objectPrefab, transform.parent));
StartCoroutine(Move(i));
}
go = false;
}
}
It's working by sending the objects to move with delay between the waypoints, but now I have some other problems.
Because I'm using StartCoroutine for the delay and because I'm setting the go flag to false then the speed value have no affect on the moving objects at run time and I'm not sure how to solve it so the speed will have a factor affecting the moving objects.
Another problem or not a problem but not sure how to do it is how to Update in run time the positions List ? Here in the Update I'm updating the List once :
positions = GetLinePointsInWorldSpace();
foreach(Transform objToMove in pointsToMove)
{
positions.Add(objToMove.position);
}
but I want that if the amount of positions in the function GetLinePointsInWorldSpace or/and the amount of pointsToMove have changed to update the positions List at rune time. I could just call this part in the update nonstop but I'm not sure of it will be too expensive ?
Last problem or how to do is how to make the object to move to move between the waypoints non stop over again from the start or moving backward when getting the last waypoint ? Again because using StartCoroutine for the delay everything is happening once.
If you want to move an object from one point to another, you should use Mathf.Sin(). The is using the sine function, and you put in the amount of cycles times Mathf.PI times 2. Here is what you would write:
float period = 2f;
Vector3 a; //the first point the object goes to
Vector3 b; //the second point that the object goes to
void Update()
{
float cycles = period * Time.time * Mathf.PI;
float amplitude = Mathf.Sin(cycles);
Vector3 location = Vector3.Lerp(a, b, amplitude);
Transform.position = location;
}
I then use Vector3.Lerp to interpolate between point a and point b. This isn’t what you were doing, but it is a much simpler way you can do it.
Vector3 a; //the first point the object goes to
Vector3 b; //the second point that the object goes to
void Update()
{
float cycles = period * Time.time;
Vector3 location = Vector3.Lerp(a, b, cycles);
Transform.position = location;
}
This one is for bringing the object just strait to point b and not back.
If you want there to be a delay between when it moves, here is the script:
float period = 2f;
float waitTime;
bool AtoB = true;
Vector3 a; //the first point the object goes to
Vector3 b; //the second point that the object goes to
void Start()
{
StartCoroutine(“Oscillator”);
}
IEnumerator Oscillator
{
float amplitude;
if (AtoB)
{
amplitude += Time.deltaTime * period;
}
else
{
amplitude -= Time.deltaTime * period;
}
amplitude = Mathf.Clamp(amplitude, 0, 1);
Vector3 location = Vector3.Lerp(a, b, amplitude);
Transform.position = location;
if (amplitude == 1)
{
AtoB = false;
yield return new WaitForSeconds(waitTime);
}
if else (amplitude == 0)
{
AtoB = true;
yield return new WaitForSeconds(waitTime);
}
else
{
yield return false;
}
}
I'm not sure what's going on but one of my coroutines is completely crashing unity. This is the coroutine in question.
IEnumerator attack()
{
currentState = state.attack;
pathFinder.enabled = false;
Vector3 origin = transform.position;
Vector3 attackPos = target.position;
float percent = 0;
float attackSpeed = 3;
while(percent <= 1)
{
percent += Time.deltaTime * attackSpeed;
float interpolation = (-Mathf.Pow(percent, 2) + percent) * 4;
transform.position = Vector3.Lerp(origin, attackPos, interpolation);
yield return null;
}
currentState = state.chase;
pathFinder.enabled = true;
}
A lot of code here might seem bad. I'm following a tutorial, probably heard that one a lot here, and on their end, it seems to be working fine but right when I press play it freezes unity forcing me to close it. Here's the entire script if that would give a better understanding of what could be going on
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;
[RequireComponent(typeof(NavMeshAgent))]
public class Enemy : LivingEntity
{
public enum state { idle, chase, attack};
private state currentState;
private NavMeshAgent pathFinder;
private Transform target;
private float attackThreshhold = .5f;
private float timeBetweenAttacks = 1;
private float attackTimer;
private float myCollisionRadius;
private float targetCollisionRadius;
// Start is called before the first frame update
protected override void Start()
{
base.Start();
currentState = state.chase;
pathFinder = GetComponent<NavMeshAgent>();
target = GameObject.FindGameObjectWithTag("Player").transform;
myCollisionRadius = GetComponent<CapsuleCollider>().radius;
targetCollisionRadius = GetComponent<CapsuleCollider>().radius;
StartCoroutine(updatePath());
}
// Update is called once per frame
void Update()
{
if(Time.time > attackTimer)
{
float sqrDistanceToTarget = (target.position - transform.position).sqrMagnitude;
if (sqrDistanceToTarget < Mathf.Pow(attackThreshhold + myCollisionRadius + targetCollisionRadius, 2)) {
attackTimer = Time.time + timeBetweenAttacks;
StartCoroutine(attack());
}
}
}
IEnumerator attack()
{
currentState = state.attack;
pathFinder.enabled = false;
Vector3 origin = transform.position;
Vector3 attackPos = target.position;
float percent = 0;
float attackSpeed = 3;
while(percent <= 1)
{
percent += Time.deltaTime * attackSpeed;
float interpolation = (-Mathf.Pow(percent, 2) + percent) * 4;
transform.position = Vector3.Lerp(origin, attackPos, interpolation);
yield return null;
}
currentState = state.chase;
pathFinder.enabled = true;
}
IEnumerator updatePath()
{
float refreshRate = .25f;
while(target != null)
{
if(currentState == state.chase)
{
Vector3 dirToTarget = (target.position - transform.position).normalized;
Vector3 targetPosition = target.position - dirToTarget * (myCollisionRadius + targetCollisionRadius/2);
if (!dead) pathFinder.SetDestination(targetPosition);
yield return new WaitForSeconds(refreshRate);
}
}
}
}
Your problem is in the other routine in updatePath.
while(target != null)
{
if(currentState == state.chase)
{
Vector3 dirToTarget = (target.position - transform.position).normalized;
Vector3 targetPosition = target.position - dirToTarget * (myCollisionRadius + targetCollisionRadius/2);
if (!dead) pathFinder.SetDestination(targetPosition);
yield return new WaitForSeconds(refreshRate);
}
}
Problem: In the case your are not in the state state.chase you never yield => you have an infinite while loop!
It should probably rather be
while(target != null)
{
if(currentState == state.chase)
{
var dirToTarget = (target.position - transform.position).normalized;
var targetPosition = target.position - dirToTarget * (myCollisionRadius + targetCollisionRadius / 2);
if (!dead) pathFinder.SetDestination(targetPosition);
yield return new WaitForSeconds(refreshRate);
}
// In other cases wait one frame
else
{
yield return null;
}
}
Because at some point percent goes negative. It also never goes above 1. I would say your math is incorrect. Try removing the negative infront of Mathf.Pow
Math function has nothing to do with crash. Unity crashes because, #SOPMOD is running while loop infinetly. That causing a overflow. Your code tells it should break while loop when percent value is smaller or equal to 1 but you are yielding it null everytime inside the while loop. yield return null tells to compiler, coroutine will run in next frame. So, coroutine start again and percent value is set to 0 again. While loop run again and coroutine again will yield null. Infinite coroutine loop is crashing your app. Move
float percent = 0;
decleration to outside of function. Move it to class level and you will be fine. Because if it is in the class level, it will hold previous value and it will able to reach >1.
Here is How Coroutine yields work
I am trying to lerp from a set of 2 values smoothly over 2 seconds, and then do the inverse over 2 seconds. I've been trying to use Mathf.Lerp or Smoothstep however it seems the value is changing just once and then not reaching the rest of the IEnumerator method. Is my problem with the IEnumerator implementation or within the Lerp?
void OnCollisionEnter2D(Collision2D other)
{
if (other.gameObject.CompareTag("Player")) {
StartCoroutine(DistortScreenFX());
puh.gm.score += 500;
SoundManager.PlaySound("lightspeed1");
Destroy(gameObject);
}
if (other.gameObject.CompareTag("Asteroid")) {
Physics2D.IgnoreCollision(other.gameObject.GetComponent<Collider2D>(), GetComponent<Collider2D>());
}
if (other.gameObject.CompareTag("Ability")) {
Physics2D.IgnoreCollision(other.gameObject.GetComponent<Collider2D>(), GetComponent<Collider2D>());
}
}
IEnumerator DistortScreenFX() {
ChromaticAberration abbFX; //ramp then reset to 0
LensDistortion lensFX; //ramp then reset to 0
pp.profile.TryGetSettings(out abbFX);
pp.profile.TryGetSettings(out lensFX);
var startTime = Time.realtimeSinceStartup;
float duration = 2.0f;
float t = 0;
while (Time.realtimeSinceStartup < startTime + duration) {
t = (Time.realtimeSinceStartup - startTime) / duration;
abbFX.intensity.value = Mathf.SmoothStep(0.0f, 1.0f, t);
lensFX.intensity.value = Mathf.SmoothStep(-25.0f, -100.0f, t);
yield return null;
}
startTime = Time.realtimeSinceStartup;
while (Time.realtimeSinceStartup < startTime + duration) {
t = (Time.realtimeSinceStartup - startTime) / duration;
abbFX.intensity.value = Mathf.SmoothStep(1.0f, 0.0f, t);
lensFX.intensity.value = Mathf.SmoothStep(-100.0f, -25.0f, t);
yield return null;
}
}
You are destroying the game object running the coroutine.
Place the Destroy(gameObject); at the end of the coroutine(after the last yield return null).
If you are running this function using StartCoroutine(DistortScreenFX()) then most likely the function is throwing an exception which is causing it to terminate. Check the logs and see if there is an error.
If you are not using StartCoroutine to call it, then you are calling it wrong.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Rotate : MonoBehaviour
{
public GameObject[] objectsToRotate;
public float duration = 5f;
public static bool desiredAngle = false;
private Vector3 lastFwd;
private bool startRot = true;
private void OnMouseDown()
{
if (startRot == true)
{
startRot = false;
StartCoroutine(StartRotationOfObjects());
}
}
private IEnumerator StartRotationOfObjects()
{
for (int i = 0; i < objectsToRotate.Length; i++)
{
// Random wait period before rotation starts
if (i == 0)
{
yield return new WaitForSeconds(0);
}
else
{
yield return new WaitForSeconds(Random.Range(0, 2f));
}
StartCoroutine(Rotates(objectsToRotate[i].transform, duration));
}
startRot = true;
}
private IEnumerator Rotates(Transform objectToRotate, float duration)
{
Quaternion startRot = objectToRotate.rotation;
float t = 0.0f;
lastFwd = objectToRotate.transform.forward;
while (t < duration)
{
t += Time.deltaTime;
objectToRotate.rotation = startRot * Quaternion.AngleAxis(t / duration * 360f, Vector3.up);
var curFwd = objectToRotate.transform.forward;
// measure the angle rotated since last frame:
var ang = Vector3.Angle(curFwd, lastFwd);
if (myApproximation(ang, 179f, 1f) == true)
{
desiredAngle = true;
}
yield return null;
}
objectToRotate.rotation = startRot;
desiredAngle = false;
}
private bool myApproximation(float a, float b, float tolerance)
{
return (Mathf.Abs(a - b) < tolerance);
}
}
I want to disable the OnMouseDown code so I will not be able to execute the Coroutine nonstop times. And after all the objects finished rotating then to enable the OnMouseDown again. I'm using the startRot flag for that but still it's true all the time and I can keep start the coroutine inside the OnMouseDown nonstop.
Below is a simplified version of your code.
private void OnMouseDown()
{
if (startRot == true)
{
startRot = false;
StartCoroutine(StartRotationOfObjects());
}
}
private IEnumerator StartRotationOfObjects()
{
for (int i = 0; i < objectsToRotate.Length; i++)
{
if (i == 0)
{
yield return new WaitForSeconds(0);
}
else
{
yield return new WaitForSeconds(Random.Range(0, 2f));
}
StartCoroutine(Rotates(objectsToRotate[i].transform, duration));
}
startRot = true;
}
private IEnumerator Rotates(Transform objectToRotate, float duration)
{
while (condition)
{
yield return null;
}
}
First, you have the OnMouseDown checking if true and it is on first run so it enters, sets it to false and start the coroutine.
Moving on with the loop. The first if check is useless since wait for 0 seconds. But it actually impacts the experience. So on first run, it enters the statement and basically does not wait. It then moves on to start the Rotates coroutine.
Entering the coroutine, it will reach a yield statement, place the coroutine in a list of coroutines to run and return. Back in the loop, it runs again, this time it will wait for to 2 seconds and run the next coroutine and so on until last one of the objectsToRotate collection.
Loop is done, startRot is set back to true. This means, even though, your coroutines may not be done, you can trigger new ones.
Your solution, either you want to wait for one rotation to be done before starting a new one with:
if (i == 0)
{
yield return new WaitForSeconds(0);
}
else
{
yield return new WaitForSeconds(Random.Range(0, 2f));
}
yield StartCoroutine(Rotates(objectsToRotate[i].transform, duration));
or you need to keep track of any coroutine running:
private int index = 0;
private IEnumerator StartRotationOfObjects()
{
for (int i = 0; i < objectsToRotate.Length; i++)
{
if (i == 0)
{
yield return new WaitForSeconds(0);
}
else
{
yield return new WaitForSeconds(Random.Range(0, 2f));
}
StartCoroutine(Rotates(objectsToRotate[i].transform, duration);
}
while(index > 0){ yield return null; }
startRot = true;
}
private IEnumerator Rotates(Transform objectToRotate, float duration)
{
index++;
while (condition)
{
yield return null;
}
index--;
}
Now anytime you start a coroutine, index is increased, at the end of the coroutine, it is decreased, in the main coroutine, you yield until index is back to 0.