目标:遊戲界面顯示 拼圖面闆 、 提示面闆(拼好的樣子)、移動次數。
通過拖動小拼圖檔 進行拼圖 ,最終拼成提示面闆的樣子。提示面闆中的藍色塊表示空白塊。
成功後提示成功!可通過點選重玩按鈕,在任何時候選擇重玩!
大緻如圖:

主要考慮四個問題:
1、界面的繪制 2、随機生成初始狀态 3、拼圖檔移動的判斷和實作 4、成功狀态的判斷
1、繪制界面
考慮到landscape的情況 ,還是用StackPanel爽點,将整個content放在一個StackPanel内,當方向改變的時候,隻要改變StackPanel的Orientation就Ok了
然後将拼圖面闆用Grid 表示,主要是使用行列很友善,下面的 提示面闆 、重玩按鈕、 以及移動次數TextBlock放在一個StackPanel中
xaml中如下:
View Code
<StackPanel x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0" Orientation="Vertical" Loaded="ContentPanel_Loaded">
<Grid Name="puzzlePanel" Margin="0 0" ></Grid>
<StackPanel Name="bottomStackPanel" Orientation="Horizontal" >
<Grid Name="TargetPanel" Grid.Column="0" Margin="20 20 20 20" Width="180" Height="180">
</Grid>
<Grid >
<TextBlock Text="移動次數: 0" Name="moveTimesTextBlock" Grid.Column="1" HorizontalAlignment="Center" Margin="0 30 0 0" VerticalAlignment="Top"></TextBlock>
<Button Name="replayButton" Content="重玩" Margin="40,70,23,70" Click="replayButton_Click" Width="128"></Button>
</Grid> </StackPanel>
</StackPanel>
最重要的是拼圖面闆的繪制,我的思路是 根據需要的小拼圖檔數 給puzzlePanel添加相應數量的行和列,建立拼圖面闆的CreatePuzzlePanel如下:
View Code
//建立拼圖面闆
//擷取puzzlePanel 并為其設定寬高,背景色
//然後為其定義指定的行列,用來存放 小拼圖檔
public void CreatePuzzlePanel()
{
puzzlePanel.Height = puzzlePanelHeight;
puzzlePanel.Width = puzzlePanelWidth;
puzzlePanel.Background = new SolidColorBrush(backgroundColor) ;
for (int i = 0; i < rowNum; i++)
{
puzzlePanel.RowDefinitions.Add(new RowDefinition());
}
for (int j = 0; j < columNum; j++)
{
puzzlePanel.ColumnDefinitions.Add(new ColumnDefinition());
}
Grid.SetRow(puzzlePanel,0);
}
每個拼圖檔使用一個Grid 然後在Grid中放一個TextBlock 用來顯示數字,拼圖檔的大小根據拼圖面闆的大小進行計算
最重要的還有需要設定一個空白的拼圖檔,這裡通過随機生成一個空白的位置來實作,添加的時候這個空白的位置就設為空,就不會顯示拼圖檔了,InitPuzzle方法會記錄下該片對應的值,供建立提示面闆使用。
然後将Grid添加到puzzlePanel容器中,這裡通過設定Grid在puzzlePanel中的Row,Column來實作同時将Grid添加到一個List中,以便移懂拼圖檔的時候使用。這裡設定Grid的Row和Column
通過Grid.SetRow和 Grid.SetColumn實作,當然這裡顯示的數字需要随機生成,是使用CreateRandNumArry方法實作的,這個後面再說。初始化拼圖的 InitPuzzle方法如下
View Code
//初始化拼圖面闆
//首先建立拼圖檔數組,數組大小等于rowNum*columNum
//然後生成生成一個 大小 小于 拼圖檔數組大小 的随機數,該位置不顯示拼圖檔
//然後就是依次生成所需要的拼圖檔,在拼圖檔上顯示 随機的數字
public int InitPuzzle()
{
int emptyPieceValue = 0;
int[] pieceValueArry = CreateRandNumArry();
//int[] pieceValueArry = new int[] { 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
int emptyValue = rand.Next(rowNum*columNum)+1;
double pieceWidth=puzzlePanelWidth/columNum;
double pieceHeight=puzzlePanelHeight/rowNum;
int index=0;
//重玩的時候需要先清空拼圖面闆,然後重新添加
puzzlePanel.Children.Clear();
for (int i = 0; i < rowNum; i++)
{
for (int j = 0; j < columNum; j++)
{
//判斷該位置是否需要顯示拼圖檔
if (getCorrectNum(i, j) != emptyValue)
{
Grid puzzlePiece = new Grid();
Grid container = new Grid();
container.Width = pieceWidth;
container.Height = pieceHeight;
container.Margin = new Thickness(1, 1, 1, 1);
container.Name = (pieceValueArry[index] + 1).ToString();
container.ManipulationDelta += new EventHandler<ManipulationDeltaEventArgs>(container_ManipulationDelta);
container.ManipulationCompleted += new EventHandler<ManipulationCompletedEventArgs>(container_ManipulationCompleted);
container.Background = new SolidColorBrush(piecesColor);
puzzlePanel.Children.Add(container);
TextBlock content = new TextBlock();
content.Text = (pieceValueArry[index] + 1).ToString();
content.VerticalAlignment = VerticalAlignment.Center;
content.HorizontalAlignment = HorizontalAlignment.Center;
content.FontSize = 50;
container.Children.Add(content);
Grid.SetRow(container, i);
Grid.SetColumn(container, j);
puzzlePiecesArry.Add(puzzlePiece);
}
else
{
//否則的話這個 拼圖檔肯定是空白的了,是以在數組中存儲空
//以标志此拼圖檔
puzzlePiecesArry.Add(null);
emptyPieceValue = pieceValueArry[index] + 1;
}
index++;//擷取下一個 拼圖檔上的 值
}
}
//記錄空白拼圖檔的 值是為了targetPanel中将空白拼圖檔顯示為特别的顔色
return emptyPieceValue;
}
完了之後還要繪制 提示面闆 其繪制方法和繪制拼圖面闆差不多,不過我把小片都放在Border元素中了,這裡用到了InitPuzzle傳回的空白片對應的值,建立提示面闆的CreateTargetPanel如下:
View Code
//建立 目标面闆 用來提示最終效果
public void CreateTargetPanel(int emptyPieceValue)
{
TargetPanel.Children.Clear();
TargetPanel.RowDefinitions.Clear();
TargetPanel.ColumnDefinitions.Clear();
TargetPanel.Background = new SolidColorBrush(backgroundColor);
for (int i = 0; i < rowNum; i++)
{
TargetPanel.RowDefinitions.Add(new RowDefinition());
}
for (int j = 0; j < columNum; j++)
{
TargetPanel.ColumnDefinitions.Add(new ColumnDefinition());
}
double targetPieceWidth = TargetPanel.Width / columNum;
double targetPieceHeight = TargetPanel.Height / rowNum;
int index = 0;
for (int i = 0; i < rowNum; i++)
{
for (int j = 0; j < columNum; j++)
{
Border border = new Border();
border.Width = targetPieceWidth;
border.Height = targetPieceHeight;
border.Margin = new Thickness(1, 1, 1, 1);
if (getCorrectNum(i, j) != emptyPieceValue)
{
border.Background = new SolidColorBrush(piecesColor);
}
else
{
border.Background = new SolidColorBrush(Color.FromArgb(10, 100, 100, 0));
}
TextBlock content = new TextBlock();
content.Text = (++index).ToString();
content.VerticalAlignment = VerticalAlignment.Center;
content.HorizontalAlignment = HorizontalAlignment.Center;
content.FontSize = 20;
border.Child = content;
Grid.SetRow(border, i);
Grid.SetColumn(border, j);
TargetPanel.Children.Add(border);
}
}
}
2、随機生成初始狀态
這裡我的思路是根據 拼圖檔的數目num,生成一個num大小的随機數組
我先定義一個 List<int> temp 依次存儲0-(num-1)的整數,然後每次随機從該List中選擇一個數添加到随機數組,同時将該數從List中删除,這樣就生成了一個随機數組。
将随機數組交給上面的InitPuzzle處理,InitPuzzle 依次讀取數組中的數 作為其顯示的内容。
這樣界面上顯示的數字就是随機的了,并且有一個是空白 拼圖檔。
生成随機數組的 CreateRandNumArry 方法如下:
View Code
//生成一個 随機的 拼圖檔值 數組,這些是拼圖檔上顯示的數字
//首先建立一個List 存儲num個順序的數字,
//然後從中随機取出一個數 給randArry 并将該數從List中移除
//重複此動作,直到生成所需的随機數組
public int[] CreateRandNumArry()
{
int num = columNum * rowNum;
int []randArry=new int[num];
List<int> temp = new List<int>();
for (int i = 0; i < num; i++)
{
temp.Add(i);
}
for (int j = 0; j < num; j++)
{
int randValue=temp[rand.Next(temp.Count)];
randArry[j] =randValue;
temp.Remove(randValue);
}
return randArry;
}
3、拼圖檔移動的判斷和實作
這個應該是最重要的部分了,隻有拼圖檔 相鄰位置為空白片的時候 他才可以移動,是以可以通過判斷元素的相鄰位置是否為空,該判斷可通過puzzlePiecesArry(存儲目前所有拼圖塊Grid,通過擷取Grid的Row和Column 很容易找到相鄰元素)中存儲的值來進行比較
若為空,将其與空元素交換位置,并且交換puzzlePiecesArry數組中的值,以便下一次繼續比較
該比較應該在拼圖檔的 ManipulationDelta 方法中實作 該方法和ManipulationCompleted已經在每個拼圖塊建立的時候注冊過, container_ManipulationDelta方法實作如下:
View Code
void container_ManipulationDelta(object sender, ManipulationDeltaEventArgs e)
{
Grid grid = sender as Grid;
int row=Grid.GetRow(grid);
int colum=Grid.GetColumn(grid);
CompositeTransform transform = new CompositeTransform();
grid.RenderTransform = transform;
//因為puzzlePiecesArry中儲存有各個 拼圖檔 且空拼圖檔對應的值為null
// 是以可以直接判斷要移動元素的相鄰元素是否為空即可判斷是否可以移動
//若不為空,則不執行任何操作
//若為空,将其與空元素交換位置,并且交換puzzlePiecesArry數組中的值,以便下一次繼續比較
Grid puzzlepiece=puzzlePiecesArry[getCorrectNum(row, colum) - 1];
double moveX = e.DeltaManipulation.Translation.X;
double moveY = e.DeltaManipulation.Translation.Y;
//向右
if (moveX > 0 && colum < (columNum - 1) && puzzlePiecesArry[getCorrectNum(row, colum + 1) - 1] == null)
{
Grid.SetRow(grid, row);
Grid.SetColumn(grid, colum + 1);
puzzlePiecesArry[getCorrectNum(row, colum + 1) - 1] = puzzlePiecesArry[getCorrectNum(row, colum) - 1];
puzzlePiecesArry[getCorrectNum(row, colum) - 1] = null;
moveTimes++;
}
//向左
else if (moveX < 0 && colum > 0 && puzzlePiecesArry[getCorrectNum(row, colum-1) - 1] == null)
{
Grid.SetRow(grid, row);
Grid.SetColumn(grid, colum - 1);
puzzlePiecesArry[getCorrectNum(row, colum -1) - 1] = puzzlePiecesArry[getCorrectNum(row, colum) - 1];
puzzlePiecesArry[getCorrectNum(row, colum) - 1] = null;
moveTimes++;
}
//向下
else if (moveY > 0 &&row < (rowNum - 1) && puzzlePiecesArry[getCorrectNum(row+1, colum) - 1] == null)
{
Grid.SetRow(grid, row + 1);
Grid.SetColumn(grid, colum);
puzzlePiecesArry[getCorrectNum(row+1, colum) - 1] = puzzlePiecesArry[getCorrectNum(row, colum) - 1];
puzzlePiecesArry[getCorrectNum(row, colum) - 1] = null;
moveTimes++;
}
//向上
else if (moveY < 0 && row > 0 && puzzlePiecesArry[getCorrectNum(row-1, colum ) - 1] == null)
{
Grid.SetRow(grid, row - 1);
Grid.SetColumn(grid, colum);
puzzlePiecesArry[getCorrectNum(row-1, colum) - 1] = puzzlePiecesArry[getCorrectNum(row, colum) - 1];
puzzlePiecesArry[getCorrectNum(row, colum) - 1] = null;
moveTimes++;
}
//觸發一次後立即結束,否則的話 拼圖檔會一直移動,
//故此處一定要調用Complete方法
e.Complete();
e.Handled = true;
}
這裡和上面用到的getCorrectNum方法是擷取 row行colum列 應該對應的數字,即第0行0列應該對應1,0行1列對應2 等等,getCorrectNum如下:
View Code
//擷取 row行colum列 應該對應的數字
private int getCorrectNum(int row, int colum)
{
return row * columNum + colum + 1;
}
當然在移動拼圖塊的時候還要修改 移動次數值,這個可以在 ManipulationCompleted方法中實作
container_ManipulationCompleted方法如下:
View Code
void container_ManipulationCompleted(object sender, ManipulationCompletedEventArgs e)
{
//更新移動次數
this.moveTimesTextBlock.Text = "移動次數: "+moveTimes.ToString();
if (IsSuccess())
{
MessageBoxResult result= MessageBox.Show("恭喜你,成功啦!再來一局?", "Congratulate", MessageBoxButton.OKCancel);
if (result == MessageBoxResult.OK)
{
replayButton_Click(sender, e);
}
}
}
4、成功狀态的判斷
每次操作完成後都要判斷是否成功, 這個其實很簡單了,根據拼圖塊所在的行和列可以很容易計算出該位置應該顯示的數字 (通過getCorrectNum方法),和事實上顯示的數字進行對比就可以
了,若除了空白塊外都一樣了,就成功了。判斷成功的方法如下:
View Code
//判斷是否成功
//分别判斷每個PazzlePiece的數字是否與他應該對應的數字相等
//他所對應的數字 可以通過getCorrectNum(int row, int colum) 獲得
public bool IsSuccess()
{
int row=0;
int colum=0;
foreach (Grid grid in puzzlePanel.Children)
{
if (grid != null)
{
row = Grid.GetRow(grid);
colum = Grid.GetColumn(grid);
//如果顯示文本不等于 他應該對應的數字
//且不是空白拼圖檔
if (grid.Name != getCorrectNum(row, colum).ToString())
{
return false;
}
}
}
return true;
}
然後需要在 頁面加載的時候 和 重玩按鈕點選的時候 調用這些方法就可以了。
此外還有就是當螢幕方向改變的時候,需要調整這幾部分顯示的位置,這個我放在了PhoneApplicationPage_OrientationChanged 事件中:
View Code
private void PhoneApplicationPage_OrientationChanged(object sender, OrientationChangedEventArgs e)
{
if (e.Orientation == PageOrientation.PortraitUp)
{
this.ContentPanel.Orientation = System.Windows.Controls.Orientation.Vertical;
this.bottomStackPanel.Orientation = System.Windows.Controls.Orientation.Horizontal;
}
else
{this.ContentPanel.Orientation = System.Windows.Controls.Orientation.Horizontal;
this.bottomStackPanel.Orientation = System.Windows.Controls.Orientation.Vertical;
}
}
就Ok了,當螢幕方向改變成landscape時候顯示效果如下:
成功時候顯示如下:
這個小程式我還要再進行修改 ,
就是實作圖檔拼圖 、行列的選擇、大小的選擇、計算機解決等
代碼下載下傳:NumberPuzzle.rar
轉載于:https://www.cnblogs.com/yinghuochong/archive/2011/09/10/2173438.html