您好,登錄后才能下訂單哦!
說明:我是我2011年發表在IT168上面的一篇空當接龍文章的第一部分,后面幾部分不好查找了。當然,我主要側重學習微軟ASP.NET及Silverlight+Windows Phone 7開發等技術。搬到此處,僅供學員參考。另外,注意中僅針對中高級玩家,而且我使用的是隨機的發牌技術。
在本篇中,我們將討論空當接龍游戲開發中的基礎編程工作。
一、定義全局變量
本游戲中使用的關鍵數據結構列舉如下:
private int nMaxMovingCards = 13;
DispatcherTimer timer;
private Card CurrentCard;
private Card InverseColorCard;
private Card SecondInverseColorCard;
private Card PreviousMatchedCardInSeryAtBottom;
private List topElementList;
Card PreviousMatchedCardInCells;
int iGlobalCellsIII = -1;
List PlaceHolder;
Card[] Cells=new Card[4];
List[] FoundationPiles ;
List[] TableauPiles;
DateTime timeStart;
int zIndexForAll = 0;
private Cursor originalCursor;
private GameOver gameoverDlg;
private MoveMultiCardsDlg MoveMultiCardsDlg;
InverseColorClickBehavior[] InverseBehaviorArray;
int iGlobalBottomColumn = -1;
int iGlobalBottomColumn2 = -1;
int iGlobalCellsColumn = -1;
下面分別給出這些變量的作用介紹。
nMaxMovingCards:用于限制底部可移動的最大撲克數。
timer:一個定時器控件,用于控制游戲總進程。
CurrentCard:用于存儲當前撲克。當你單擊左上方可用單元或下部發牌區的任意一張撲克(除去右上方的回收單元)時,當前撲克即被存儲到此變量中。
InverseColorCard:用于存儲當前的反色撲克。
SecondInverseColorCard:用于存儲屏幕下部最近出現的反色撲克。
topElementList:用于記錄屏幕下部可能存在的有效撲克序列,這個序列中的撲克將從一列移動到另一列上。
PreviousMatchedCardInSeryAtBottom:與變量topElementList聯合應用,用于標記下部的有效序列中的第一張撲克。
Cells:一個Card數組,用于記錄屏幕左上方右用單元區的4張撲克。
FoundationPiles:一個List數組,用于記錄屏幕右上方回收單元中的四疊撲克。
TableauPiles:一個List數組,用于記錄下部的8疊撲克。
InverseBehaviorArray:一個InverseColorClickBehavior數組,用于關聯到52張撲克中以實現反色效果。
關于另外幾個變量的作用,在此不再贅述。在接下來的文章中將結合代碼介紹其作用。
二、選擇移動多張撲克的對話框
在空當接龍游戲中,當你可能要移動多張撲克時將彈出一個對話框MoveMultiCardsDlg,如下圖所示。你可以根據情況,選擇移動一張還是多張。
注意到,在上圖游戲主界面中,在我們剛剛單擊過的下部棧區存在一組有序的撲克,而當前單擊的這一列為空。此時將彈出一個相關對話框提示你想要把一張還是多張撲克移動到這個空欄。而移動撲克的實際張數依賴于可用的中介單元的總數而定。
三、初始化占位符
為了方便確定撲克的位置,我們在本游戲中也引入了一個稱為PlaceHolder的List列表。我們共定義了16個占位符,它們的初始化是在游戲初始化的InitPlaceHolder方法中實現的。主要實現代碼如下:
private void InitPlaceHolder(){
PlaceHolder = new List(16);
PlaceHolder.Add(new Rect(3 + PADDING, 0 + PADDING, WIDTH, border="1" Height1));//cell1
//……省略其他
PlaceHolder.Add(new Rect(343 + PADDING, 0 + PADDING, WIDTH, border="1" Height1));//topright1
//……省略其他
PlaceHolder.Add(new Rect(0 + PADDING, 114 + PADDING, WIDTH, border="1" Height2));//bottom1
//……省略其他
}
16個占位符中,4個相應于左上方的可用單元,4個相應于右上方的回收單元,剩下的8個對應于下部8列。
下面,我們來討論如何使用上面定義好的數據結構來生成撲克與發牌的問題。
四、生成撲克與發牌
首先,發牌操作是在GenerateAndDealCards方法中實現的。
(1)生成52張隨機順序的撲克
首先,應當以隨機的順序生成52張撲克:
private void GenerateAndDealCards(){
Card[] arrPoker = new Card[52];
Card[] OrderPoker = new Card[52];
int[] RandomI = new int[52];
int curNum;
RandomKDiffer(0, 51, 52, RandomI);
for (int i = 0; i < 52; i++){
curNum = i < 13 ? i + 1 : ((i + 1) % 13 == 0 ? 13 : (i + 1) % 13);//range 1~13
if (i < 13) //0-12
OrderPoker[i] = new Card(curNum, Suits.Clubs);
else if (i < 26) //13-25
OrderPoker[i] = new Card(curNum, Suits.Diamonds);
else if (i < 39) //26-38
OrderPoker[i] = new Card(curNum, Suits.Hearts);
else
OrderPoker[i] = new Card(curNum, Suits.Spades);
arrPoker[RandomI[i]] = OrderPoker[i];//亂序
InverseBehaviorArray[i].Attach(arrPoker[RandomI[i]]);
//……省略其他
}
首先,我們定義一個Card數組arrPoker來存儲52張隨機順序的撲克。另一個Card數組OrderPoker用于存儲52張按自小到大順序的撲克。
其次,借助于方法RandomKDiffer的幫助,我們生成序號從0到51的52張不同的撲克。
接下來,存儲在數組arrPoker中的52張撲克都被初始化為正面向上(其實在空當接龍游戲中根本沒有正面朝下的情況)。
最后,一旦生成一張撲克即把一個相關的InverseColorClickBehavior行為附加到其上。
(2)創建游戲主界面中的初始元素
當玩家按下菜單“Game”—“Start”后,界面底部的52張撲克準備就緒。下圖給出了游戲開始時的運行時快照。
注意到,我們在上部添加了一些矩形控件用于修飾可用單元和回收單元。當然,你也可以采用其他的策略,但是我們必須注意由此產生的一些“影響”。
具體來說,本游戲中有兩處涉及到這個“影響”。一處是前面討論過的獲取當前撲克牌的方法GetCurrentCard。另一處是我們應當何時及怎樣添加上述矩形控件。
首先,我們選擇在方法GenerateAndDealCards中添加這些矩形。不過,在單擊“Start”菜單前,屏幕上是看不到這些矩形的。
其次,我們決定在正式加載撲克控件前以動態方法加載這些矩形相關的XAML代碼。
那么,如何動態加載呢?下面討論完成這項任務的LoadRectangleElementsFirst方法。此方法的關鍵代碼如下:
private void LoadRectangleElementsFirst(){
XElement rectanglestr = XElement.Parse(
@"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Fill="Red" Stroke="Black" border="1" height="32" width="29" Canvas.Left="308" Canvas.Top="29" />");
cardContainer.Children.Add(XamlReader.Load(rectanglestr.ToString()) as UIElement);
XElement p3Str = XElement.Parse(
@"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Fill="White" Stretch="Fill" border="1" height="100" width="1" UseLayoutRounding="False" Canvas.Left="75" Data="M319,100 L319,8" Stroke="#FFBEFF00"/>");
cardContainer.Children.Add(XamlReader.Load(p3Str.ToString()) as UIElement);
XElement p2Str = XElement.Parse(
@"
xmlns="http://schemas.microsoft.com/client/2007"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Fill="White" Stretch="Fill" Stroke="Black" border="1" height="100" width="1" UseLayoutRounding="False" Canvas.Left="0" Data="M319,100 L319,8"/>");
cardContainer.Children.Add(XamlReader.Load(p2Str.ToString()) as UIElement);
//……省略其他
cardContainer.Children.Add(XamlReader.Load(p0_Copy6Str.ToString()) as UIElement);
}
在上面代碼中,有如下幾點值得注意:
使用LINQ to XML技術動態創建XAML元素。
程序開始必須先添加對于程序集System.Xml.Linq.dll的引用。
創建了大量的XElement對象并填充以繁瑣的XAML標記。
?在每一個XElement對象中,必須要聲明兩個默認的XML命名空間;否則,我們會遇到如下運行時錯誤:AG_E_PARSER_MISSING_DEFAULT_NAMESPACE。
五、把撲克控件添加到容器控件中
在游戲的開始,僅有屏幕下部存在撲克。其中,從左邊起的前4列各有7張隨機生成的撲克,后4列放置6張隨機生成的撲克。相關代碼如下:
for (int column = 0; column < 4; column++){
for (int i = 7 * column; i < 7 * (column + 1); i++) {
Canvas.SetLeft(arrPoker[i], (double)PlaceHolder[8 + column].X);
Canvas.SetTop(arrPoker[i], (double)PlaceHolder[8 + column].Y + LARGE_OFFSET * (i - 7 * column));
TableauPiles[column].Add(arrPoker[i]);
cardContainer.Children.Add(arrPoker[i]);
}
}
for (int column = 0; column < 4; column++){
for (int i = 6 * column + 28; i < 6 * column + 34; i++){
Canvas.SetLeft(arrPoker[i], (double)PlaceHolder[12 + column].X);
Canvas.SetTop(arrPoker[i], (double)PlaceHolder[12 + column].Y + LARGE_OFFSET * (i - 6 * column - 28));
TableauPiles[column + 4].Add(arrPoker[i]);
cardContainer.Children.Add(arrPoker[i]);
}
}
上面代碼通過二個簡單的二重foreach循環語句把生成的隨機順序的52張撲克依次添加到容器控件cardContainer中。
六、開始玩游戲
通過菜單控件來控制本游戲的總體流程不但簡化了程序設計而且使程序操作類似于傳統桌面應用。啟動游戲的代碼如下所示:
private void MenuItem_Click(object sender, RoutedEventArgs e){
string sText = (sender as MenuItem).MenuText.Trim();
switch (sText) {
case "Start":
InitAndStartGame();
bStart = true;
break;
//…省略其他
七、再次玩游戲
作為一個完整的游戲程序,提供重玩游戲是基本的要求。顧名思義,在重新開始下一場游戲前應當把所有數據結構恢復到其最初的數據狀態。事實上,這里面還存 在一定的技巧。一方面,恢復工作依賴于你所使用的數據結構;另一方面,恢復工作還依賴于我們從何位置啟動這個恢復操作。本游戲中使用的恢復操作相關代碼如 下:
private void MenuItem_Click(object sender, RoutedEventArgs e)
{
string sText = (sender as MenuItem).MenuText.Trim();
switch (sText) {
case "Start":
InitAndStartGame();
break;
//…省略其他
在此,通過調用一個助理方法InitAndStartGame,把所有內容準備就緒。
下面,我們看一下在紙牌游戲和空當接龍游戲初始化階段的主要區別。
private void InitAndStartGame(){
if (timer != null) {
timer.Tick -= new EventHandler(timer_Tick);
timer.Stop();
timer = null;
}
zIndexForAll = 0;
PlaceHolder = null;
timer = new DispatcherTimer();
timer.Interval = new TimeSpan(0, 0, 1);
timer.Tick += new EventHandler(timer_Tick);
InitPlaceHolder();
TableauPiles = null;
FoundationPiles = null;
TableauPiles = new List[8];
for (int i = 0; i < 8; i++)
TableauPiles[i] = new List();
FoundationPiles = new List[4];
for (int i = 0; i < 4; i++)
FoundationPiles [i] = new List();
InverseBehaviorArray = null;
InverseBehaviorArray = new InverseColorClickBehavior[52];
for (int i = 0; i < 52; i++)
InverseBehaviorArray[i] = new InverseColorClickBehavior();
GenerateAndDealCards();
for (int i = 0; i < 4; i++)
Cells[i] = null;
nMaxMovingCards = 13;
iGlobalCellsIII = -1;
iGlobalBottomColumn = -1;
iGlobalBottomColumn2 = -1;
iGlobalCellsColumn = -1;
bStart = false;
timeStart = new DateTime();
timeStart = DateTime.Now;
timer.Start();
}
首先,初始化兩個重要結構:TableauPiles和FoundationPiles。在此,我們使用數據結構TableauPiles存儲游戲主界面底部的8列撲克,而數據結構FoundationPiles用于存儲回收單元中的4列撲克。
接下來,我們創建52個InverseColorClickBehavior類型的行為,它們分別用于關聯到52張不同的撲克上。再次強調,在Silverlight編程中,一個行為只能關聯到一個UI元素上。
最后應當注意的是,鼠標相關事件的訂閱是在MainPage控件的MainPage_Loaded方法中完成的,而不是在上面的InitAndStartGame方法中,恕不再贅舉有關代碼。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。