Windows Phone 7 XNA 游戏的排行榜






4.81/5 (8投票s)
为 Windows Phone 7 (WP7) XNA 游戏创建排行榜的解决方案
老式高分榜,来源于 Coding Horror
引言
本文讨论了为 Windows Phone 7 (WP7) XNA 游戏创建一个活跃排行榜的解决方案,以便将游戏分数发布到后端存储库,并在游戏中显示前十名分数的排行榜。
背景
我正在为 WP7 开发一系列休闲益智游戏。我希望在这些游戏中实现的一个功能是活跃排行榜或高分榜。当玩家完成游戏时,游戏会将分数发布到一个网页。移动设备也可以从网页请求排行榜。
由于我希望我的排行榜功能与我现有的网站配合使用,因此我想实现一个简单的网页,该网页将接受对当前排行榜的请求,并接受来自 WP7 设备的新游戏分数。
使用代码
数据库
由于我的互联网主机提供商使用 SQL Server 作为后端数据库,因此我将在本文中使用它,以便将其集成到我当前的网站中。
为了开始处理数据库,我在 Microsoft SQL Server Management Studio 中创建了一个名为“Leaderboard”的新数据库。在该“Leaderboard”数据库中,我创建了一个名为“Leaderboard”的新表,其架构如下:
CREATE TABLE [dbo].[Leaderboard](
[RowId] [int] IDENTITY(1,1) NOT NULL,
[GameId] [int] NOT NULL,
[PlayerId] [int] NOT NULL,
[Score] [int] NOT NULL,
[Moves] [int] NOT NULL,
[TimestampUTC] [datetime] NOT NULL,
[TimestampServer] [datetime] NOT NULL,
[TimestampDevice] [datetime] NULL,
[IPAddress] [nvarchar](50) NULL,
[CountryCode] [nvarchar](50) NULL,
[CountryName] [nvarchar](50) NULL,
[RegionName] [nvarchar](50) NULL,
[CityName] [nvarchar](50) NULL,
[ZipPostalCode] [nvarchar](50) NULL,
[Latitude] [nvarchar](50) NULL,
[Longitude] [nvarchar](50) NULL,
[GmtOffset] [nvarchar](50) NULL
) ON [PRIMARY]
RowId
是一个 INT Identity
字段。TimestampUTC
默认为 GETUTCDATE()
,TimestampServer
默认为 GETDATE()
。
您应该有一个额外的表用于存储玩家信息,您可以在 PlayerId 上与之建立关系,但为了使本文保持简单,我只在排行榜中显示 PlayerId。
ASP.NET
该服务可以通过几种不同的方式编写。
一种方法是创建一个网页,该网页将接受查询字符串,解析它以获取相关数据,然后将该数据写入数据库。同一页面还可以通过查询字符串中的正确参数返回现有的排行榜。
另一种方法是创建一个 Web 服务来处理排行榜请求。
发布高分
地理位置信息
在 ASP.NET 代码中,我使用 WP7 设备报告的 IP 地址来获取额外的地理位置信息。我曾考虑在未来的地理位置游戏功能中使用这些信息。如果您想使用此功能,您需要从 IPInfoDB获取免费的 API 密钥。否则,如果您对该功能不感兴趣,只需注释掉整个 try/catch 代码块即可。
try
{
// Get your own API key at http://ipinfodb.com
string ApiKey = "xxxx you'll need to get your own api key xxxx";
string ApiUrlFormat = "http://api.ipinfodb.com/v3/ip-city/?key={0}&ip={1}";
string reqUrl = string.Format(ApiUrlFormat, ApiKey, IpAddress);
HttpWebRequest httpReq = (HttpWebRequest) HttpWebRequest.Create(reqUrl);
string webResponseString = string.Empty;
HttpWebResponse webResponse = (HttpWebResponse) httpReq.GetResponse();
using (StreamReader reader = new StreamReader(webResponse.GetResponseStream()))
{
webResponseString = reader.ReadToEnd();
}
string[] webResponseArray = webResponseString.Split(';');
if (webResponseArray.Length > 0)
{
apiStatus = webResponseArray[0];
apiIp = webResponseArray[2];
apiCountryCode = webResponseArray[3];
apiCountyName = webResponseArray[4];
apiRegionName = webResponseArray[5];
apiCityName = webResponseArray[6];
apiZipPostalCode = webResponseArray[7];
apiLatitude = webResponseArray[8];
apiLongitude = webResponseArray[9];
apiGmtOffset = webResponseArray[10];
}
}
请求排行榜
当 WP7 设备请求排行榜时,网页会查询数据库以获取相关信息。一旦请求的数据存储在 DataTable 中,就会调用 DataTable 的 WriteXml() 方法,将数据和数据结构以 XML 格式写入字符串。此 XML 块将发送到请求的 WP7 设备。
dataTable = dataSet.Tables[0];
using (StringWriter stringWriter = new StringWriter())
{
dataTable.WriteXml(stringWriter);
result = stringWriter.ToString().Trim();
}
为方便测试,我不得不将 Response 对象的缓存设置为一秒后过期,否则我无法在 WP7 设备上看到快速更新。此设置应移至 web.config,以便以后需要时进行更改。
Response.Cache.SetExpires(DateTime.Now.AddSeconds(1));
Windows Phone 7
WP7 项目是一个最精简的 XNA 项目。
用户界面
WP7 用户界面仅包含两个按钮。“Post Score”按钮会将一个随机游戏分数发布到网页。“Get Scores”按钮会从网页请求排行榜并在 WP7 屏幕上显示它。
发布分数
为了获取 WP7 设备的 IP 地址,会异步调用 http://whatismyip.org。
Uri uri = new Uri("http://whatismyip.org");
WebClient webClient = new WebClient();
webClient.DownloadStringCompleted += new DownloadStringCompletedEventHandler(Address_DownloadStringCompleted);
webClient.DownloadStringAsync(uri);
一旦知道了 WP7 设备的 IP 地址,就会将分数发布到网页。
string IpAddress = e.Result;
string GameId = "1";
string PlayerId = "21";
string Score = new Random().Next(999).ToString();
string Moves = new Random().Next(100).ToString();
string PostFormat = "post={0}|{1}|{2}|{3}|{4}";
string Post = String.Format(PostFormat, GameId, PlayerId, Score, Moves, IpAddress);
Uri uri = new Uri("https://:45291/Leaderboard.aspx?" + Post);
WebClient webClient = new WebClient();
webClient.DownloadStringCompleted += new DownloadStringCompletedEventHandler(PostScore_DownloadStringCompleted);
webClient.DownloadStringAsync(uri);
在这里,GameId
是当前正在玩的游戏的 ID。传递 GameId
是为了让排行榜可以维护不同游戏的高分。理论上听起来不错,但在实践中可能效果不佳,因为不同的游戏可能以不同的方式存储分数。
PlayerId
是当前玩家的玩家 ID。应该实现某种注册系统,以便玩家可以注册。但目前,这超出了本文的范围。
当前 URL 设置为测试目的。
请求排行榜
通过在异步调用中将 GameId
传递给网页来请求排行榜。当前 URL 设置为测试目的。
Uri uri = new Uri("https://:45291/Leaderboard.aspx?request=1");
WebClient webClient = new WebClient();
webClient.DownloadStringCompleted += new DownloadStringCompletedEventHandler(Leaderboard_DownloadStringCompleted);
webClient.DownloadStringAsync(uri);
正则表达式
使用正则表达式确保我们只获取 <Leaderboard>
标签之间(包含)的 XML 块。其余的响应对象将被忽略。
result = result.Replace(System.Environment.NewLine, string.Empty);
result = result.Trim();
string xml_block = string.Empty;
System.Text.RegularExpressions.MatchCollection matchCollectionTitle = System.Text.RegularExpressions.Regex.Matches(result, @"
<leaderboard>(.*?)</Leaderboard>");
if (matchCollectionTitle.Count == 1)
{
xml_block = matchCollectionTitle[0].Value;
}
将 XML 块转换为 XML 文档
现在我们有了清理过的 XML 块,它被转换为 XML 文档进行处理。xml 块字符串被转换为字节数组。然后从那里转换为内存流。内存流随后被转换为流读取器。最后,我们将 xml 块放入可以加载到 XML 文档的格式中。
byte[] byteArray = Encoding.UTF8.GetBytes(xml_block);
MemoryStream stream = new MemoryStream(byteArray);
StreamReader streamReader = new StreamReader(stream);
XDocument doc = XDocument.Load(streamReader);
LINQ 和匿名对象
一旦创建了 XML 文档,就可以利用 LINQ 和匿名对象的魔力来解析 XML 文档并填充排行榜。
var xmlLeaderboardEntries = from xmlLeaderboardEntry in doc.Descendants("Table")
select new
{
PlayerId = xmlLeaderboardEntry.Element("PlayerId").Value,
Score = xmlLeaderboardEntry.Element("Score").Value,
Moves = xmlLeaderboardEntry.Element("Moves").Value,
TimestampUTC = xmlLeaderboardEntry.Element("TimestampUTC").Value,
};
leaderboard.Clear();
foreach (var xmlLeaderboardEntry in xmlLeaderboardEntries)
{
LeaderboardEntry leaderboardEntry = new LeaderboardEntry();
leaderboardEntry.PlayerId = xmlLeaderboardEntry.PlayerId;
leaderboardEntry.Score = xmlLeaderboardEntry.Score;
leaderboardEntry.Moves = xmlLeaderboardEntry.Moves;
leaderboardEntry.TimestampUTC = xmlLeaderboardEntry.TimestampUTC;
leaderboard.Add(leaderboardEntry);
}
测试
要测试该解决方案,请在一个 Visual Studio 2010 (VS2010) 实例中加载并执行 Web 项目。在第二个 VS2010 实例中加载 WP7 项目。在 WP7 模拟器中运行 WP7 项目。
在 WP7 模拟器中,点击“Post Score”以发布一些随机分数数据。
点击“Get Scores”以检索当前排行榜。


关注点
我遇到的一项问题是,当我反复尝试从 WP7 项目更新排行榜时,排行榜没有发生变化。WP7 项目一直获取响应的缓存副本。经过一些研究,我发现了一些解决此问题的方法。
我在 ASP.NET 代码中添加了一行,使响应对象缓存在一秒后过期。
或者,您可以在 WP7 项目的查询字符串中添加一个额外的字段,以包含一个随机数或一个日期时间戳。这将导致服务器始终发送新数据而不是缓存副本。
待办事项
本文所述解决方案的功能只是一个概念验证。我需要将解决方案部署到我的网站,将排行榜代码集成到我的一个 Windows Phone 7 游戏中,并进行测试。我计划在将解决方案迁移到我的 Web 主机之前实现一些额外的功能。
- 将连接字符串和其他硬编码的配置设置移至 web.config 文件<
- 将 SQL 代码从 C# 移至数据库本身中的存储过程
- 加密发送到和来自移动设备的数据
- 实现某种移动设备和 ASP.NET 之间的身份验证
摘要
请告诉我您对本文和解决方案的看法。任何问题、疑虑和批评都欢迎。
参考文献
链接
- http://ipinfodb.com
历史
2012-02-26 初始文章