溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

Silverlight3游戲開發(fā)之空當(dāng)接龍基礎(chǔ)篇

發(fā)布時間:2020-10-03 10:47:16 來源:網(wǎng)絡(luò) 閱讀:250 作者:googlingman 欄目:開發(fā)技術(shù)


說明:我是我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)代碼。


向AI問一下細(xì)節(jié)

免責(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)容。

AI