Sokoban Pro是經典Sokoban難題游戲的一個現代版本。本文討論從XML技術角度來實現該游戲。下圖是游戲運行中的一個快照。
Sokoban Pro是經典Sokoban難題游戲的一個現代版本。該游戲極其簡單,然而該游戲卻是極具挑戰性和富有吸引力。游戲規則是把所有的盒子移動到正確的地方。你可以僅僅推動一個盒子,而不是拉。你可以隨時通過按動U鍵來撤消你的上次移動。
該游戲的一個現代版本如今在Sokoban Pro網站上可以玩了。因為所有的最重要的部分(如讀取/寫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); }