Click here to Skip to main content
Click here to Skip to main content

Tracking High Scores on Windows Phone 7

, 12 Mar 2011
Rate this:
Please Sign up or sign in to vote.
How to implements local high scores in Windows Phone 7.

Another frequent question I come across in user forums is related to how to implement local high scores. The question has come up frequently enough for me to conclude that it is to the benefit of the community to have an implementation available that can be used in Silverlight or XNA that is ready to be used with very little setup.

So I've made a solution for others to use. By default, the component will keep track of up to 10 high scores and will take care of loading and saving itself. If you add a score, the component will take care of ensuring the score is in its proper place and removing scores that are no longer one of the top. For persisting score information, I've made use of the DataSaver<T> code from a previous blog post. I hope others will find the solution easy to use.

To get started with using the component, add a reference to my component to your project. You'll want to instantiate HighScoreList passing an optional file name that it will use to save score information. It's possible to keep track of more than one high score list as long as your instances have different file names. Youe might want to do this if they keep track of scores in different modes separately from each other (e.g.: a score list for Difficult mode, a score list for Easy mode, and so on).

HighScoreList _highScoreList = new HighScoreList("MyScores"); 

Upon instantiation, the component will take care of loading any previous high scores without you doing anything more.

To add a score, create a new instance of ScoreInfo and populate its PlayerName and Score fields. (There is also a ScoreDate field that automatically gets populated with the current date and time). Then use the AddScore(ScoreInfo) method on the HighScoreList instance to add it to the score list.

ScoreInfo scoreInfo = new ScoreInfo(){PlayerName = "Jack", Score = 1048576};
_highScoreList.AddScore(scoreInfo);

And that's it, there's nothing more for you to do. When you make that call, the score gets added to the high score list, scores that are no longer in the top 10 (or whatever you set the limit to be) will fall off the list, and the list will automatically be persisted back to IsolatedStorage so that it is available the next time your game runs. Easy, right?

As a test project, I've created a Silverlight application that allows you to enter new scores and see the behaviour of the component.

image.png

The main bits of the source code are below. First, the ScoreInfo class which is nothing more than a serializable collection of three properties:

/// <span class="code-SummaryComment"><summary>
</span>/// ScoreInfo contains information on a single score
/// <span class="code-SummaryComment"></summary>
</span>[DataContract]
public class ScoreInfo : INotifyPropertyChanged 
{
    // PlayerName - generated from ObservableField snippet - Joel Ivory Johnson
    private string _playerName = String.Empty;

    /// <span class="code-SummaryComment"><summary>
</span>    /// The name of the player that made this score
    /// <span class="code-SummaryComment"></summary>
</span>    [DataMember]
    public string PlayerName
    {
        get { return _playerName; }
            set
            {
                if (_playerName != value)
                {
                    _playerName = value;
                    OnPropertyChanged("PlayerName");
                }
            }
        }
        //-----

                
        // Score - generated from ObservableField snippet - Joel Ivory Johnson
        private int _score = 0;

        /// <span class="code-SummaryComment"><summary>
</span>        /// The score that the player made
        /// <span class="code-SummaryComment"></summary>
</span>        [DataMember]
        public int Score
        {
        get { return _score; }
            set
            {
                if (_score != value)
                {
                    _score = value;
                    OnPropertyChanged("Score");
                }
            }
        }
        //-----

                
        // ScoreDate - generated from ObservableField snippet - Joel Ivory Johnson
        private DateTime _scoreDate = DateTime.Now;

        /// <span class="code-SummaryComment"><summary>
</span>        /// The date and time that the player made the score. If this field is not 
        /// assigned a value it will automatically be assigned with the date and time
        /// that the score isntance was created
        /// <span class="code-SummaryComment"></summary>
</span>        [DataMember]
        public DateTime ScoreDate
        {
            get { return _scoreDate; }
            set
            {
                if (_scoreDate != value)
                {
                    _scoreDate = value;
                    OnPropertyChanged("ScoreDate");
                }
            }
        }
        //-----
        protected void OnPropertyChanged(String propertyName)
        {
            if(PropertyChanged!=null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }

    #region INotifyPropertyChanged Members

    public  event PropertyChangedEventHandler PropertyChanged;

    #endregion
}

And then the HighScoreList class, which is a collection class:

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Runtime.Serialization;


namespace J2i.Net.ScoreKeeper
{
    public class HighScoreList : ObservableCollection<ScoreInfo>, 
                                 INotifyPropertyChanged    
    {
        static DataSaver<HighScoreList> MyDataSaver = 
               new DataSaver<HighScoreList>();

        public HighScoreList()
        {
            
        }

        public HighScoreList(string fileName):this()
        {
            this.ScoreFileName = fileName;
            HighScoreList temp = MyDataSaver.LoadMyData(fileName);
            if(temp!=null)
            {
                foreach(var item in temp)
                {
                    Add(item);
                }
            }
        }
                
        // MaxScoreCount - generated from ObservableField snippet - Joel Ivory Johnson
        private int _maxScoreCount = 10;
        [DataMember]
        public int MaxScoreCount
        {
            get { return _maxScoreCount; }
            set
            {
                if (_maxScoreCount != value)
                {
                    _maxScoreCount = value;
                    OnPropertyChanged("MaxScoreCount");
                }
            }
        }
        //-----

                
        // ScoreFileName - generated from ObservableField snippet - Joel Ivory Johnson
        private string _scoreFileName = "DefaultScores";
        [DataMember]
        public string ScoreFileName
        {
            get { return _scoreFileName; }        
            set
            {
                if (_scoreFileName != value)
                {
                    _scoreFileName = value;
                    OnPropertyChanged("ScoreFileName");
                }
            }
        }
        //-----

                
        // AutoSave - generated from ObservableField snippet - Joel Ivory Johnson
        private bool _autoSave = true;
        [DataMember]
        public bool AutoSave
        {        
            get { return _autoSave; }
            set
            {
                if (_autoSave != value)
                {
                    _autoSave = value;
                    OnPropertyChanged("AutoSave");
                }
            }
        }
        //-----

        static int ScoreComparer(ScoreInfo a, ScoreInfo b)
        {
            return b.Score - a.Score;
        }

        public void SortAndDrop()
        {
            List<ScoreInfo> temp = new List<ScoreInfo>(this.Count);
            foreach(var item in this)
            {
                temp.Add(item);
            }

            if (temp.Count > MaxScoreCount)
            {
                temp.RemoveRange(MaxScoreCount - 1, (temp.Count) - (MaxScoreCount));
            }

            temp.Sort(ScoreComparer);
            this.Clear();

            temp.ForEach((o)=>Add(o));


        }

        public void Save()
        {
            if(String.IsNullOrEmpty(ScoreFileName))
                throw new ArgumentException("A file name wasn't provided");
            MyDataSaver.SaveMyData(this, ScoreFileName);
        }

        public void AddScore(ScoreInfo score)
        {
            this.Add(score);
            SortAndDrop();
            if(AutoSave)
                Save();
        }
        
        protected void OnPropertyChanged(String propertyName)
        {
            if(PropertyChanged!=null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        #region INotifyPropertyChanged Members

        public event PropertyChangedEventHandler PropertyChanged;

        #endregion
    }
}

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Joel Ivory Johnson
Software Developer Razorfish
United States United States
I attended Southern Polytechnic State University and earned a Bachelors of Science in Computer Science and later returned to earn a Masters of Science in Software Engineering.
 
For the past few years I've been providing solutions to clients using Microsoft technologies for web and Windows applications.
 
While most of my CodeProject.com articles are centered around Windows Phone it is only one of the areas in which I work and one of my interests. I also have interest in mobile development on Android and iPhone. Professionally I work with several Microsoft technologies including SQL Server technologies, Silverlight/WPF, ASP.Net and others. My recreational development interest are centered around Artificial Inteligence especially in the area of machine vision.
 
Follow on   Twitter

Comments and Discussions

 
-- There are no messages in this forum --
| Advertise | Privacy | Mobile
Web03 | 2.8.140821.2 | Last Updated 12 Mar 2011
Article Copyright 2011 by Joel Ivory Johnson
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid