N.B. This is a mirror of a page that no longer exists at its original location.
I needed to be able to save game state, and I wanted to communicate back and forth with websites, and I needed the files to be human readable, and I needed it to be easy [I have been told I am very needy]. The best thing for me to do was to figure out how to read, and write, XML inside Unity3D. It’s a web plugin right? it should support XML processing, and the DOM model right? Well, yes, yes it does.
How does Unity3D do XML?
Well, Unity3D uses the Mono implementation of the .net XML processor. Honestly I didn’t really want to know this, I just wanted to process XML. [did I mention that I am both needy AND demanding?].
In Unity3D (for 2.5 and 2.6 at least) it turns out that XML is best processed through C#.
What you need to do
- Write file open functions in C#
- Write XML read and write functions in C#
- write a function to read the XML and process the results
- write a function to write game data in XML
- Optional: Read and write lists
The instructions are a bit confusing, you may just want to Skip them and, just read source code: Read and Write XML from Unity3D.
Calling C# from JavaScript in Unity3D
My project is coded in JavaScript, because it is the most productive. But, even though calling the .net functions can be done from JavaScript, it is still a bit of a mystery. Processing XML is most natural through C#. So now I will have a project that uses both C# and JavaScript.
Fiddly bit of Unity3D magic
The first thing you have to know is that the C# files have to be “built” first, so the class names can be known to the JavaScript compiler. The Unity3D documentation mentions that files in the Plugins or Editor folders get built before JavaScript.
- Create a C# file, place it in the Plugins directory
I’ll cover how to call the C# stuff from JavaScript later.
Stuff you need to include for C# into Unity3D
using UnityEngine;
using System.Collections.Generic;
using System.Xml;
using System.Xml.Serialization;
using System.IO;
using System.Text;
Unfortunately this (currently) causes a cascade of includes, and you will notice the size of your plugin BLOAT bigtime.
Opening the file
It turns out that you will want to open your save file one way for read, and another way for write.
Most of the howtos on the web show reading and writing an XML file by opening a Stream. I found it is MUCH easier to open a stream to write, and open a “TextReader” for read.
but first, your C# class needs to be defined:
public class GameXML : MonoBehaviour
{
string _FileLocation,_FileName;
StreamWriter _writer;
TextReader _reader;
Then you can open the file. I created one function for read and another for write.
public void OpenSaveFileForWrite()
{
FileInfo t = new FileInfo(_FileLocation+""+ _FileName);
if(!t.Exists)
{
_writer = t.CreateText();
}
else
{
t.Delete();
_writer = t.CreateText();
}
}
public void OpenSaveFileForRead()
{
FileInfo t = new FileInfo(_FileLocation+""+ _FileName);
if(t.Exists)
{
_reader = t.OpenText();
}
else
{
Debug.Log("SaveFile NotFound");
}
}
Yeah Ok, so I am not taking time to figure out indenting. You can check out formatted code in the included sourcecode.
Using Unity3D to write XML, it really is just two function calls
Well, you need a class that describes your games’ save data, but once you have that, all you need to do is turn it into a single text string (Serialization) then write the file out. Also once the data has been serialized, it can be written out using debug routines or printf or whatever, it’s just a big text string.
public void WriteGameDataAndTurnSequence(GameSaveGame SGDat )
{
string _data = SerializeGame(SGDat);
Debug.Log("Game Sequence looks like:" + _data);
_writer.Write(_data);
}
That’s it…Well sort of. The GameSaveGame class needs a little esplination. The function above is called from JavaScript. You can pass a reference to a class back and forth between JavaScript and C#. As long as that class has all the data you need then your good. BUT the tricky bit is building a class structure that describes all the data you need. For most games you’ll have structured data and lists that need to be read and written. This can get a little messy.
Templates, and sequences classes, Oh My
Your Serialize function will serialize ONE instance of a class. So in order to read and write complex data, you need to create a russian doll set of class constructs. Lists of class elements held by a “top level” class.
Ok, this gets messy. I want to write out a list of turns. the .net serialization can serialize a templated List. This is powerful, it means I can write out a list of arbitrary structure.What you need to know is that you will likely want to define 3 classes:
That One class can contain a Sequence(and tons and tons of other stuff). The Sequence is a class that contains a list of class instances. Your sequence is essentially defined as a List
Confused? I sure was.
Bottom line, create 3 class definitions in your C# file.
- One “top level” class that defines the top level structure of your game
- One (or more) “Sequence” classes that hold lists of data
- One “List Element” class that describes the data in each list element
Top Level
So my “top level” class just needs to really define two members, one for “settings” and one for the game’s turn list.
public class GameSaveGame
{
public GameData GameSettings;
public GameSequence Turns;
public GameSaveGame()
{
GameSettings = new GameData();
Turns = new GameGameSequence();
}
public GameSaveGame(GameData pData, GameSequence pGS)
{
GameSettings = pData;
Turns = pGS;
}
}
Sequence
my Sequence GameSequence class so that it would be easy to add elements and stuff like that.
public class GameSequence
{
public List<TurnData> TurnList = new List<TurnData>();
private int Sequence = 0;
public GameSequence () { }
Thee are more convenience functions in the source code, but you get the Idea. Finally, the sequence is a sequence of “TurnData” structures. The “TurnData” class actually describes what a turn is.
List Element
Finally, the inner most nugget. A simple class that describes the data in each turn.
public class TurnData
{
public TurnData (){ Unconditional = false; SequenceNum = 0;}
public TurnData ( int x, int y, int z, int PID, bool cond, int seq)
{
X = x;
Y = y;
Z = z;
PlayerID = PID;
Unconditional = cond;
SequenceNum = seq;
}
Reading XML with Unity3D
Reading is just as easy as writing, but I recommend using a different type of stream to read and parse the XML. Extracting XML into game data is just two steps
- Open the file for read
- De-serialize
Open the file for read….
public void OpenSaveFileForRead()
{
FileInfo t = new FileInfo(_FileLocation+""+ _FileName);
if(t.Exists)
{
_reader = t.OpenText();
}
else
{
Debug.Log("SaveFile NotFound");
}
}
Reading XML with Unity3D: a little magic
Here we actually “do something with XML”. XmlSerializer is the function that parses the formatted xml data and fills in all those classes you defined.
GameSaveGame DeserializeGame(TextReader file)
{
XmlSerializer xdsg = new XmlSerializer(typeof(GameSaveGame));
GameSaveGame newGame;
newGame = (GameSaveGame)xdsg.Deserialize(_reader);
_reader.Close();
return newGame;
}
within Unity3D Calling C# from JavaScript
So, one last thing. How to call the C# functions from Javascript.
- Declare a variable using the name of the C# file (becomes the class name)
- Get the instance of that class
- Fill a data structure with your game save data
- call your unity3D save/load function
It looks something like this
private var SaveLogic : RnWXML; // note I put my CSharp files in the Plugins directory
SaveLogic = GameRootClass.GetComponent(RnWXML);
var NewGame : SaveGame = SaveLogic.ReadSaveGame(); // the SaveGame class is defined in SaveLogic C#
I hope this help someone. There are a lot of twiddly bits of magic that need to be strung together in order to read and write XML. What I have NOT done yet is read and write XML from a URL, which will mean opening up a stream reader and writer to an HTTP connection.
That will be interesting, facebook might be a good source of XML….
Tags:
- Log in to post comments