There are loads of solutions to that.
The most used one is called PlayerPrefs and some documentation about that can be found in the docs. That approach does not save the data to a file which you can place anywhere you want though so that solution might be out of the question.
The second solution is called XML serialization and can automatically generate an XML file from a class, it can also put together the class from an XML class so you won't need to worry about writing your own parser.
On the wiki there is a full example script using XML serialization.
The third option is using mono's serialization class, it works much like the XML serializer but requires a bit more work but is a lot more stable in return.
I don't have any code here so I don't remember where the documentation is, a search on the forums always yields good results though.
The fourth option is to write a parser of your own, I have tried this and it is quite hard, especially when dealing with floats (vectors too).
Documentation about this can be found when searching for XML text writer or StringWriter.
Hope this gives you some ideas! If you decide to use one option I can explain more about that then.
[Edit]
Well I though a bit about the XML serialization, and actually I don't think it will be very good for your game to store it that way. The XML will look something like this
<Player>
<Score score=245>
<Life life=85>
</Player>
Which means the player will be able to open the XML file in a text editor and change whatever he/she wants, and that isn't so good, is it?
Stable wasn't the right word I think, limited would suite better, for example the XML serializer can't save Hashtables or any special stuff, neither can it save classes other than primitive types (I can't anyway, I have heard that it should work).
So I think the "ordinary" serializer would work better for you.
If you still want to use the XML serializer you can take a look here
The Serializer works like this:
You create a holder class for everything you want to save and then you mark it with the Serializable attribute:
[Serializable ()]
public class SaveData : ISerializable {
public bool foundGem1 = false;
public float score = 42;
public int levelReached = 3;
public SaveData () {
}
}
Then you have to define functions for how the class should be loaded and how it should be saved, these should be put inside the class.
public SaveData (SerializationInfo info, StreamingContext ctxt)
{
//Get the values from info and assign them to the appropriate properties
foundGem1 = (bool)info.GetValue("foundGem1", typeof(bool));
score = (float)info.GetValue("score", typeof(float));
levelReached = (int)info.GetValue("levelReached", typeof(int));
}
//Serialization function.
public void GetObjectData (SerializationInfo info, StreamingContext ctxt)
{
info.AddValue("foundGem1", (foundGem1));
info.AddValue("score", score);
info.AddValue("levelReached", levelReached);
}
The only thing left now is the save and load functions in your script.
public void Save () {
SaveData data = new SaveData ();
Stream stream = File.Open("MySavedGame.game", FileMode.Create);
BinaryFormatter bformatter = new BinaryFormatter();
bformatter.Binder = new VersionDeserializationBinder();
Debug.Log ("Writing Information");
bformatter.Serialize(stream, data);
stream.Close();
}
public void Load () {
SaveData data = new SaveData ();
Stream stream = File.Open("MySavedGame.gamed", FileMode.Open);
BinaryFormatter bformatter = new BinaryFormatter();
bformatter.Binder = new VersionDeserializationBinder();
Debug.Log ("Reading Data");
data = (SaveData)bformatter.Deserialize(stream);
stream.Close();
}
Oh, just one thing more, you need to define a VersionDeserializationBinder class, otherwise you can't load the game when you open it later, something about Unity generates a new key of some sort for the binary formatter, search for "Serialization" on the forum and you can find a post about that.
public sealed class VersionDeserializationBinder : SerializationBinder
{
public override Type BindToType( string assemblyName, string typeName )
{
if ( !string.IsNullOrEmpty( assemblyName ) && !string.IsNullOrEmpty( typeName ) )
{
Type typeToDeserialize = null;
assemblyName = Assembly.GetExecutingAssembly().FullName;
// The following line of code returns the type.
typeToDeserialize = Type.GetType( String.Format( "{0}, {1}", typeName, assemblyName ) );
return typeToDeserialize;
}
return null;
}
}
You will need to use these namespaces to get it working:
using System.Text;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using System;
using System.Runtime.Serialization;
using System.Reflection;
Done, at last :D