Jump to content
Unity Insider Forum

Level per XML-Datei generieren


LarsB

Recommended Posts

Ich habe wieder eine Frage, ob meine Lösung gut ist oder ob ihr Verbesserungsmöglichkeiten seht.

Mein Projekt besteht aus mehreren Leveln mit mehreren Phasen, wobei die Phasen innerhalb eines Level aufeinander aufbauen. Daher kann ich meines Erachtens nicht mit Scenes arbeiten, um die jeweiligen Phasen darzustellen, da die Darstellung der Objekte einer Phase von den vorherigen Phasen abhängig sein könnten.

Ich habe mich daher dazu entschieden, alle Level mit den dazugehörigen Phasen in eine XML-Datei zu packen, die wie folgt aussieht (zu Testzwecken nur mit einem Level mit einer Phase):

<levels>

	<level0001>
        <phase1>
            <field>
                <x_position>1</x_position>
                <y_position>1</y_position>
                <type>0</type>
                <setup>3, 4, 1, 4</setup>
            </field>
            <field>
                <x_position>0</x_position>
                <y_position>1</y_position>
                <type>0</type>
                <setup>3, 4, 1, 4</setup>
            </field>
        </phase1>
    </level0001>

</levels>

Um die XML-Datei auszulesen, habe ich einen LevelHandler gebaut mit einer Methode, die sämtliche Felder einer bestimmte Phase eines Levels in eine Liste packt. Hier der Code dazu:

public XmlNodeList GetFieldArray(string level, string phase)
    {

        levelList = levelDoc.GetElementsByTagName(level);
        foreach (XmlNode levelData in levelList)
        {
            XmlNodeList phaseList = levelData.ChildNodes;
            foreach (XmlNode phaseData in phaseList)
            {
                if (phaseData.Name == phase)
                {
                    XmlNodeList fieldList = phaseData.ChildNodes;
                    return fieldList;
                }
            }
        }
        return null;

    }

Auf diesen LevelHandler greife ich in meinem BoardManager zu:

fieldList = this.gameObject.GetComponent<LevelHandler>().GetFieldArray(level, phase);

Dann gehe ich durch alle Daten der Liste und lese die x-, y-Koordinaten und den Feldtyp aus:

foreach (XmlNode fieldData in fieldList)
{
       XmlNodeList fieldInfo = fieldData.ChildNodes;
       x = int.Parse(fieldInfo[0].InnerText);
       y = int.Parse(fieldInfo[1].InnerText);
       fieldType = int.Parse(fieldInfo[2].InnerText);

       GameObject ... // s.u.

Den Feldtyp habe ich im BoardManager vorher in einem Array definiert:

public GameObject color_field;
private GameObject[] field_type;

void Start () {
        field_type = new GameObject[10];
        field_type[0] = color_field;
  ...

In der XML wird die ID des Feldtyps abgespeichert. Im BoardManager lege ich ein Array mit allen GameObjects an (bislang zu Testzwecken erst eins). Mit den x-, y-Koordinaten und dem Feldtyp kann ich dann in der foreach-Schleife (s.o.) das GameObject für den Level erzeugen:

GameObject newColorField = Instantiate(field_type[fieldType], new Vector3(startX + (xOffset * x), startY + (yOffset * y), 0), field_type[fieldType].transform.rotation);

Das klappt auch alles. Nun meine Frage, ob es noch Verbesserungsmöglichkeiten gibt, insbesondere:

  1. Bietet sich für meinen Fall die Generierung eines Level über XML an?
  2. Soll ich alle Level in eine XML-Datei packen? Es können einige hundert Level mit mehreren Phasen und vielen Feldern werden.
  3. Macht es Sinn, im LevelHandler mit ChildNodes zu arbeiten, um eine bestimmte Phase eines Levels zu erreichen?
  4. Gibt es einen besseren Weg, als die GameObject-ID aus der XML auszulesen und aus dem Ergebnis ein GameObject zu erzeugen?

Danke schon einmal für eure Anmerkungen.

Viele Grüße, Lars

Link zu diesem Kommentar
Auf anderen Seiten teilen

Das ist eigentlich eine Frage der Vorlieben. Ich persönlich mag XML ja nun gar nicht. Das ist mir viel zu umständlich auszulesen.
Ich würde die Daten per JSON generieren und einlesen. Aber wie gesagt. Jeder wie er will.

Ob du nun alle Level mit allen Level-Phasen in eine Datei rein tust oder je Level eine eigene Datei generierst ist eigentlich auch nicht soo von Bedeutung.
Ich persönlich würde je Level eine Datei generieren, denn dann geht das Einladen der Daten zum Levelstart doch viel schneller von der Hand, als wenn du alles auf einmal einliest und dann mit deinen Schleifen auseinanderziehst und in die Listen speicherst. Außerdem sind diese Dateine dann doch viel übersichtlicher, wenn du da mal rein gucken musst, weil evtl. irgendetwas verbugt ist.

Zu 3 Weiß ich nicht was du meinst.

Bei 4 kann ich auch nicht viel zu sagen, weil ich deinen Szenenaufbau nicht kenne. Wären da immer die gleiche Anzahl an Farbfeldern, dann würde ich die Felder schon in der Szene haben und ihnen nur ihre Eingenschaften übergeben bzw. vom Feld direkt holen lassen. Ist aber die Anzahl der Felder variabel, dann würde ich auch instanzieren und dabei gleich die Eigenschaften übergeben.
 

 

Link zu diesem Kommentar
Auf anderen Seiten teilen

vor 2 Minuten schrieb malzbie:

...

Zu 3 Weiß ich nicht was du meinst.

...

Meine XML-Datei ist wie folgt aufgebaut:

<level0001>
   <phase1>
       <field>

In meinem LevelHandler frage ich das wie folgt ab:

levelList = levelDoc.GetElementsByTagName("level0001");		// Abfrage des Levels
foreach (XmlNode levelData in levelList)
{
   XmlNodeList phaseList = levelData.ChildNodes;			// Abfrage der Phasen
   foreach (XmlNode phaseData in phaseList)
   {
   		if (phaseData.Name == phase)
   		{
        	XmlNodeList fieldList = phaseData.ChildNodes;	// Abfrage der Felder
            return fieldList;

Macht das Sinn. Oder gibt es einen besseren Weg, um an die Felder-Liste zu kommen?

 

vor 2 Minuten schrieb malzbie:

Bei 4 kann ich auch nicht viel zu sagen, weil ich deinen Szenenaufbau nicht kenne. Wären da immer die gleiche Anzahl an Farbfeldern, dann würde ich die Felder schon in der Szene haben und ihnen nur ihre Eingenschaften übergeben bzw. vom Feld direkt holen lassen. Ist aber die Anzahl der Felder variabel, dann würde ich auch instanzieren und dabei gleich die Eigenschaften übergeben.

Es sind immer unterschiedliche Anzahl an Feldern. Und es gibt nicht nur Farbfelder sondern auch Weichen, Pipelines und weitere Felder.

 

vor 19 Minuten schrieb malzbie:

Das ist eigentlich eine Frage der Vorlieben. Ich persönlich mag XML ja nun gar nicht. Das ist mir viel zu umständlich auszulesen.
Ich würde die Daten per JSON generieren und einlesen. Aber wie gesagt. Jeder wie er will.

Beides ist aber gleich schnell?!

Link zu diesem Kommentar
Auf anderen Seiten teilen

vor 2 Stunden schrieb LarsB:

Beides ist aber gleich schnell?!

Aktuell sicherlich, aber wenn Du erstmal 50, 100 oder 500 Level hast, wirst Du den Unterschied merken. :) 

Hast Du vor, dafür einen Editor zu bauen? Dann bin ich auf jeden Fall @malzbies Meinung, dass Du lieber JSON nehmen solltest.

Das hat weniger Overhead und zudem hat Unity eigene Methoden bzw. das JsonUtility, das automatisch von und zu JSON serialisiert.

Link zu diesem Kommentar
Auf anderen Seiten teilen

vor 6 Minuten schrieb devandart:

zudem hat Unity eigene Methoden bzw. das JsonUtility, das automatisch von und zu JSON serialisiert.

Das ist richtig, und ich schließe mich der JSON-Empfehlung auch an. Allerdings würde ich hier gerne hinzufügen, dass Unitys JSONUtility nicht sonderlich mächtig ist. Sobald es da um Schachtelung geht, ist der nicht mehr zu gebrauchen. Und da C#, zumindest in Unitys Version, von Haus aus nur XML vernünftig (de)serialisieren kann, muss bei komplexeren Sachen erstmal eine externe JSON-Bibliothek ran. Ist natürlich nur halb so wild; Herunterladen, in die Assets schmeißen und fertig.

Link zu diesem Kommentar
Auf anderen Seiten teilen

  • 2 weeks later...

Um mein Thema abzuschließen: Ich habe LitJson genutzt. Json.NET von Newtonsoft soll zwar schneller sein. Mit LitJson kam ich aber besser klar.

Meine Json-Datei sieht jetzt so aus:

{
  "Levels":[
    {
      "id": 0,
      "xSize": 7,
      "ySize": 7,
      "phases": [
        {
          "id":  0,
          "fields": [
            {
              "xPosition": 1,
              "yPosition": 1,
              "typeID": 0,
              "setup": "3,4,1,4"
            },
            {
              "xPosition": 0,
              "yPosition": 1,
              "typeID": 0,
              "setup": "1,2,4,3"
            }
          ]
        },
        {
          "id": 1,
          "fields": [
            {
              "xPosition": 0,
              "yPosition": 1,
              "typeID": 0,
              "setup": "1,3,5,2"
            },
            {
              "xPosition": 2,
              "yPosition": 1,
              "typeID": 0,
              "setup": "2,1,2,1"
            }
          ]
        }
      ]
    },
    ...

Mit meinem LevelHandler lese ich die Level / Phasen aus:

    private JsonData itemData;
    private JsonData fieldData;

    void Awake()
    {

        string jsonString = File.ReadAllText(Application.dataPath + "/Resources/jLevels.json");
        itemData = JsonMapper.ToObject(jsonString);

    }

    public JsonData GetPhaseJsonData(int level, int phase)
    {

        fieldData = itemData["Levels"][level]["phases"][phase];
        return fieldData;

    }

... und im BoardManagerScript frage ich die Level ab

JsonData fieldData = this.gameObject.GetComponent<LevelHandler>().GetPhaseJsonData(level, phase);

        for (int i = 0; i < fieldData["fields"].Count; i++)
        {
            // Feldeigenschaften aus der Datenbank auslesen
            x = (int)fieldData["fields"][i]["xPosition"];
            y = (int)fieldData["fields"][i]["yPosition"];
            fieldType = (int)fieldData["fields"][i]["typeID"];
          ...

Klappt alles wunderbar. Danke noch einmal für eure Hilfe.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Archiviert

Dieses Thema ist jetzt archiviert und für weitere Antworten gesperrt.

×
×
  • Neu erstellen...