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、会随机一些拼不出来的拼图

时间: 2024-10-25 20:52:01

WPF学习系列 游戏-选张图片做成9宫格拼图的相关文章

WPF学习系列之五(WPF控件)

控件:    1.内容控件------这些控件能够包含嵌套的元素,为它们提供几乎无限的显示能力.内容控件包括Lable,Button 以及ToolTip类. 内容控件是更特殊的控件类型,它们可以包含(并显示)一块内容.从技术角度来讲,内容控件是可以包含单个嵌套元素的控件.与布局容器不同的是内容控件只能包含一个子元素,而布局控件只要愿意可以包含任意多个嵌套元素.              提示:当然,仍然可以在单个内容控件中放置大量内容-----诀窍是使用单个容器,比如,使用StackPanel面

WPF学习系列之二 (依赖项属性)

依赖属性;(dependency property)  它是专门针对WPF创建的,但是WPF库中的依赖项属性都使用普通的.NET属性过程进行了包装.从而可能通过常规的方式使用它们,即使使用他们的代码不理解WPF依赖项属性系统也是如此,使用旧技术包装新技术看起来有些奇怪,但这正是WPF能够改变基础组成部分,而不会扰乱.NET领域中其他部分的原因.三步:一:定义依赖项属性.public static readonly DependencyProperty MarginProperty;二:.在静态构

WPF学习系列之八(形状,画刷和变换)

形状,画刷和变换   概述: 在许多用户界面技术中,普通控件和自定义绘图之间具有清晰的区别.通常来说,绘图特性只用于特定的应用程序--如游戏,数据可视化和物理仿真等.而WPF具有一个非常不同的原则.它以相同的方式处理控件和绘制的图形. 一.理解形状.       在WPF用户界面中,绘制2D图形内容的最简单方法是使用形状(shape) :专门用于表示简单的直线,椭圆,矩形以及多边形的类.从技术角度讲,形状就是所谓的绘图图元.可以组合这些基本元素来创建更复杂的图形.形状最重要的细节是,它们都继承自

WPF学习系列之六 (元素绑定)

元素绑定 简单地说,数据绑定是一种关系,该关系告诉WPF从一个源对象提取一些信息,并使用这些信息设置目标对象的属性.目标属性总是依赖属性,并且通常位于WPF元素中. 一.将元素绑定到一起 <Window x:Class="StudyWPF.元素绑定"        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"        xmlns:x="http://schem

wpf datagrid根据多选选中的获取单元格内容,进行操作数据

private void Button_Click(object sender, RoutedEventArgs e)        {            var vLst = this.dgList.SelectedItems;            for (int i = 0; i < vLst.Count; i++)            { 1.//获取选中的数据                string str = (dgList.Columns[1].GetCellConte

WPF学习笔记系列之一 (布局详情)

布局:StackPanel  栈布局:控件不会拐弯且多出的不再显示.DockPanel   停靠布局 吸在上边下边或左右.WrapPanel    环绕布局   一行控件会拐弯Canvas  进行基于坐标的布局 Grid中若不指定Grid.Row属性及Grid.Column则默认为:0行,0列.RowDefinitions ColumnDefinitions ShowGridLines=true <ColumnDefinition Width="100"></Colu

MvvmLight学习篇—— Mvvm Light Toolkit for wpf/silverlight系列(导航)

一.Mvvm Light Toolkit for wpf/silverlight系列之准备工作 二.Mvvm Light Toolkit for wpf/silverlight系列之搭建mvvmlight开发框架 三.Mvvm Light Toolkit for wpf/silverlight系列之数据绑定 四.Mvvm Light Toolkit for wpf/silverlight系列之Command和Events 五.Mvvm Light Toolkit for wpf/silverli

quick-cocos2d-x 学习系列之十四 测试用例

quick-cocos2d-x 学习系列之十四 测试用例 定义变量,创建13个场景名字 local items = { "framework.helper", "framework.native", "framework.display", "framework.crypto", "framework.network", "framework.luabinding", "fra

[转]Tesseract-OCR学习系列

转载地址:http://www.jianshu.com/p/a53c732d8da3 Tesseract-OCR学习系列(三)简例 Tesseract API Basic Example using CMake Configuration 参考文档:https://github.com/tesseract-ocr/tesseract/wiki/APIExample Tesseract提供的API可以在baseapi.h文件中找到.然而,如果没有个示例带我们飞一会儿,也是颇难搞懂到底该怎么调用te