Silverlight Game – Part 5 – Storing Highscores in Isolated Storage
This is part five in a series of 5 posts showing how I built a simple game in Silverlight, you can follow along (reading the posts in order) to build your own Traffic Jam game.
I would recommend that you also go through ScottGu’s silverlight tutorials as well as the other tutorials on www.silverlight.net
In parts one through four we created a traffic jam game called Seattle Streets. The object of the game was to move cars around and get the red car to reach the exit in as few moves as possible. In this last part we are going to store away high scores for the levels in isolated storage, i.e. we are going to store away the least number of moves the user has been able to complete a level in.
I have choose the following format of the record list:
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<RECORDS>
<RECORD LEVEL="1" MOVES="15" />
<RECORD LEVEL="2" MOVES="23" />
</RECORDS>
So what we will do in this part is to read the current record at the start of a level and display it in the Record TextBlock, and once the red car reaches the exit we will save the score if the number of moves is less than the record for that level.
Silverlight Isolated storage is basically just a folder on the hard drive where you can create files or folders up to a given size limit. In our case the file will be quite small so we don’t have to worry about reaching the limit, but if not you should always check that you haven’t reached the limit before saving something.
The path to your isolated storage differs from operating system to operating system but in Windows 2003 it will be a folder under
C:Documents and Settings<username>Local SettingsApplication DataMicrosoftSilverlight
For this particular application my isolated storage folder was:
C:Documents and Settings<username>Local SettingsApplication DataMicrosoftSilverlightisekrkqr54.wnrtx5hm2dm.szm1s5y2lu35ideijonura3bs1rinntito5zrrijuwcxv1cbmj5dgw0aaaggaf
So while I am developing I can go in here and look at the file to make sure things got written OK.
The isolated storage folders are per user, per application.
1. Create methods to read and write high score records
In order to be able to work with IsolatedStorage and do some IO operations, we need to add the following using statements:
using System.IO.IsolatedStorage;
using System.IO;
Let’s start with getting data from the records.xml file
int GetHighScore(string level)
{
using (IsolatedStorageFile isoStore = IsolatedStorageFile.GetUserStoreForApplication())
{
XDocument doc;
IsolatedStorageFileStream isoStream;
//if the records file does not exist
if (!isoStore.FileExists("records.xml"))
{
return -1;
}
else
{
//read the current data and add a new record or modify the old one
isoStream = new IsolatedStorageFileStream("records.xml", FileMode.Open, FileAccess.Read, isoStore);
doc = XDocument.Load(isoStream);
isoStream.Close();
var records = from record in doc.Descendants("RECORD")
where record.Attribute("LEVEL").Value == level
select new
{
Level = record.Attribute("LEVEL").Value,
Moves = record.Attribute("MOVES").Value
};
if (records.Count() > 0)
{
return Int32.Parse(records.ElementAt(0).Moves);
}
else
{
return -1;
}
}
}
}
IsolatedStorageFile.GetUserStoreForApplication() will get us a pointer to the isolated storage scope for this user.
We check if the records.xml file exists and if not we return –1 showing that no records have been recorded for this level (or any level for that matter).
If it does exist we can get an IsolatedStorageFileStream that we can use just like any stream and pass it to XDocument.Load for example to load up the XML and work on it with Linq to XML.
The next step here is to extract any RECORDS for this particular level, and if we have one we return the number of moves, else we just return –1 again.
Writing the records work very much in the same way:
void WritePotentialHighScore(string level, int moves)
{
using (IsolatedStorageFile isoStore = IsolatedStorageFile.GetUserStoreForApplication())
{
XDocument doc;
IsolatedStorageFileStream isoStream;
//if the records file does not exist
if (!isoStore.FileExists("records.xml"))
{
//create new document
isoStream = new IsolatedStorageFileStream("records.xml", FileMode.Create, isoStore);
doc = new XDocument(new XDeclaration("1.0", "utf-8", "yes"),
new XElement("RECORDS",
new XElement("RECORD",
new XAttribute("LEVEL", level), new XAttribute("MOVES", moves))));
doc.Save(isoStream);
isoStream.Close();
}
else
{
//read the current data and add a new record or modify the old one
isoStream = new IsolatedStorageFileStream("records.xml", FileMode.Open, FileAccess.Read, isoStore);
doc = XDocument.Load(isoStream);
isoStream.Close();
var records = from record in doc.Descendants("RECORD")
where record.Attribute("LEVEL").Value == level
select new
{
Level = record.Attribute("LEVEL").Value,
Moves = record.Attribute("MOVES").Value
};
if (records.Count() > 0)
{
//check to see what the value is and alter it
if (Int32.Parse(records.ElementAt(0).Moves) > moves)
{
foreach(XElement el in doc.Element("RECORDS").Elements("RECORD")){
if (el.Attribute("LEVEL").Value == level)
{
el.Attribute("MOVES").Value = moves.ToString();
}
}
isoStream = new IsolatedStorageFileStream("records.xml", FileMode.Create, FileAccess.Write, isoStore);
doc.Save(isoStream);
isoStream.Close();
}
}
else
{
//otherwise add a new record
doc.Element("RECORDS").Add(new XElement("RECORD",
new XAttribute("LEVEL", level), new XAttribute("MOVES", moves)));
isoStream = new IsolatedStorageFileStream("records.xml", FileMode.Create, FileAccess.Write, isoStore);
doc.Save(isoStream);
isoStream.Close();
}
}
}
}
If the file doesn’t exist we create it and generate the header etc. The tricky part is updating it, and I am not 100% sure that this is the best way to save the data but the way I have done it above is by reading the data, modifying it and then opening a new stream to write it back.
2. Display the high score at the start of a level
With the get and write methods already in place this becomes a pretty easy task. In InitializeLevel we can just add this code
//get the current highscore
int highscore = GetHighScore(levelName.Substring(5));
if (highscore == -1)
tbBest.Text = "-";
else
tbBest.Text = highscore.ToString();
3. Store the high scores when the user finishes a level
Finally, the only part that is missing is storing the high scores when the user finishes a level.
if (currentCar.Orientation == Orientation.Horizontal)
{
double left = Canvas.GetLeft(currentCar);
int col = PosToColOrRow(left);
Canvas.SetLeft(currentCar, col * 60.0);
currentCar.Column = col;
if (currentCar.Column + currentCar.Length == 7)
{
MessageBox.Show("Congratulations");
WritePotentialHighScore(tbLevel.Text, moves+1);
}
}
Only horizontal cars can reach the exit, and the exit is reached when starting column + length = 7. When this is done we display a messagebox and write the “potential” high score to isolated storage. WritePotentialHighScore takes care of checking if it should write it or not.
And with that, the game is finished! ENJOY!!!
Comments
Anonymous
March 04, 2009
This is part five in a series of 5 posts showing how I built a simple game in Silverlight, you can followAnonymous
March 04, 2009
Thank you for submitting this cool story - Trackback from DotNetShoutoutAnonymous
March 04, 2009
Great job on this. I love the fact you just go step by step and dont over explain - just the facts! It is a great tutorial for someone who wants to get there feet wait fast into Silverlight! PS The only thing that slowed me down was sometimes I didn't see the difference between the previously added grayed out code and the new code using my laptop screen so it wasnt obvious out of the gate were some of the code should be placed.Anonymous
March 04, 2009
Where is the "Click here to try it" link? :-)Anonymous
March 08, 2009
.NETASP.NETRoutingperformancecomparetoHttpHandlerLINQonObjectsPerformanceWhy...Anonymous
March 08, 2009
.NET ASP.NET Routing performance compare to HttpHandler LINQ on Objects Performance Why My ASP.NET Application'sAnonymous
March 10, 2009
The comment has been removedAnonymous
April 21, 2009
But this does not work but on the localhost (you cannot access it from another computer on the network) as the "Levels" folder and the isolated storage folder are on the localhost. Right?Anonymous
April 21, 2009
Rami, Isolated storage will be on the client machine, i.e. specific for each client, so it is your highscores rather than everyones highscores. The levels folder, if you are referring to the xml file with info about how the levels look are stored in the xap.Anonymous
May 20, 2009
See this web app http://www.ToolToMeet.com to see other possibilities of using isolated storage