3

I'm building an application where the user needs to be able to change the font colour and size (and the colour of the main camera), plus input an 'unlimited' amount of other data.

I'll admit that I've never used XML before, and am a relative beginner to C# as well. I've done a couple of days of searching on this topic already and the tutorials were all either writing from the wrong language or too vague in their explanations for me to completely understand what I was doing (I've included a list of the links I visited at the end of this question).

Ideally, I'd want to save to two locations, with the following format (apologies for the actual data - I'm not sure of the format some of it saves in):

Application.persistentDataPath, "prefs.xml"

<settings>
    <font>
        <colour>#FFFFFFFF</colour>
        <size>14</size>
    </font>
    <camera>
        <colour>#00000000</colour>
    </camera>
</settings>

Application.persistentDataPath, "data.xml"

<data>
    <section1>
        <item1>
            <date>01/01/2001</date>
            <details>Details here</details>
            <imagepath>Application.persistentDataPath, "image1.jpg"</imagepath>
        </item1>
        <item2>
            <date>03/01/2001</date>
            <details>Details here</details>
        </item2>
    </section1>
    <section2>
        <item1>
            <date>02/01/2001</date>
            <details>Details here</details>
        </item1>
    </section2>
</data>

To choose the colours, I'm using a package from the asset store called Color Picker and using two instances of this, with their game objects named as FontPicker and BackgroundPicker. I know that the next two scripts are completely over the place, but I'm putting them in as reference to what I've fiddled around with so far and what my variable names look like (script mainly edited using the YouTube video linked as reference). The code I currently have in my Settings.cs script is as follows:

public class Settings : MonoBehaviour {

    public Color SelectedColor {get; set;}

    public void Write (string fileName) {
        using (var stream = new FileStream(fileName, FileMode.Create)) {
            XmlSerializer XML = new XmlSerializer(typeof(ColorPicker));
            XML.Serialize(stream, this);
        }
    }

    public void FontColour () {
        var FontPicker = GameObject.Find("FontPicker").GetComponent(ColorPicker);

        // I get the following compiler errors in the Unity editor:
        // Assets/Scripts/Settings.cs(23,77): error CS0119: Expression denotes a `type', where a `variable', `value' or `method group' was expected
        // Assets/Scripts/Settings.cs(23,64): error CS1502: The best overloaded method match for `UnityEngine.GameObject.GetComponent(System.Type)' has some invalid arguments
        // Assets/Scripts/Settings.cs(23,64): error CS1503: Argument `#1' cannot convert `object' expression to type `System.Type'

        Color = ColorPicker.SelectedColor;

        // Assets/Scripts/Settings.cs(24,37): error CS0120: An object reference is required to access non-static member `ColorPicker.SelectedColor'
        // These same compiler errors are present in the next function
    }

    public void BackgroundColour () {
        var BackgroundPicker = GameObject.Find("BackgroundPicker").GetComponent(ColorPicker);
        Color = ColorPicker.SelectedColor;
    }

}

I was hoping I'd be able to write to XML from within these separate functions so that I can call them individually when a colour is selected, but I have no idea where everything's meant to go and which bit of code is meant to be in which script.

From the YouTube video linked below, I also have this script, which ties in in some way:

[XmlRoot ("Preferences")]

public class XMLSerialiser {
    [XmlArray ("Font"), XmlArrayItem("Colour")]
    public Settings[] Settings;

    public void Write(string path){
        var serialiser = new XmlSerializer(typeof(XMLSerialiser));
        using (var stream = new FileStream(path, FileMode.Create)) {
            serialiser.Serialize(stream, this);
        }
    }

    public static Settings Read(string path) {
        var serialiser = new XmlSerializer(typeof(XMLSerialiser));
        using (var stream = new FileStream(path, FileMode.Open)) {
            return serialiser.Deserialize(stream) as XMLSerialiser;
        }
    }
}

What I'd be very grateful for is if someone could either point me in the direction of a good tutorial or explain to me, in very simple terms, how I would go about doing this - what I would have to write, which script I'd have to write it in, and where I'd have to put it in the Unity Editor. I'm very sorry my explanation took up as much space as it did.

Links used:

How to store and retrieve objects using XML?

http://unitynoobs.blogspot.co.uk/2011/02/xml-loading-data-from-xml-file.html

XML Data management in .NET

https://www.youtube.com/watch?v=zAn-ZbJqS90

http://wiki.unity3d.com/index.php?title=Saving_and_Loading_Data%3A_XmlSerializer

http://unitynoobs.blogspot.co.uk/2011/04/xml-writing-to-existing-xml-file.html

Append XML string block to existing XmlDocument

How can I build XML in C#?

Part of what I'm getting confused with is the fact that many of these links seems to use completely different methods to go about it. Take a look at the following two links, for example:

Writing a XML file to iOS with Unity (Using C#)

How to write an XML file in C# in Unity?

Is there a difference between these methods? Is one more preferable than the other?

Community
  • 1
  • 1
Kratos
  • 41
  • 10

1 Answers1

0

So I managed to wrap my head around where I wanted things to go, thanks to a few more days of research (and especially this link).

I'm answering my own question so it may help someone else who has the same problem (and because I guess it's a bit silly to leave it unanswered now I have the solution).

To get around the issue of overwriting things, I used the method shown in the link, making an if loop to check whether the file existed. In the first section (the case where the file does not already exist), I used xmlWriter to put things together. XmlWriter appears to be a lot simpler than the method I use in the other section, with the downside that you don't seem to be able to use it without overwriting everything (hence only using it when the file doesn't already exist).

In the case that the file does exist, xDocument is brilliant. I modified the code from the link so that it replaced elements rather than adding new ones (although that can still be done). For this to work, include system.linq and system.xml (possibly system.xml.linq as well). Here is an example:

   // Loads the document
   XDocument xDocument = XDocument.Load("document.xml");
            // Specifies the encoding
            new XDeclaration("1.0","utf-8","yes");
            // Follows the route settings-font and replaces the current colour 
            // element with a new one
            xDocument.Element("Settings").Element("Font").Element("Colour").ReplaceWith(new XElement("Colour",fontColourHex));
           // As above
            xDocument.Element("Settings").Element("Background").Element("Colour").ReplaceWith(new XElement("Colour",backgroundColourHex));

            // One of the more important things to note is that everything saved in an 
            // element ought to be a string, and the xml file doesn't like element 
            // names with spaces in them (use an underscore instead)
            xDocument.Element("Settings").Element("Font").Element("Size_Modifier").ReplaceWith(new XElement("Size_Modifier",globalFontSizeModifier.ToString()));

            // Saves the document
            xDocument.Save("document.xml");

If you're wondering why there is no whitespace between words in this code, it's because, for some reason, Mono doesn't like it. I did find a fix somewhere else for it, but I couldn't get it to work and deleting the whitespace does the job fine.

The resultant file looked like this:

<?xml version="1.0" encoding="utf-8"?>
<Settings>
  <Font>
    <Colour>FFFFFFFF</Colour>
    <Size_Modifier>0</Size_Modifier>
  </Font>
  <Background>
    <Colour>000000FF</Colour>
  </Background>
</Settings>

After I modified it and tied it to a couple of functions, all I had to do was create a new game object with the script attached and use it as needed.

Community
  • 1
  • 1
Kratos
  • 41
  • 10