Okay, so formuliert, ich bin sicher, es klingt dunkel, aber ich werde mein Bestes tun, um mein Problem zu beschreiben.
Lassen Sie mich zunächst erklären, was mit meinem echten Quellcode nicht stimmt.
Ich mache einen Plattformer, und hier sind die aktuellen Komponenten meines Spiels: ein Level , das ein eigenes Kachelraster enthält, sowie die Kachelblätter und eine Kachel , die nur den Namen des Kachelblatts und des Index innerhalb des Feldes enthält Fliesenblatt. All dies befindet sich in einem separaten XNA-Spielprojekt namens GameLib.
So werden meine Ebenen in einer Textdatei formatiert:
x x . . . . .
. . . b g r .
. . . . . . .
X X X X X X X
Wo .
eine leere Kachel darstellt, b
eine blaue Plattform darstellt, X
einen Block mit einer zufälligen Farbe darstellt usw.
Anstatt die gesamte Arbeit des Ladens eines Levels innerhalb der Level- Klasse zu erledigen, habe ich beschlossen, die Content-Pipeline-Erweiterung zu nutzen und eine LevelContentPipelineExtension zu erstellen.
Als ich anfing, wurde mir schnell klar, dass ich nicht mehr als 20 Zeilen fest codieren wollte:
if (symbol == 'b')
return new Tile(/*...*/);
In der Hoffnung, zumindest die gesamte Inhaltserstellung in einer Klasse zu zentralisieren, habe ich eine statische Klasse namens LevelContentCreation erstellt , die Informationen darüber enthält, welches Symbol was und all diese Dinge erstellt. Es enthält auch eine Liste aller Asset-Pfade der Kachelblätter, um sie später zu laden. Diese Klasse befindet sich in meinem GameLib-Spielbibliotheksprojekt.
Das Problem ist, dass ich diese statische LevelContentCreation- Klasse sowohl in meiner Level-Klasse (um die eigentlichen Kachelblätter zu laden) als auch in meiner Inhaltspipeline-Erweiterung (um zu wissen, welches Symbol welche Kachel darstellt) verwende ...
Und so sieht meine LevelContentCreation- Klasse aus:
public static class LevelContentCreation
{
private static Dictionary<char, TileCreationInfo> AvailableTiles =
CreateAvailableTiles();
private static List<string> AvailableTileSheets { get; set; }
/* ... rest of the class */
}
Jetzt, da die LevelContentCreation Klasse Teil des Spiel Bibliothek ist, die Content - Pipeline - Erweiterung versucht , es zu benutzen , aber der Anruf zu CreateAvailableTiles () nie passiert , und ich bin nicht in der Lage eine Stufe Objekt aus der Content - Pipeline zu laden.
Ich weiß, dass dies daran liegt, dass die Inhaltspipeline vor der eigentlichen Kompilierung oder ähnlichem ausgeführt wird. Aber wie kann ich das Problem beheben?
Ich kann die LevelContentCreation- Klasse nicht wirklich in die Pipeline-Erweiterung verschieben, da die Level- Klasse sie dann nicht verwenden kann. Ich bin ziemlich verloren und weiß nicht, was ich tun soll. Ich kenne diese sind ziemlich schlechte Designentscheidungen, und ich würde es gerne sehen, wenn mich jemand in die richtige Richtung weisen könnte.
Vielen Dank für Ihre wertvolle Zeit.
Wenn etwas nicht klar genug ist oder ich mehr Informationen / Code bereitstellen sollte, hinterlassen Sie bitte unten einen Kommentar.
Ein bisschen mehr Informationen zu meinen Projekten:
- Spiel - XNA Windows-Spielprojekt. Enthält Verweise auf: Engine, GameLib und GameContent
- GameLib - XNA- Spielbibliotheksprojekt . Enthält Verweise auf: Motor
- GameContent - XNA-Inhaltsprojekt. Enthält Verweise auf: LevelContentPipelineExtension
- Engine - XNA-Spielbibliotheksprojekt. Keine Referenzen.
- LevelContentPipelineExtension - Erweiterung der XNA-Inhaltspipeline. Enthält Verweise auf: Engine und GameLib
Ich weiß so gut wie nichts über XNA 4.0 und bin ein bisschen verloren darüber, wie Inhalte jetzt funktionieren. Wenn Sie weitere Informationen benötigen, sagen Sie es mir.
Antworten:
In Fällen, in denen Sie Singletons, statische Klassen oder globale Variablen verwenden, sollten Sie in 99% der Fälle einen Spieldienst verwenden . Spieledienste werden verwendet, wenn eine Instanz einer Klasse allen anderen Klassen zur Verfügung stehen soll, aber die enge Kopplung der anderen Optionen gibt Ihnen einen lustigen Geschmack. Services haben auch nicht die Instanziierungsprobleme, die Sie gesehen haben, sie sind wiederverwendbarer und können verspottet werden (wenn Sie sich für automatisierte Unit-Tests interessieren) .
Um einen Dienst zu registrieren, müssen Sie Ihrer Klasse eine eigene Schnittstelle geben. Dann ruf einfach an
Game.Services.AddService()
. Rufen Sie an, um diesen Dienst später in einer anderen Klasse zu verwendenGame.Services.GetService()
.In Ihrem Fall würde Ihre Klasse ungefähr so aussehen:
Irgendwann zu Beginn des Programms müssten Sie Ihren Dienst bei registrieren
Game.Services
. Ich bevorzuge dies zu BeginnLoadContent()
, aber einige Leute bevorzugen es, jeden Dienst im Konstruktor dieser Klasse zu registrieren.Wenn Sie jetzt
LevelContentCreation
in einer anderen Klasse auf Ihre Klasse zugreifen möchten, tun Sie dies einfach:Nebenbei bemerkt, dieses Paradigma ist als Inversion of Control (IoC) bekannt und wird auch außerhalb der XNA-Entwicklung häufig verwendet . Das beliebteste Framework für IoC in .Net ist Ninject , das eine bessere Syntax als die Game.Services-Methoden aufweist:
Es unterstützt auch die Abhängigkeitsinjektion , eine Form von IoC, die etwas komplexer ist, aber viel besser zu bearbeiten ist.
[Bearbeiten] Natürlich können Sie diese nette Syntax auch mit XNA erhalten:
quelle
Ich habe tatsächlich einen Weg gefunden, das Problem zu beheben: Ich habe die statische Klasse entfernt und sie zu einer normalen Klasse gemacht. Auf diese Weise bin ich sicher, dass die richtigen Anweisungen zur richtigen Zeit ausgeführt werden.
Ich wünschte, ich hätte deine Zeit nicht so verschwendet, aber ich kam tatsächlich auf die Idee, dies zu tun, indem ich die Kommentare / Antworten las.
Trotzdem danke.
quelle
if
/switch
Anweisungen finden, die einfach verschiedene Typen zurückgeben, möchten Sie wahrscheinlich, dass diese Werte stattdessen Teil der Klasse sind. In Ihrem Fall würde ich eineTileType
Klasse erstellen , die Informationen (Textur, Dateibrief usw.) zu jedem Kacheltyp enthält. Um einenTileType
Brief zu erhalten, durchsuchen Sie einfach Ihre ListeTileTypes
oder speichern Sie eine statische AufladungDictionary<char, TileType>
in derTileType
Klasse , wenn Sie Bedenken hinsichtlich der Leistung haben (Sie können sie automatisch imTileType
Konstruktor ausfüllen ).Tile
(Klasse, die eine einzelne Kachel darstellt, die auf dem Bildschirm angezeigt wird) auf eine einzelne verweisenTileType
. Wenn genügend Kacheln ein eindeutiges Verhalten aufweisen, für das spezieller Code erforderlich ist, sollten Sie stattdessen eine Klassenhierarchie verwenden.Schauen Sie sich das an: http://msdn.microsoft.com/en-us/library/bb447745.aspx
Für das Level-Layout habe ich einfach eine Funktion in meine Level-Klasse eingefügt, die "LoadFromFile (String-Name)" heißt. Wenn Sie später Unterstützung für benutzerdefinierte Karten in Ihrem Spiel hinzufügen möchten, müssen Sie die Ebenen ohnehin aus Ihrem eigenen Dateiformat speichern und laden.
Sie können auch weiterhin einen Inhaltsimporter erstellen, um ihn aus den enthaltenen Assets und aus vom Benutzer bereitgestellten Dateien zu laden. Auf dieser Seite finden Sie weitere Informationen zum Erstellen eines Importers
http://msdn.microsoft.com/en-us/library/bb447743.aspx
quelle