Piensa en cómo foreach
funciona un :
foreach (var number in Enumerable.Range(1, 1000000))
{
if (number > 10) break;
}
El control sobre la iteración está en la persona que llama; si detiene la iteración (aquí con break
), eso es todo.
La yield
palabra clave es una forma simple de hacer un enumerable en C #. El nombre sugiere esto: yield return
devuelve el control a la persona que llama (en este caso, nuestra foreach
); Es la persona que llama quien decide cuándo continuar con el siguiente elemento. Entonces puedes hacer un método como este:
IEnumerable<int> ToInfinity()
{
var i = 0;
while (true) yield return i++;
}
Parece ingenuo que se ejecutará para siempre; pero en realidad, depende completamente de la persona que llama. Puedes hacer algo como esto:
var range = ToInfinity().Take(10).ToArray();
Esto puede ser un poco confuso si no estás acostumbrado a este concepto, pero espero que también sea obvio que esta es una propiedad muy útil. Era la forma más sencilla en la que podía ceder el control a su interlocutor, y cuando la persona que llama decide hacer un seguimiento, solo puede hacer el siguiente paso (si Unity se creó hoy, probablemente lo usaría en await
lugar de yield
, pero await
no existía) entonces).
Todo lo que necesita para implementar sus propias rutinas (no hace falta decir que las más simples y más estúpidas) es esto:
List<IEnumerable> continuations = new List<IEnumerable>();
void StartCoroutine(IEnumerable coroutine) => continuations.Add(coroutine);
void MainLoop()
{
while (GameIsRunning)
{
foreach (var continuation in continuations.ToArray())
{
if (!continuation.MoveNext()) continuations.Remove(continuation);
}
foreach (var gameObject in updateableGameObjects)
{
gameObject.Update();
}
}
}
Para agregar una WaitForSeconds
implementación muy simple , solo necesita algo como esto:
interface IDelayedCoroutine
{
bool ShouldMove();
}
class Waiter: IDelayedCoroutine
{
private readonly TimeSpan time;
private readonly DateTime start;
public Waiter(TimeSpan time)
{
this.start = DateTime.Now;
this.time = time;
}
public bool ShouldMove() => start + time > DateTime.Now;
}
Y el código correspondiente en nuestro bucle principal:
foreach (var continuation in continuations.ToArray())
{
if (continuation.Current is IDelayedCoroutine dc)
{
if (!dc.ShouldMove()) continue;
}
if (!continuation.MoveNext()) continuations.Remove(continuation);
}
Ta-da: eso es todo lo que necesita un sistema de rutina simple. Y al ceder el control a la persona que llama, la persona que llama puede decidir cualquier cantidad de cosas; podrían tener una tabla de eventos ordenada en lugar de iterar a través de todas las rutinas en cada cuadro; pueden tener prioridades o dependencias. Permite una implementación muy simple de la multitarea cooperativa. Y mira lo simple que es esto, gracias a yield
:)
"Fire1"
, ¿es algo que puede configurar en el motor para permitir reasignaciones de teclas en lugar de escribirKeycode.Foo
?yield
efectivamente es la abreviatura de "Ceda el control a la persona que llama hasta que se solicite el siguiente elemento en el Enumerable".StopAllCoroutines()
en este caso. Está bien cuando solo usas una rutina, pero si alguna vez planeaste tener más de una, esto tendría efectos no deseados. En su lugar, debe usarStopCoroutine()
y simplemente detener el que sea relevante en lugar de todos. (StopAllCoroutines()
sería útil, por ejemplo, al finalizar el nivel o cargar una nueva área, etc., pero no para cosas específicas como "Ya no estoy disparando".)