Чтобы ответить на вопрос в целом: вы можете просто поставить
while (true)
{
...
yield return ...
}
вокруг вашего кода. Пока вы yield
где-то внутри, это совершенно верно для сопрограмм.
Я полагаю, что вы получите одновременные сопрограммы, потому что вы не ждете, пока IncreaseRadius
завершит выбор следующей случайной звезды ... которая может быть такой же, как и раньше.
if (chosenStar.range <= 0f)
{
StartCoroutine(IncreaseRadius(stars[Random.Range(0, stars.Length)], duration));
}
else
{
waitTime = 0f;
}
также вы снова делаете Random.Range, хотя вы уже выбрали другую звезду раньше, это было задумано?
Сначала вообще вместо использования GetComponent<Light>
снова и снова, а довольно просто:
public Light[] stars;
ссылаются на объект точно так же, как и раньше, но теперь вы напрямую имеете дело с Light
ссылками вместо GameObject
.
Тогда вы знаете, что
float duration = Random.Range(3, 8);
фактически возвращает случайные полные int
значения между 3
и 7
. Если вы предпочитаете, чтобы значения float
были также между 3
и 8
, включите, например, например, 3.253453f
тогда вам лучше использовать
var duration = Random.Range(3.0f, 8.0f);
Решение 1 - Только одна звезда за раз
В качестве простой альтернативы вы всегда можете анимировать только одну звезду за раз. Вы можете достичь этого путем yield return
другого IEnumerator
. Это заставляет другого IEnumerator
исполняться и в то же время ожидает его завершения. Что-то вроде
public Light[] stars;
private void Start()
{
StartCoroutine(ChooseStar());
}
private IEnumerator IncreaseRadius(Light star, float duration)
{
Debug.Log("Increasing: " + star.name + " radius: " + star.range);
float counter = 0;
while (counter < duration)
{
counter += Time.deltaTime;
star.range = counter;
yield return null;
}
// again do the decreasing and at the same time wait for it to finish
yield return DecreaseRadius(star);
}
private static IEnumerator DecreaseRadius(Light star)
{
Debug.Log("Decreasing: " + star.name + " radius: " + star.range);
var counter = star.range;
while (star.range >= 0f)
{
counter -= Time.deltaTime;
star.range = counter;
yield return null;
}
star.range = 0f;
}
IEnumerator ChooseStar()
{
// Looks scary but is totally fine in Coroutines as long as you yield somewhere
// instead of starting a new Coroutine simple continue the one you already have
while (true)
{
var duration = Random.Range(3.0f, 8.0f);
var choosenStar = stars[Random.Range(0, stars.Length)];
// This starts the Increase routine on that star
// and at the same time waits for it to finish!
//
// since we also wait until DecreaseRadius is done this means
// at any time only exactly 1 star is animated at the same time
yield return IncreaseRadius(choosenStar, duration);
}
}
Решение 2 - Фильтр случайных чисел
В качестве альтернативы, как вы хотите , чтобы разрешить параллельную анимацию звезд, я бы просто отфильтровал Список доступных звезд (тех, которые в данный момент не анимированы) для получения случайного диапазона. Что-то вроде
public Light[] stars;
// Use a list for dynamically adding and removing items
private List<Light> availableStars = new List<Light>();
private void Start()
{
// initialize the available list
// copy the references from stars
availableStars.AddRange(stars);
StartCoroutine(ChooseStar());
}
private IEnumerator IncreaseRadius(Light star, float duration)
{
Debug.Log("Increasing: " + star.name + " radius: " + star.range);
// As soon as you start animating this star
// remove it from the list of availables
availableStars.Remove(star);
float counter = 0;
while (counter < duration)
{
counter += Time.deltaTime;
star.range = counter;
yield return null;
}
// Decreasing and at the same time wait for it to finish
yield return DecreaseRadius(star);
// when finished add the star again to the availables
availableStars.Add(star);
}
private static IEnumerator DecreaseRadius(Light star)
{
Debug.Log("Decreasing: " + star.name + " radius: " + star.range);
var counter = star.range;
while (star.range >= 0f)
{
counter -= Time.deltaTime;
star.range = counter;
yield return null;
}
star.range = 0f;
}
IEnumerator ChooseStar()
{
// Looks scary but is totally fine in Coroutines as long as you yield somewhere
while (true)
{
var duration = Random.Range(3.0f, 8.0f);
// in case that currently all stars are being animated
// simply wait until the next one becomes available again
yield return new WaitUntil(() => availableStars.Count > 0);
// Pick a random star from the availables instead
var chosenStar = availableStars[Random.Range(0, availableStars.Count)];
// this check becomes then actually redundant
//if (chosenStar.range <= 0f)
//{
StartCoroutine(IncreaseRadius(chosenStar, duration));
yield return new WaitForSeconds(2f);
//}
}
}