Sokoban Pro是经典Sokoban难题游戏的一个现代版本。本文讨论从XML技术角度来实现该游戏。下图是游戏运行中的一个快照。

最近的版本(v1.0b)相对于现在这个版本包括了下列几个方面的改进:
·"官方"beta发行版
·增加菜单支持
·我发现使窗口的大小在每一级中改变是十分令人恼火的事情,所以在当前版本中它变为固定的
·你可以在不同级间来回跳动,但是仅限于你以前已经完成的级上
·针对级别的撤消功能
·Splash屏幕和图标
·修改了许多错误
·也许还有其它一些
在游戏开始时,你可以创建一个新的玩家或选择一个现有的玩家。既然Sokoban Pro能够保存你的进度,那么你还可以选择是否你想要继续你的上一次游戏。在创建一个玩家后,你可以选择一个级别集合。一个级别集合包含你喜欢的尽可能多的级别。Sokoban Pro带有原来的BoxWorld游戏中的前40个级别。级别集合被存储在XML文件中,这意味着你可以从互联网上不同的Sokoban站点下载级别集合。你还可以创建你自己的级别。当你把它们放到级别目录中时,Sokoban Pro将自动地识别级别集合。
你用一个XML文件来实现保存游戏。它保存最后玩完的级别集合和最后的玩的级别,这样你可以从你上次玩游戏时离开的地方继续玩下去。另外,它还保存你已经完成的级别及相应的得分(移动和推的数目)。如果你重玩一级并且你的执行更好一些的话,那么你的分数将被更新。
基本上,该游戏由下列几个类组成:
·LevelSet-包含所有的有关一个级别集合(作者信息,级别数目,等等……)的信息。它也把级别集合XML中的级别装载到内存中。
·Level-代表在一个级别集合中的一个级别。这里发生的最重要的事情是它追踪所有的你的移动。在玩家移动或推一个盒子时,它更新在一个级别中的项。它更新相应的图形并实现撤消功能,并且最后在屏幕上绘制出级别。
·PlayerData-追踪所有的玩家信息。基本上,它反映出你的SaveGame。
·Board(Form)-主表单处理所有的玩家输入并且初始化所有的对象。
·Players(Form)-让你创建一新的玩家或选择一个现有的玩家。
·Levels(Form)-让你选择一个你想要玩的级别集合。
该应用程序使用读取和写XML文件。例如,下列方法-SaveLevel()-在玩家完成一级后它存储玩家数据。
public void SaveLevel(Level level)
{
XmlDocument doc = new XmlDocument();
doc.Load(filename);
XmlNode lastFinishedLvl = doc.SelectSingleNode("//lastFinishedLevel");
lastFinishedLvl.InnerText = level.LevelNr.ToString();
XmlNode setName = doc.SelectSingleNode("/savegame/levelSets/" +"levelSet[@title = \"" + level.LevelSetName + "\"]");
XmlNode nodeLevel = setName.SelectSingleNode("level[@levelNr = " + level.LevelNr + "]");
if (nodeLevel == null){
XmlElement nodeNewLevel = doc.CreateElement("level");
XmlAttribute xa = doc.CreateAttribute("levelNr");
xa.Value = level.LevelNr.ToString();
nodeNewLevel.Attributes.Append(xa);
XmlElement moves = doc.CreateElement("moves");
moves.InnerText = level.Moves.ToString();
XmlElement pushes = doc.CreateElement("pushes");
pushes.InnerText = level.Pushes.ToString();
nodeNewLevel.AppendChild(moves);
nodeNewLevel.AppendChild(pushes);
setName.AppendChild(nodeNewLevel);
}
else{
XmlElement moves = nodeLevel["moves"];
XmlElement pushes = nodeLevel["pushes"];
int nrOfMoves = int.Parse(moves.InnerText);
int nrOfPushes = int.Parse(pushes.InnerText);
if (level.Pushes < nrOfPushes){
pushes.InnerText = level.Pushes.ToString();
moves.InnerText = level.Moves.ToString();
}
else if (level.Pushes == nrOfPushes && level.Moves < nrOfMoves)
moves.InnerText = level.Moves.ToString();
}
doc.Save(filename);
}
