如何在不停止协程的情况下多次执行协程?
How to execute multiple times a couroutine without stopping it?
我有一系列游戏对象是灯光,我试图随着时间的推移增加和减少点光源的范围大小,问题是灯光有时不会随着时间的推移而减少,它们只是进入范围 0即刻。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public class MainMenu : MonoBehaviour
{
public GameObject[] stars;
private void Start()
{
StartCoroutine(ChooseStar());
}
public void PlayGame()
{
SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex + 1);
}
public void QuitGame()
{
Application.Quit();
}
IEnumerator IncreaseRadius(GameObject star, float duration)
{
Debug.Log("Increasing: "+star.name + " radius: " + star.GetComponent<Light>().range);
float counter = 0;
while (counter < duration)
{
counter += Time.deltaTime;
star.GetComponent<Light>().range = counter;
yield return null;
}
StartCoroutine(DecreaseRadius(star));
}
IEnumerator DecreaseRadius(GameObject star)
{
Debug.Log("Decreasing: "+star.name+" radius: "+ star.GetComponent<Light>().range);
float counter = star.GetComponent<Light>().range;
while (star.GetComponent<Light>().range >= 0f)
{
counter -= Time.deltaTime;
star.GetComponent<Light>().range = counter;
yield return null;
}
star.GetComponent<Light>().range = 0f;
}
IEnumerator ChooseStar()
{
float duration = Random.Range(3, 8);
float waitTime = 2f;
GameObject choosenStar = stars[Random.Range(0, stars.Length)];
if (choosenStar.GetComponent<Light>().range <= 0f)
{
StartCoroutine(IncreaseRadius(stars[Random.Range(0, stars.Length)], duration));
}
else
{
waitTime = 0f;
}
yield return new WaitForSeconds(waitTime);
StartCoroutine(ChooseStar());
}
}
预期结果应该是这个序列:
1 - 从游戏对象数组中随机挑选星星
2 - 检查星星的范围是否已经增加,如果是,则重新开始搜索一个新的,如果没有开始增加。
3 - 光线开始增加直到持续时间,然后调用减少功能
4 - 星号开始减少,当功能结束时它会将范围重置为 0
笼统地回答这个问题:你可以简单地输入一个
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);
实际上 returns 在 3
和 7
之间的随机完整 int
值。如果您希望 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);
//}
}
}
我有一系列游戏对象是灯光,我试图随着时间的推移增加和减少点光源的范围大小,问题是灯光有时不会随着时间的推移而减少,它们只是进入范围 0即刻。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public class MainMenu : MonoBehaviour
{
public GameObject[] stars;
private void Start()
{
StartCoroutine(ChooseStar());
}
public void PlayGame()
{
SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex + 1);
}
public void QuitGame()
{
Application.Quit();
}
IEnumerator IncreaseRadius(GameObject star, float duration)
{
Debug.Log("Increasing: "+star.name + " radius: " + star.GetComponent<Light>().range);
float counter = 0;
while (counter < duration)
{
counter += Time.deltaTime;
star.GetComponent<Light>().range = counter;
yield return null;
}
StartCoroutine(DecreaseRadius(star));
}
IEnumerator DecreaseRadius(GameObject star)
{
Debug.Log("Decreasing: "+star.name+" radius: "+ star.GetComponent<Light>().range);
float counter = star.GetComponent<Light>().range;
while (star.GetComponent<Light>().range >= 0f)
{
counter -= Time.deltaTime;
star.GetComponent<Light>().range = counter;
yield return null;
}
star.GetComponent<Light>().range = 0f;
}
IEnumerator ChooseStar()
{
float duration = Random.Range(3, 8);
float waitTime = 2f;
GameObject choosenStar = stars[Random.Range(0, stars.Length)];
if (choosenStar.GetComponent<Light>().range <= 0f)
{
StartCoroutine(IncreaseRadius(stars[Random.Range(0, stars.Length)], duration));
}
else
{
waitTime = 0f;
}
yield return new WaitForSeconds(waitTime);
StartCoroutine(ChooseStar());
}
}
预期结果应该是这个序列:
1 - 从游戏对象数组中随机挑选星星 2 - 检查星星的范围是否已经增加,如果是,则重新开始搜索一个新的,如果没有开始增加。 3 - 光线开始增加直到持续时间,然后调用减少功能 4 - 星号开始减少,当功能结束时它会将范围重置为 0
笼统地回答这个问题:你可以简单地输入一个
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);
实际上 returns 在 3
和 7
之间的随机完整 int
值。如果您希望 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);
//}
}
}