您好,登錄后才能下訂單哦!
說明:我是我2011年發(fā)表在IT168上面的一篇空當(dāng)接龍文章的第一部分,后面幾部分不好查找了。當(dāng)然,我主要側(cè)重學(xué)習(xí)微軟ASP.NET及Silverlight+Windows Phone 7開發(fā)等技術(shù)。搬到此處,僅供學(xué)員參考。另外,注意中僅針對中高級玩家,而且我使用的是隨機的發(fā)牌技術(shù)。
在本篇中,我們將討論空當(dāng)接龍游戲開發(fā)中的基礎(chǔ)編程工作。
一、定義全局變量
本游戲中使用的關(guān)鍵數(shù)據(jù)結(jié)構(gòu)列舉如下:
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:用于限制底部可移動的最大撲克數(shù)。
timer:一個定時器控件,用于控制游戲總進(jìn)程。
CurrentCard:用于存儲當(dāng)前撲克。當(dāng)你單擊左上方可用單元或下部發(fā)牌區(qū)的任意一張撲克(除去右上方的回收單元)時,當(dāng)前撲克即被存儲到此變量中。
InverseColorCard:用于存儲當(dāng)前的反色撲克。
SecondInverseColorCard:用于存儲屏幕下部最近出現(xiàn)的反色撲克。
topElementList:用于記錄屏幕下部可能存在的有效撲克序列,這個序列中的撲克將從一列移動到另一列上。
PreviousMatchedCardInSeryAtBottom:與變量topElementList聯(lián)合應(yīng)用,用于標(biāo)記下部的有效序列中的第一張撲克。
Cells:一個Card數(shù)組,用于記錄屏幕左上方右用單元區(qū)的4張撲克。
FoundationPiles:一個List數(shù)組,用于記錄屏幕右上方回收單元中的四疊撲克。
TableauPiles:一個List數(shù)組,用于記錄下部的8疊撲克。
InverseBehaviorArray:一個InverseColorClickBehavior數(shù)組,用于關(guān)聯(lián)到52張撲克中以實現(xiàn)反色效果。
關(guān)于另外幾個變量的作用,在此不再贅述。在接下來的文章中將結(jié)合代碼介紹其作用。
二、選擇移動多張撲克的對話框
在空當(dāng)接龍游戲中,當(dāng)你可能要移動多張撲克時將彈出一個對話框MoveMultiCardsDlg,如下圖所示。你可以根據(jù)情況,選擇移動一張還是多張。
注意到,在上圖游戲主界面中,在我們剛剛單擊過的下部棧區(qū)存在一組有序的撲克,而當(dāng)前單擊的這一列為空。此時將彈出一個相關(guān)對話框提示你想要把一張還是多張撲克移動到這個空欄。而移動撲克的實際張數(shù)依賴于可用的中介單元的總數(shù)而定。
三、初始化占位符
為了方便確定撲克的位置,我們在本游戲中也引入了一個稱為PlaceHolder的List列表。我們共定義了16個占位符,它們的初始化是在游戲初始化的InitPlaceHolder方法中實現(xiàn)的。主要實現(xiàn)代碼如下:
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個相應(yīng)于左上方的可用單元,4個相應(yīng)于右上方的回收單元,剩下的8個對應(yīng)于下部8列。
下面,我們來討論如何使用上面定義好的數(shù)據(jù)結(jié)構(gòu)來生成撲克與發(fā)牌的問題。
四、生成撲克與發(fā)牌
首先,發(fā)牌操作是在GenerateAndDealCards方法中實現(xiàn)的。
(1)生成52張隨機順序的撲克
首先,應(yīng)當(dāng)以隨機的順序生成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數(shù)組arrPoker來存儲52張隨機順序的撲克。另一個Card數(shù)組OrderPoker用于存儲52張按自小到大順序的撲克。
其次,借助于方法RandomKDiffer的幫助,我們生成序號從0到51的52張不同的撲克。
接下來,存儲在數(shù)組arrPoker中的52張撲克都被初始化為正面向上(其實在空當(dāng)接龍游戲中根本沒有正面朝下的情況)。
最后,一旦生成一張撲克即把一個相關(guān)的InverseColorClickBehavior行為附加到其上。
(2)創(chuàng)建游戲主界面中的初始元素
當(dāng)玩家按下菜單“Game”—“Start”后,界面底部的52張撲克準(zhǔn)備就緒。下圖給出了游戲開始時的運行時快照。
注意到,我們在上部添加了一些矩形控件用于修飾可用單元和回收單元。當(dāng)然,你也可以采用其他的策略,但是我們必須注意由此產(chǎn)生的一些“影響”。
具體來說,本游戲中有兩處涉及到這個“影響”。一處是前面討論過的獲取當(dāng)前撲克牌的方法GetCurrentCard。另一處是我們應(yīng)當(dāng)何時及怎樣添加上述矩形控件。
首先,我們選擇在方法GenerateAndDealCards中添加這些矩形。不過,在單擊“Start”菜單前,屏幕上是看不到這些矩形的。
其次,我們決定在正式加載撲克控件前以動態(tài)方法加載這些矩形相關(guān)的XAML代碼。
那么,如何動態(tài)加載呢?下面討論完成這項任務(wù)的LoadRectangleElementsFirst方法。此方法的關(guān)鍵代碼如下:
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技術(shù)動態(tài)創(chuàng)建XAML元素。
程序開始必須先添加對于程序集System.Xml.Linq.dll的引用。
創(chuàng)建了大量的XElement對象并填充以繁瑣的XAML標(biāo)記。
?在每一個XElement對象中,必須要聲明兩個默認(rèn)的XML命名空間;否則,我們會遇到如下運行時錯誤:AG_E_PARSER_MISSING_DEFAULT_NAMESPACE。
五、把撲克控件添加到容器控件中
在游戲的開始,僅有屏幕下部存在撲克。其中,從左邊起的前4列各有7張隨機生成的撲克,后4列放置6張隨機生成的撲克。相關(guān)代碼如下:
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循環(huán)語句把生成的隨機順序的52張撲克依次添加到容器控件cardContainer中。
六、開始玩游戲
通過菜單控件來控制本游戲的總體流程不但簡化了程序設(shè)計而且使程序操作類似于傳統(tǒng)桌面應(yīng)用。啟動游戲的代碼如下所示:
private void MenuItem_Click(object sender, RoutedEventArgs e){
string sText = (sender as MenuItem).MenuText.Trim();
switch (sText) {
case "Start":
InitAndStartGame();
bStart = true;
break;
//…省略其他
七、再次玩游戲
作為一個完整的游戲程序,提供重玩游戲是基本的要求。顧名思義,在重新開始下一場游戲前應(yīng)當(dāng)把所有數(shù)據(jù)結(jié)構(gòu)恢復(fù)到其最初的數(shù)據(jù)狀態(tài)。事實上,這里面還存 在一定的技巧。一方面,恢復(fù)工作依賴于你所使用的數(shù)據(jù)結(jié)構(gòu);另一方面,恢復(fù)工作還依賴于我們從何位置啟動這個恢復(fù)操作。本游戲中使用的恢復(fù)操作相關(guān)代碼如 下:
private void MenuItem_Click(object sender, RoutedEventArgs e)
{
string sText = (sender as MenuItem).MenuText.Trim();
switch (sText) {
case "Start":
InitAndStartGame();
break;
//…省略其他
在此,通過調(diào)用一個助理方法InitAndStartGame,把所有內(nèi)容準(zhǔn)備就緒。
下面,我們看一下在紙牌游戲和空當(dāng)接龍游戲初始化階段的主要區(qū)別。
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();
}
首先,初始化兩個重要結(jié)構(gòu):TableauPiles和FoundationPiles。在此,我們使用數(shù)據(jù)結(jié)構(gòu)TableauPiles存儲游戲主界面底部的8列撲克,而數(shù)據(jù)結(jié)構(gòu)FoundationPiles用于存儲回收單元中的4列撲克。
接下來,我們創(chuàng)建52個InverseColorClickBehavior類型的行為,它們分別用于關(guān)聯(lián)到52張不同的撲克上。再次強調(diào),在Silverlight編程中,一個行為只能關(guān)聯(lián)到一個UI元素上。
最后應(yīng)當(dāng)注意的是,鼠標(biāo)相關(guān)事件的訂閱是在MainPage控件的MainPage_Loaded方法中完成的,而不是在上面的InitAndStartGame方法中,恕不再贅舉有關(guān)代碼。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。