Welcome, Guest. Please login or register.
Did you miss your activation email?

Login with username, password and session length

 
Advanced search

1056248 Posts in 42892 Topics- by 34842 Members - Latest Member: smoofano

October 22, 2014, 07:48:49 PM
TIGSource ForumsDeveloperTechnical (Moderators: Glaiel-Gamer, ThemsAllTook)Read XML to Unity
Pages: [1]
Print
Author Topic: Read XML to Unity  (Read 4493 times)
Tumetsu
Level 10
*****



View Profile WWW
« on: May 02, 2013, 06:40:49 AM »

I'm having a bit trouble figuring out how to get this working. I want to load some data from xml-file in Unity. I think that I understand the c# part of the process (it looked rather similar to other languages I have used).

However, I have to admit that I have no idea how to load the xml-data through Unity. I'd like to include the xml-file into project, not as a separate file or download it from url. I found this guide: http://blog.sokay.net/2012/08/14/loading-xml-in-unity-building-a-stage-from-xml/ and tried to load the xml (ogmo level) with
Code:
LevelXMLFile = (TextAsset) Resources.Load("StageData", typeof(TextAsset));
But it always returned null.

So in short how exactly I should go to
  • include xml into Unity project
  • load xml to be parsed?

Note that I'm pretty new to Unity specific structures (I'd prefer specific references to functions, class etc. so that I can research them on my own) and I use C#, so if possible take that in account when answering.

EDIT: I got a bit further. I managed to load the xml into Unity by saving it into .txt and using this code:

Code:
    public TextAsset levelData;
    StringReader xml;
   
// Use this for initialization
void Start ()
    {
        xml = new StringReader(levelData.text);
...

However, atm I'm not sure how I should parse this as XML in C# since every source talks about loading it directly from file. How I should convert Stringreader to the xml?
« Last Edit: May 02, 2013, 07:15:57 AM by Tumetsu » Logged

Fallsburg
Level 10
*****


Fear the CircleCat


View Profile
« Reply #1 on: May 02, 2013, 08:40:02 AM »

So, here's how I parse Ogmo into Unity:

Editor/OgmoImporter.cs
Code:
using UnityEditor;
using UnityEngine;
using System.IO;
 
public class OgmoImporter : AssetPostprocessor
{
    public static void OnPostprocessAllAssets(string[] importedAssets, string[] deletedAssets, string[] movedAssets, string[] movedFromAssetPaths)
    {
        foreach (string asset in importedAssets)
        {
            if (asset.EndsWith(".oel"))
            {
                string filePath = asset.Substring(0, asset.Length - Path.GetFileName(asset).Length) + "Generated Assets/";
                string newFileName = filePath + Path.GetFileNameWithoutExtension(asset) + ".txt";
 
                if (!Directory.Exists(filePath))
                {
                    Directory.CreateDirectory(filePath);
                }
 
                StreamReader reader = new StreamReader(asset);
                string fileData = reader.ReadToEnd();
                reader.Close();
 
                FileStream resourceFile = new FileStream(newFileName, FileMode.OpenOrCreate, FileAccess.Write);
                StreamWriter writer = new StreamWriter(resourceFile);
                writer.Write(fileData);
                writer.Close();
                resourceFile.Close();
 
                AssetDatabase.Refresh(ImportAssetOptions.Default);

FileUtil.DeleteFileOrDirectory(filePath + Path.GetFileNameWithoutExtension(asset) + "_possibleSizes.txt");
            }
        }
    }
 
}

OgmoLevel.cs
Code:
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System.Xml;

public class OgmoLevel {
public string name;
public int width;
public int height;
public Dictionary<string,OgmoLayer> layers;
public OgmoLevel(TextAsset xmlFile) {
name = xmlFile.name;
XmlDocument xml = new XmlDocument();
xml.LoadXml(xmlFile.text);
XmlNode root = xml.FirstChild;
width = System.Convert.ToInt32(root.Attributes.GetNamedItem("width").Value);
height = System.Convert.ToInt32(root.Attributes.GetNamedItem("height").Value);
XmlNodeList children = root.ChildNodes;
layers = new Dictionary<string,OgmoLayer>();
for (int ii = 0; ii < children.Count; ii++){
OgmoLayer layer = new OgmoLayer(children[ii],width,height);
layers[layer.name] = layer;
}
}
}



OgmoLayer.cs
Code:
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System.Xml;
public enum LayerType{
TILES,
GRID,
ENTITIES
}
public class OgmoLayer {
public string name;
public int tileWidth;
public int tileHeight;
public List<OgmoEntity> entities;
public int[,] tiles;

public OgmoLayer(XmlNode layerNode, int width, int height){

entities = new List<OgmoEntity>();
LayerType type = LayerType.ENTITIES;
name = layerNode.Name;
if (layerNode.Attributes.GetNamedItem("tileset") != null){
type  = LayerType.TILES;
}
else if (layerNode.Attributes.GetNamedItem("exportMode") != null){
type = LayerType.GRID;
}

switch (type){
case LayerType.ENTITIES:
ParseEntityLayer(layerNode);
break;
case LayerType.TILES:
ParseTileLayer(layerNode,width,height);
break;
case LayerType.GRID:
ParseGridLayer(layerNode,width,height);
break;
}

}

protected void ParseEntityLayer(XmlNode layerNode){
tiles = new int[0,0];
tileWidth = 0;
tileHeight = 0;

for (int ii=0; ii < layerNode.ChildNodes.Count; ii++) {
entities.Add (new OgmoEntity(layerNode.ChildNodes[ii]));
    }
}

protected void ParseGridLayer(XmlNode layerNode, int width, int height){
string text = layerNode.InnerText;
string[] textLines = text.Split(new string[2]{"\n","\r"},System.StringSplitOptions.RemoveEmptyEntries);
tiles = new int[textLines[0].Length,textLines.Length];
for (int ii = 0; ii < tiles.GetLength(0); ii++){
for (int jj = 0; jj < tiles.GetLength(1); jj++){
tiles[ii,jj] = System.Convert.ToInt32(textLines[jj][ii]);
}
}
tileWidth = width/tiles.GetLength(0);
tileHeight = height/tiles.GetLength(1);
}

protected void ParseTileLayer(XmlNode layerNode, int width, int height){
string text = layerNode.InnerText;
string[] textLines = text.Split('\n');
for (int jj = 0; jj < textLines.Length; jj++){
string[] splitLine = textLines[jj].Split(',');
if (tiles == null){
tiles = new int[splitLine.Length,textLines.Length];
}
for (int ii = 0; ii < splitLine.Length; ii++){
tiles[ii,jj] = System.Convert.ToInt32(splitLine[ii]);
}
}
tileWidth = width/tiles.GetLength(0);
tileHeight = height/tiles.GetLength(1);
}
}



OgmoEntity.cs
Code:
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System.Xml;

public class OgmoEntity {
public string name;
public int x;
public int y;
public float width;
public float height;
public List<Vector2> nodes;
public Dictionary<string, string> entityAttributes;
public OgmoEntity(XmlNode node){
XmlAttributeCollection attributes = node.Attributes;
name = node.Name;
width = float.NaN;
height = float.NaN;
entityAttributes = new Dictionary<string, string>();
for (int ii = 0; ii < attributes.Count; ii++){
XmlNode attribute = attributes[ii];
switch(attribute.Name){
case "x":
x = System.Convert.ToInt32(attribute.Value);
break;
case "y":
y = System.Convert.ToInt32(attribute.Value);
break;
case "width":
width = System.Convert.ToSingle(attribute.Value);
break;
case "height":
height = System.Convert.ToSingle(attribute.Value);
break;
default:
entityAttributes[attribute.Name] = attribute.Value;
break;
}
}
nodes = new List<Vector2>();
for (int ii=0; ii < node.ChildNodes.Count; ii++) {
    XmlNode childNode = node.ChildNodes[ii];
attributes = childNode.Attributes;
Vector2 nodePosition = new Vector2( System.Convert.ToSingle(attributes.GetNamedItem("x").Value),
System.Convert.ToSingle(attributes.GetNamedItem("y").Value));
nodes.Add(nodePosition);
    }
}
}



Then it all gets used like:
Code:
string levelFolder = "Levels";

Object[] levels = Resources.LoadAll(levelFolder + "/Generated Assets" , typeof(TextAsset));

List<OgmoLevel> levels = new List<OgmoLevel>();
for (int ii = 0; ii < rooms.Length; ii++){
  levels.Add(new OgmoLevel(rooms[ii] as OgmoLevel);
}


As you discovered, Unity doesn't like TextAssets with odd extensions, so the editor class takes care of that. Beyond that, the code is relatively straight-forward and should handle most of want you need to do.

Caveats:
- My code is shit
- I probably don't actually handle the Ogmo file format to spec. It grabs everything that I need from Ogmo, so you'll have to test and make sure it does what you want.

Logged
Richard Kain
Level 10
*****



View Profile WWW Email
« Reply #2 on: May 02, 2013, 09:12:14 AM »

In order to use the "Resources.Load()" function, I'm pretty sure that any assets you are trying to load must be placed in a folder named "Resources" under the Assets folder. If your XML file isn't in a "Resources" folder, I don't think it is possible to load it using that method.

It is also possible to load text assets through the visual IDE. (drag-and-drop style) But I can certainly see the appeal of wanting to load it through code.
Logged
Fallsburg
Level 10
*****


Fear the CircleCat


View Profile
« Reply #3 on: May 02, 2013, 09:46:33 AM »

Yeah, I assumed he was doing that since he said he was able to loading it after changing it to .txt, but that goes without saying.

Logged
Tumetsu
Level 10
*****



View Profile WWW
« Reply #4 on: May 02, 2013, 10:02:48 AM »

Yeah, I figured out how to load the file through visual IDE and through code Smiley

Thank you Fallsburg for your code, I'll take a look into it soon.
However, I'd like to figure out how to get my textdata to xml-format since I think I should learn it. Atm, I have gotten the correct data into Stringreader and would like to know how to get it into XML-datatype so that I could use System.Xml to parse through it. I have to admit that this has proved to be more difficult than I thought from my AS3 experience :/

If I have understood correctly, both StringReader should act as a stream and XMLDocument.Load can take in a stream. However, when I try to access xml-nodes, I get only null-references.

So how would one convert xml-data in string format to XmlDocument?

EDIT: Looks like I stumbled into solution (or at least it looks like a one)
Code:
xml = new StringReader(xmlObj.text);
        XmlTextReader reader = new XmlTextReader(xml);

        while (reader.Read())
        {
            Debug.Log(reader.Name);
        }
I apparently confused XmlTextReader to XmlDocument. Now Debug.log returned nodes in XML-file, which all I need atm. Something I have to ask though is what is the practical difference between XmlTextReader, XmlReader and XmlDocument?
« Last Edit: May 02, 2013, 10:13:09 AM by Tumetsu » Logged

Fallsburg
Level 10
*****


Fear the CircleCat


View Profile
« Reply #5 on: May 02, 2013, 10:17:05 AM »

Sorry, that's in my code:
Code:
TextAsset xmlFile;  //Get this from the loading of resources or whatever
XmlDocument xml = new XmlDocument();
xml.LoadXml(xmlFile.text);

That's how I handle the xml parsing at least.
Logged
Tumetsu
Level 10
*****



View Profile WWW
« Reply #6 on: May 02, 2013, 11:20:19 AM »

Yeah, I found my solution before I read properly your code Smiley
You seem to use XmlDocument. What is the difference between it and XmlTextReader?
Logged

biomechanic
Level 3
***



View Profile
« Reply #7 on: May 02, 2013, 11:24:03 AM »

XmlDocument is a DOM-like representation of the XML doc, which allows you to freely traverse and change the structure.

The Readers are, well, readers. They go through the document reading it node by node. Not sure about the differences between Xml- and XmlTextReader.
Logged
Tumetsu
Level 10
*****



View Profile WWW
« Reply #8 on: May 02, 2013, 01:48:45 PM »

Ah, that clears it up. Thanks Smiley
Logged

EdgeOfProphecy
Level 2
**



View Profile WWW Email
« Reply #9 on: May 02, 2013, 02:39:30 PM »

I put my XML files into Assets/StreamingAssets, which allows the files to be loaded directly, without going through Resources.Load(), and you can keep them named as .XML files.  Then I use regular mono libraries for manipulating files and XML.

The actual path you load from changes if you're on iOS for whatever reason, like so:

Code:
string levelXMLPath = Application.dataPath + "/Raw/Levels/";
if(!Directory.Exists(levelXMLPath)) {
levelXMLPath = Application.dataPath + "/StreamingAssets/Levels/";
}
Logged
Fallsburg
Level 10
*****


Fear the CircleCat


View Profile
« Reply #10 on: May 02, 2013, 02:56:47 PM »

Just to clarify, you can keep xml files named xml.  It's other files (say .oel for ogmo or .plist for cocos2d) that follow xml specs but have a different file extension that Unity doesn't like.
Logged
Sar
Level 3
***


Likes Violins


View Profile WWW
« Reply #11 on: May 02, 2013, 11:11:26 PM »

Not sure about the differences between Xml- and XmlTextReader.

XmlReader is an abstract class which provides XML-reading methods but cannot be directly instanced; XmlTextReader is an implementation of XmlReader which works on text files.
Logged

Currently working on: Chronicle of Mars
Previous Projects: [Käfer|Tristan and Iseult(TIGS)]
kio
Level 0
*


View Profile
« Reply #12 on: May 03, 2013, 02:46:42 AM »

this one worked nicely for me as a starting point: http://wiki.unity3d.com/index.php?title=Saving_and_Loading_Data:_XmlSerializer

now i use a small template class ( maybe i got it from somewhere in the interwebs, i dont remember... )

really easy to use:
LevelData currentLevel = Serializer.DeserializeTextAsset<LevelData>("XML/levels/level00");
( Path to level00.xml is "../Assets/Resources/XML/levels/level00.xml"


Code:
using UnityEngine;
using System.Collections;

using System.Xml;
using System.Xml.Serialization;
using System.IO;

public static class Serializer
{
static public void Save<T>( T t, string path)
    {
if ( path == "")
{
Debug.LogWarning("saving failed - path error!");
}
else
{
       var serializer = new XmlSerializer(typeof(T));
       using(var stream = new FileStream(path, FileMode.Create))
       {
           serializer.Serialize(stream, t);
       }
}
    }

public static T DeserializeTextAsset<T>(string filename)
{
   TextAsset textAsset = (TextAsset)Resources.Load(filename, typeof(TextAsset));

   if(textAsset == null)
   {
       Debug.LogError("Could not load text asset " + filename);
   }

   return DeserializeString<T>(textAsset.ToString());
}


public static T DeserializeString<T>(string xml)
{
   XmlSerializer serializer = new XmlSerializer(typeof(T));
   StringReader stringReader = new StringReader(xml);
   XmlTextReader xmlReader = new XmlTextReader(stringReader);
   T obj = (T)serializer.Deserialize(xmlReader);

   xmlReader.Close();
   stringReader.Close();

   return obj;
}


public static T LoadFile<T>(string path)
    {
if ( System.IO.File.Exists(path) )
{
var serializer = new XmlSerializer(typeof(T));
var stream = new FileStream(path, FileMode.Open);
var container = (T) serializer.Deserialize(stream);
stream.Close();
return container;
}
else
{
Debug.LogError("file does not exist: " + path );
return default(T);
}
}

}
« Last Edit: May 03, 2013, 03:40:43 AM by kio » Logged
Gimym JIMBERT
Level 10
*****


NOTGAMER ludophile


View Profile Email
« Reply #13 on: May 03, 2013, 12:17:11 PM »

following this
Logged


ILLOGICAL, random guy on internet, do not trust (lelebĉcülo dum borobürükiss)
Pages: [1]
Print
Jump to:  

Theme orange-lt created by panic