Überlegen Sie, wie ein foreach
funktioniert:
foreach (var number in Enumerable.Range(1, 1000000))
{
if (number > 10) break;
}
Die Kontrolle über die Iteration liegt beim Aufrufer - wenn Sie die Iteration stoppen (hier mit break
), ist es das.
Das yield
Schlüsselwort ist eine einfache Möglichkeit, eine Aufzählung in C # zu erstellen. Der Name deutet darauf hin - yield return
gibt dem Anrufer (in diesem Fall unserem foreach
) die Kontrolle zurück ; Es ist der Anrufer, der entscheidet, wann mit dem nächsten Element fortgefahren werden soll. So können Sie eine Methode wie folgt erstellen:
IEnumerable<int> ToInfinity()
{
var i = 0;
while (true) yield return i++;
}
Das sieht naiv so aus, als würde es für immer laufen; In Wirklichkeit hängt es jedoch ganz vom Anrufer ab. Sie können so etwas tun:
var range = ToInfinity().Take(10).ToArray();
Dies kann etwas verwirrend sein, wenn Sie nicht an dieses Konzept gewöhnt sind, aber ich hoffe, es ist auch offensichtlich, dass dies eine sehr nützliche Eigenschaft ist. Es war der einfachste Weg, Ihrem Anrufer die Kontrolle zu geben, und wenn der Anrufer beschließt, nachzufolgen, kann er nur den nächsten Schritt ausführen (wenn Unity heute erstellt wurde, würde es wahrscheinlich await
anstelle von verwenden yield
, existierte aber await
nicht zurück dann).
Alles, was Sie brauchen, um Ihre eigenen Coroutinen zu implementieren (natürlich die einfachsten, dümmsten Coroutinen), ist Folgendes:
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();
}
}
}
Um eine sehr einfache WaitForSeconds
Implementierung hinzuzufügen , benötigen Sie lediglich Folgendes:
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;
}
Und der entsprechende Code in unserer Hauptschleife:
foreach (var continuation in continuations.ToArray())
{
if (continuation.Current is IDelayedCoroutine dc)
{
if (!dc.ShouldMove()) continue;
}
if (!continuation.MoveNext()) continuations.Remove(continuation);
}
Ta-da - das ist alles, was ein einfaches Coroutine-System benötigt. Und indem er dem Anrufer die Kontrolle übergibt, kann der Anrufer über eine beliebige Anzahl von Dingen entscheiden. Sie haben möglicherweise eine sortierte Ereignistabelle, anstatt alle Coroutinen in jedem Frame zu durchlaufen. Sie können Prioritäten oder Abhängigkeiten haben. Es ermöglicht eine sehr einfache Implementierung von kooperativem Multitasking. Und schau dir an, wie einfach das ist, dank yield
:)
"Fire1"
. Können Sie dies in der Engine einrichten, um Neuzuordnungen von Schlüsseln zu ermöglichen, anstatt sie auszutippenKeycode.Foo
?