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
?