Asynchrones Verhalten für NPC im C # -Spiel

9

Diese Frage bezieht sich auf dieses (Verwenden von C # 5 async, um auf etwas zu warten, das über mehrere Spiel-Frames hinweg ausgeführt wird) .

Kontext

Als Miguel de Icaza das C # 5-asynchrone Framework für Spiele zum ersten Mal auf der Alt-Dev-Conf 2012 präsentierte Ich liebte wirklich die Idee, async und await zu verwenden, um "Skripte" (sozusagen, weil sie in c # sind, und in meinem Fall kompiliert - gerade rechtzeitig, aber trotzdem kompiliert) in Spielen.

Bevorstehende Paradox3D Spiel-Engine scheint zu vertrauen im asynchronen Framework , um auch Skripte zu handhaben, aber aus meiner Sicht gibt es eine echte Lücke zwischen der Idee und der Implementierung.

In der verknüpfte verwandte Frage verwendet jemand await , damit ein NPC eine Befehlsfolge ausführt, während der Rest des Spiels noch läuft.

Idee

Ich möchte einen Schritt weiter gehen und einem NPC erlauben, mehrere Aktionen gleichzeitig auszuführen, während diese Aktionen sequentiell ausgedrückt werden. Etwas in der Art von:

%Vor%

Um das zu tun, habe ich Jon Skeets so wunderbare wie immer Antwort von verwandte Frage .

Implementierung

Meine Spielzeugimplementierung besteht aus zwei Dateien, NPC.cs und Game.cs NPC.cs:

%Vor%

Game.cs:

%Vor%

Dies ist eine ziemlich einfache Implementierung!

Problem

Es scheint jedoch nicht wie erwartet zu funktionieren.

Genauer gesagt, mit NPC_NUMBER bei 10, 100 oder 1000 habe ich kein Problem. Aber bei 10.000 oder mehr wird das Programm nicht mehr vervollständigt, es schreibt "sprechende" Zeilen für eine Weile, dann kommt nichts mehr auf Konsole. Während ich nicht daran denke, 10.000 NPCs gleichzeitig in meinem Spiel zu haben, werden sie auch keine dummen Dialoge schreiben, sondern auch bewegen, animieren, Texturen laden und so weiter. Also würde ich gerne wissen, was mit meiner Implementierung nicht stimmt und ob ich eine Chance habe, es zu beheben.

Ich muss präzisieren, dass der Code unter Mono läuft. Auch der "problematische" Wert könnte bei Ihnen anders sein, es kann eine computerspezifische Sache sein. Wenn das Problem nicht unter .Net zu reproduzieren scheint, werde ich es unter Windows versuchen.

BEARBEITEN

In .Net läuft es bis zu 1000000, obwohl es Zeit benötigt, um zu initialisieren, kann es ein Mono-spezifisches Problem sein. Debugguer-Daten sagen mir, dass es tatsächlich NPCs gibt, die nicht erledigt sind. Keine Information, warum noch, leider.

BEARBEITEN 2

Unter Monodevelop scheint das Starten der Anwendung ohne einen Debugger das Problem zu beheben. Keine Ahnung, warum aber ...

Endwort

Ich weiß, das ist eine wirklich, wirklich lange Frage, und ich hoffe, Sie werden sich die Zeit nehmen, es zu lesen, ich würde wirklich gerne verstehen, was ich falsch gemacht habe.

Vielen Dank im Voraus.

    
dureuill 18.02.2014, 19:04
quelle

2 Antworten

2

Es gibt einen wichtigen Punkt in TaskCompletionSource.SetResult : Der von SetResult ausgelöste Continuation Callback ist normalerweise synchron .

Dies gilt insbesondere für eine single-threaded-Anwendung, bei der kein Synchronisationskontextobjekt wie das Ihre auf dem Hauptthread installiert ist. Ich konnte keine echte asynchronicity in Ihrer Beispiel-App finden, irgendetwas, das einen Threadwechsel verursachen würde, z. %Code%. Im Wesentlichen ähnelt Ihre Verwendung von await Task.Delay() der synchronen Ausführung von Game-Loop-Ereignissen (die mit TaskCompletionSource.SetResult behandelt werden).

Die Tatsache, dass await Game.Frame synchron sein kann (und normalerweise auch wird), wird oft übersehen, aber es kann implizite Rekursion, Stapelüberlauf und Deadlocks verursachen. Ich habe zufällig eine verwandte Frage beantwortet, falls Sie an weiteren Details interessiert sind.

Das heißt, ich konnte auch keine Rekursion in Ihrer App entdecken. Es ist schwer zu sagen, was Mono hier verwirrt. Versuchen Sie periodische Garbage Collection, um zu sehen, ob das hilft:

%Vor%

Aktualisiert , versuchen Sie hier die tatsächliche Nebenläufigkeit einzuführen und sehen Sie, ob sich dadurch etwas ändert. Der einfachste Weg wäre, die Methode SetResult wie folgt zu ändern (Anmerkung Speak ):

%Vor%     
Noseratio 18.02.2014 23:25
quelle
1

Nicht sicher, ob das verwandt ist, aber diese Linie fiel mir auf:

%Vor%

Ich würde empfehlen, auf Ihre Perform -Methode zu warten. Da Sie möchten, dass alle NPCs asynchron ausgeführt werden, fügen Sie ihre Perform Task zu einer Liste hinzu und verwenden Sie Task.WaitAll(...) als Ergänzung.

Im Gegenzug könntest du so etwas tun:

%Vor%

Nur etwas zum Nachdenken.

Ich habe die await/async -Schlüsselwörter mit der Mono Task -Bibliothek ohne Problem benutzt, damit ich nicht so schnell zu Mono springen würde.

    
Erik 18.02.2014 19:19
quelle

Tags und Links