WPF學習系列 游戲-選張圖片做成9宮格拼圖


今天要學習一個拼圖項目。

目標是傳入一張圖片,然后將它分成9份,去掉一份,鼠標點擊進行拼圖。

源文件結構很簡單

第一步、新建項目

這一步沒什么好說的,新建一個項目就跟源文件結構一樣了

 第二步、頁面布局(.xaml文件)

看下源文件

控件有 DockPanel Grid Button三個然后設置了Grid有三列和三行。DockPannel暫時不知道有什么用,所以我先不忙加。然后我就報錯了

原來 xaml是用的xml格式。button外面沒有雙標簽包圍,不能識別,所以報錯。所以外面再加個標簽包裹就行了,如果加DockPanel標簽就和源文件一樣了,此處為了明白DockPane有什么用,所以還是用Grid,看等會兒會不會報錯。我現在的代碼是

第二步、編寫點擊按鈕選圖片的功能

 這個帖子上周就開始寫了,但是做了一半又去研究c++了。c++研究了一段時間,忽然明白我為什么要編程了。我編程不是對計算機有興趣,不是為了0和1。我學計算機和程序只是為了做東西。所以又回過頭來繼續寫這個系列,之后的內容我不會再抓細節,有些東西,能看懂就行了。記不住也沒關系,要用的時候再查就是了。將項目做出來之后,我還要將它做成我喜歡的樣子,而不是做成跟源代碼一樣。

 點擊按鈕要做兩件事

1、彈出文件選擇對話框,選擇圖片。

2、選擇圖片后生成拼圖

下面是選擇圖片的代碼

OpenFileDialog ofd = new OpenFileDialog();                          // 需要引用Microsoft.Win32.
ofd.Filter      = "Image Files(*.BMP;*.JPG;*.GIF;*.PNG)|*.BMP;*.JPG;*.GIF;*.PNG";  //支持的圖片格式
ofd.Multiselect   = false;                                   //不允許多選
if (ofd.ShowDialog()!=true)                                   //ofd.ShowDialog()可能有三個值 true flase null
{
  return;
}
try
{

  BitmapImage image = new BitmapImage(new Uri(ofd.FileName, UriKind.RelativeOrAbsolute));
  Image img = new Image { Source = image }; 

  //這里寫創建拼圖的代碼 
}
catch { MessageBox.Show("Couldnt load the image file " + ofd.FileName); }
選擇圖片

 

 

  

生成拼圖 第一步是把圖片分成9塊,並填充相應區域的圖像

這個有點復雜,源碼用了很多方法,我習慣拆出來作為一個類單獨寫。

   /// <summary>
    /// 拼圖生成類
    /// 傳入一張圖片 生成一個9*9的圖片集合
    /// </summary>
    public class PuzzleForImage
    {
        BitmapImage     _image;                                             //原圖片
        public List<Rectangle> initialUnallocatedParts = new List<Rectangle>();//要返回的拼圖集合
        /// <summary>
        /// 新建對象時傳入原圖片
        /// </summary>
        /// <param name="image"></param>
        public PuzzleForImage(BitmapImage image)
        {
            _image = image;
       //創建拼圖 }   }

第一步:寫個子方法,根據起點和圖片寬高繪制矩形。然后調用9次,得到整個拼圖集合

第二步:將9張中的8張拼圖隨機排列,這里選前八張

第三步:再添加塊空白的拼圖

第四步:添加鼠標點擊移動事件

到這一步,源碼部分就結束了,自己添加了個判斷成功的代碼 在方塊中的點擊事件中執行。

下面是我的代碼。

/// <summary>
    /// MainWindow.xaml 的交互邏輯
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        /// <summary>
        /// 挑選圖片生成拼圖
        /// </summary>
        private void BtnPickImg_Click(object sender, RoutedEventArgs e)
        {
            OpenFileDialog ofd = new OpenFileDialog();
            ofd.Filter         = "Image Files(*.BMP;*.JPG;*.GIF;*.PNG)|*.BMP;*.JPG;*.GIF;*.PNG";//支持的圖片格式
            ofd.Multiselect    = false;//不允許多選

            if (ofd.ShowDialog() != true)
            {
                return;
            }
            try
            {
                BitmapImage    image  = new BitmapImage(new Uri(ofd.FileName, UriKind.RelativeOrAbsolute));
                //Image img = new Image { Source = image };
                PuzzleForImage puzzle = new PuzzleForImage(image);//創建拼圖
                puzzle.SetGrid(GridImg);
            }
            catch
            {
                MessageBox.Show("不支持該文件: " + ofd.FileName);
            }
        }
    }
MainWindow.xaml.cs
    /// <summary>
    /// 拼圖生成類
    /// 傳入一張圖片 生成一個9*9的圖片集合
    /// </summary>
    public class PuzzleForImage
    {
        BitmapImage            _image;                                         //原圖片
        List<Rectangle>        initialUnallocatedParts = new List<Rectangle>();//要返回拼圖集合
        public List<Rectangle> allocatedParts = new List<Rectangle>();         //被打亂后的圖片
        int[] map = new int[9];                                                //游戲地圖 判斷是否成功

        /// <summary>
        /// 新建對象時傳入原圖片
        /// </summary>
        /// <param name="image"></param>
        public PuzzleForImage(BitmapImage image)
        {
            _image = image;
            CreatePuzzleForImage();
        }

        /// <summary>
        /// 將拼圖放到UI中
        /// </summary>
        /// <param name="GridImg"></param>
        public void SetGrid(Grid GridImg)
        {
            GridImg.Children.Clear();
            GridImg.Height = _image.Height / _image.Width * GridImg.Width;
            int index = 0;
            for (int i = 0; i < 3; i++)
            {
                for (int j = 0; j < 3; j++)
                {
                    allocatedParts[index].SetValue(   Grid.RowProperty, i);
                    allocatedParts[index].SetValue(Grid.ColumnProperty, j);
                    GridImg.Children.Add(allocatedParts[index]);
                    index++;
                }
            }
        }

        /// <summary>
        /// 創建拼圖
        /// </summary>
        private void CreatePuzzleForImage()
        {
            #region 拼圖容器初始化
            initialUnallocatedParts.Clear();
            allocatedParts.Clear();
            #endregion

            #region 創建前8個拼圖
            double width  = 1.0 / 3;//每個拼圖的寬度
            double height = 1.0 / 3;//每個拼圖的高度
            //row0
            CreateImagePart(         0,          0, width, height);
            CreateImagePart(     width,          0, width, height);
            CreateImagePart( 2 * width,          0, width, height);
            //row1
            CreateImagePart(         0,     height, width, height);
            CreateImagePart(     width,     height, width, height);
            CreateImagePart( 2 * width,     height, width, height);
            //row2
            CreateImagePart(         0, 2 * height, width, height);
            CreateImagePart(     width, 2 * height, width, height);
            //CreateImagePart( 2 * width, 2 * height, width, height);
            #endregion
            
            //隨機排列
            RandomizeTiles();

            //創建白色方塊
            CreateBlankRect();
        }

        /// <summary>
        /// 繪制一個矩形
        /// 創建一個圖像畫刷使用X / Y /寬度/高度參數創建時,只顯示圖像的一部分。這是ImageBrush用來填充矩形,添加到未分配的矩形的內部表
        /// </summary>
        /// <param name="x">起點x</param>
        /// <param name="y">起點y</param>
        /// <param name="width"></param>
        /// <param name="height"></param>
        private void CreateImagePart(double x, double y, double width, double height)
        {
            #region 定義筆刷
            ImageBrush ib   = new ImageBrush();
            //ib.Stretch = Stretch.UniformToFill;                  //裁剪已適應屏幕 這個不能要,會造成圖片不拉伸對不上
            ib.ImageSource  = _image;
            ib.Viewbox      = new Rect(x, y, width, height);
            ib.ViewboxUnits = BrushMappingMode.RelativeToBoundingBox; //按百分比設置寬高
            ib.TileMode     = TileMode.None;                          //按百分比應該不會出現 image小於要切的值的情況
            #endregion

            #region 定義矩形
            Rectangle rectPart           = new Rectangle();
            rectPart.Fill                = ib;
            rectPart.Margin              = new Thickness(0);
            rectPart.HorizontalAlignment = HorizontalAlignment.Stretch;
            rectPart.VerticalAlignment   = VerticalAlignment.Stretch;
            rectPart.MouseDown          += new MouseButtonEventHandler(rectPart_MouseDown);//添加矩形點擊事件
            #endregion
            initialUnallocatedParts.Add(rectPart);                                         //將矩形添加到拼圖集合
        }

        /// <summary>
        /// 將 圖片打亂
        /// </summary>
        private void RandomizeTiles()
        {
            Random rand = new Random();
            for (int i = 0; i < 8; i++)
            {
                int index = 0;
                //if (initialUnallocatedParts.Count > 1)
                //{
                //    index = (int)(rand.NextDouble() * initialUnallocatedParts.Count);
                //}
                index = (int)(rand.NextDouble() * initialUnallocatedParts.Count);
                while(initialUnallocatedParts[index] == null)
                {
                    index = (int)(rand.NextDouble() * initialUnallocatedParts.Count);
                }
                allocatedParts.Add(initialUnallocatedParts[index]);
                //initialUnallocatedParts.RemoveAt(index); // 移除圖片
                initialUnallocatedParts[index] = null;
                map[i] = index;                          // 添加地圖
            }
        }

        /// <summary>
        /// 再創建一個空白矩形
        /// </summary>
        private void CreateBlankRect()
        {
            Rectangle rectPart           = new Rectangle();
            rectPart.Fill                = new SolidColorBrush(Colors.White);
            rectPart.Margin              = new Thickness(0);
            rectPart.HorizontalAlignment = HorizontalAlignment.Stretch;
            rectPart.VerticalAlignment   = VerticalAlignment.Stretch;
            allocatedParts.Add(rectPart);
            map[8] = 8;
        }

        /// <summary>
        /// 方塊點擊事件
        /// </summary>
        private void rectPart_MouseDown(object sender, MouseButtonEventArgs e)
        {
            //get the source Rectangle, and the blank Rectangle
            //NOTE : Blank Rectangle never moves, its always the last Rectangle
            //in the allocatedParts List, but it gets re-allocated to 
            //different Gri Row/Column
            Rectangle rectCurrent = sender as Rectangle;                        //當前方塊
            Rectangle rectBlank   = allocatedParts[allocatedParts.Count - 1];   //缺省方塊

            //得到白塊和缺省方塊的位置
            int currentTileRow  = (int)rectCurrent.GetValue(Grid.RowProperty);
            int currentTileCol  = (int)rectCurrent.GetValue(Grid.ColumnProperty);
            int currentBlankRow = (int)rectBlank.GetValue(Grid.RowProperty);
            int currentBlankCol = (int)rectBlank.GetValue(Grid.ColumnProperty);

            //白塊能移動的四個位置
            List<PossiblePositions> posibilities = new List<PossiblePositions>();
            posibilities.Add(new PossiblePositions { Row = currentBlankRow - 1, Col = currentBlankCol });
            posibilities.Add(new PossiblePositions { Row = currentBlankRow + 1, Col = currentBlankCol });
            posibilities.Add(new PossiblePositions { Row = currentBlankRow, Col = currentBlankCol - 1 });
            posibilities.Add(new PossiblePositions { Row = currentBlankRow, Col = currentBlankCol + 1 });

            //檢查該方塊是否能點擊(白塊能移動到當前方塊的位置)
            bool validMove = false;
            foreach (PossiblePositions position in posibilities)
                if (currentTileRow == position.Row && currentTileCol == position.Col)
                    validMove = true;

            //only allow valid move
            if (validMove)
            {
                //交換位置
                rectCurrent.SetValue(   Grid.RowProperty, currentBlankRow);
                rectCurrent.SetValue(Grid.ColumnProperty, currentBlankCol);

                rectBlank  .SetValue(   Grid.RowProperty, currentTileRow);
                rectBlank  .SetValue(Grid.ColumnProperty, currentTileCol);

                //更新地圖
                int indexCur   = currentTileRow * 3 + currentTileCol;
                int indexBlank = currentBlankRow * 3 + currentBlankCol;
                int temp = map[indexCur];
                map[indexCur] = map[indexBlank];
                map[indexBlank] = temp;

                //判斷是否成功
                if (isSuccess())
                {
                    MessageBox.Show("成功了,好棒啊!");
                }
            }
        }

        /// <summary>
        /// 驗證是否游戲成功
        /// </summary>
        /// <returns></returns>
        private bool isSuccess()
        {
            for (int i = 0; i < 9; i++)
            {
                if(map[i]!=i)
                {
                    return false;
                }
            }
            return true;
        }
    }

    /// <summary>
    /// Simply struct to store Row/Column data
    /// </summary>
    struct PossiblePositions
    {
        public int Row { get; set; }
        public int Col { get; set; }
    }
PuzzleForImage.cs

運行效果:

還存在的問題:

1、現在圖片會被拉伸,暫時沒想到好的辦法。

2、會隨機一些拼不出來的拼圖

 

------------------------------分割線2016-8-3 16:47---------------------------------

圖片拉伸問題已經解決

@曙光閃現
[quote]圖片拉伸應該只能通過限制窗體的縮放按圖片的比例縮放了。之前搞過9path原理就是類似這個九宮格[/quote]

下面是代碼

      /*------------------------------------------------------------------
                 *如果選擇的圖片寬高比例比屏幕的寬高比例大,則窗體和圖片區域寬度采用600,高度等比縮放
                 * 反之,圖片區域高度采用600,寬度等比算出,但是窗體寬度就不等比縮放了(照顧button)
                 ------------------------------------------------------------------*/
                double imgWidthHeightRatio    = image.Width / image.Height;//圖片 寬高比
                double windowWidthHeightRatio =  600/ 600;//默認屏幕寬高比
                this.Width                    = 600;
                if (imgWidthHeightRatio > windowWidthHeightRatio)
                {
                    GridImg.Width = 600;
                    GridImg.Height = GridImg.Width / imgWidthHeightRatio;
                    this.Height = GridImg.Height + 50;
                }
                else
                {
                    this.Height = 650;//button占了50
                    GridImg.Height = 600;
                    GridImg.Width = GridImg.Width * imgWidthHeightRatio;
                }
設置寬高

更新了MainWindow.xaml.cs文件 和 MainWindow.xaml文件

下面是最新的代碼

using Microsoft.Win32;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace Puzzle
{
    /// <summary>
    /// MainWindow.xaml 的交互邏輯
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        /// <summary>
        /// 挑選圖片生成拼圖
        /// </summary>
        private void BtnPickImg_Click(object sender, RoutedEventArgs e)
        {
            OpenFileDialog ofd = new OpenFileDialog();
            ofd.Filter         = "Image Files(*.BMP;*.JPG;*.GIF;*.PNG)|*.BMP;*.JPG;*.GIF;*.PNG";//支持的圖片格式
            ofd.Multiselect    = false;//不允許多選

            if (ofd.ShowDialog() != true)
            {
                return;
            }
            try
            {
                BitmapImage    image  = new BitmapImage(new Uri(ofd.FileName, UriKind.RelativeOrAbsolute));
                #region 設置寬高
                /*------------------------------------------------------------------
                 *如果選擇的圖片寬高比例比屏幕的寬高比例大,則窗體和圖片區域寬度采用600,高度等比縮放
                 * 反之,圖片區域高度采用600,寬度等比算出,但是窗體寬度就不等比縮放了(照顧button)
                 ------------------------------------------------------------------*/
                double imgWidthHeightRatio    = image.Width / image.Height;//圖片 寬高比
                double windowWidthHeightRatio =  600/ 600;//默認屏幕寬高比
                this.Width                    = 600;
                if (imgWidthHeightRatio > windowWidthHeightRatio)
                {
                    GridImg.Width = 600;
                    GridImg.Height = GridImg.Width / imgWidthHeightRatio;
                    this.Height = GridImg.Height + 50;
                }
                else
                {
                    this.Height = 650;//button占了50
                    GridImg.Height = 600;
                    GridImg.Width = GridImg.Width * imgWidthHeightRatio;
                }
                #endregion
                //Image img = new Image { Source = image };
                PuzzleForImage puzzle = new PuzzleForImage(image);//創建拼圖
                puzzle.SetGrid(GridImg);
            }
            catch
            {
                MessageBox.Show("不支持該文件: " + ofd.FileName);
            }
        }
    }
}
MainWindow.xaml.cs
<Window x:Class="Puzzle.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="650" Width="600">
    <Grid>
        <Button x:Name="BtnPickImg" Content="Button" HorizontalAlignment="Left" Margin="10,10,0,0" VerticalAlignment="Top" Width="560" RenderTransformOrigin="-0.459,-0.974" Click="BtnPickImg_Click"/>
        <Grid x:Name="GridImg" Margin="0,50,0,0" Background="#eee">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="*"/>
                <ColumnDefinition Width="*"/>
                <ColumnDefinition Width="*"/>
            </Grid.ColumnDefinitions>
            <Grid.RowDefinitions>
                <RowDefinition Height="*"/>
                <RowDefinition Height="*"/>
                <RowDefinition Height="*"/>
            </Grid.RowDefinitions>
        </Grid>
    </Grid>
</Window>
MainWindow.xaml

PuzzleForImage.cs沒變還是上面那個

 

--20180419 發現一個功能多的:http://download.microsoft.com/download/B/2/5/B25C4C6A-97FE-4014-9D4B-B39607BA9A12/wpf_samples/15Puzzle.exe


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM