zoukankan      html  css  js  c++  java
  • 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

  • 相关阅读:
    URAL——DFS找规律——Nudnik Photographer
    URAL1353——DP——Milliard Vasya's Function
    URAL1203——DPor贪心——Scientific Conference
    递推DP HDOJ 5389 Zero Escape
    区间DP UVA 1351 String Compression
    树形DP UVA 1292 Strategic game
    Manacher HDOJ 5371 Hotaru's problem
    同余模定理 HDOJ 5373 The shortest problem
    递推DP HDOJ 5375 Gray code
    最大子序列和 HDOJ 1003 Max Sum
  • 原文地址:https://www.cnblogs.com/tanl/p/5710842.html
Copyright © 2011-2022 走看看