搜索
首页后端开发C#.Net教程C#编写拼图游戏的图文代码分享(下)
C#编写拼图游戏的图文代码分享(下)Apr 18, 2017 am 09:11 AM
c#拼图游戏

这篇文章主要为大家详细介绍了C#拼图游戏的编写代码下篇,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

前言:在C#拼图游戏编写代码程序设计 之 C#实现《拼图游戏》(上),上传了各模块代码,而在本文中将详细剖析原理,使读者更容易理解并学习,程序有诸多问题,欢迎指出,共同学习成长!

正文:

拼图是一个非常经典的游戏,基本每个人都知道他的玩法,他的开始,运行,结束。那么,当我们想要做拼图的时候如何入手呢?答案是:从现实出发,去描述需求(尽量描述为文档),当我们拥有了全面的需求,就能够提供可靠的策略,从而在代码中实现,最终成为作品!

(一)需求: (这个需求书写较为潦草,为广大小白定制,按照最最最普通人的思维来,按照参与游戏的流程来)

   1.图片:我们玩拼图 最起码有个图

   2.切割:拼图不是一个图,我们需要把一个整图它切割成N*N的小图

   3.打乱:把这N*N的小图打乱顺序,但是要保证通过游戏规则行走能还原回来

   4.判断:判拼图成功

   5.交互:我们使用哪一种交互方式,这里我选择鼠标点击

      6.展示原图片完整的缩略图

以上为基本功能,以下为扩展功能

     7.记录步数:记录完成需要多少步

     8.更换图片:一个图片玩久了我们是不是可以换一换啊 哈哈

     9.选择难度:太简单?不要!3*3搞定了有5*5,5*5搞定了有9*9,舍友挑战最高难度 3000多步,心疼我的鼠标TAT

(二)分析:

有了需求,我们就可以分析如何去实现它(把现实需求映射在计算机中),其中包括:

1.开发平台:这里选择C#语言

 1).存储:其中包括我们要存什么?我们用什么结构存?我们反观需求,会发现,有一些需要存储的资源

      图片:使用 Image 对象存储

      单元(原图片切割后的子图像集合):自定义结构体 struct Node ,其中包括Image对象用来存储单元小图片,和用整形存储的编号(切割以后,每个小单元都弄个编号,利于检验游戏是否完成)。

      各单元(原图片切割后的子图像集合):使用二维数组(像拼图,五子棋,消消乐,连连看,俄罗斯方块等平面点阵游戏都可以用他来存储,为什么?因为长得像嘛!)来存储

      难度:使用自定义的枚举类型(简单and普通and困难)存储

      步数:整形变量 int Num存储 

  有了存储,我们就可以去思考模块的划分(正确的逻辑划分已于扩展,也可以使通信变得更加清晰)并搭建,并实现各模块涉及到的具体算法

      首先程序的模块分为四个:

  逻辑型:

    1.拼图类:用于描述拼图

    2.配置类:存储配置变量

  交互型:

    3.游戏菜单窗口:进行菜单选项

    4.游戏运行窗口:游戏的主要界面

  1.通过游戏菜单可以操纵配置,如难度或图片。

  2.运行窗口可以访问并获得游戏配置,并利用其对应构造拼图对象。

  3.用户通过运行窗口进行交互,间接使拼图对象调用移动方法,获得图案方法

  看代码的同学,我觉得最有问题的地方,不合理的地方就是把 难度的枚举类型写在了拼图类中,应该写在配置类中,或单独成类,读者们自行更改


 public enum Diff //游戏难度
 {
  simple,//简单
  ordinary,//普通
  difficulty//困难
 }

我们可以认为,配置类就像数据存储,而拼图类呢作为逻辑处理,菜单和运行窗口作为表现用于交互,我承认这种设计不是很合理,但是在问题规模不够大的时候,过分的考虑设计,会不会使程序变得臃肿?我想一定是有一个度,具体是多少,我不得而知,但我感觉,针对这个程序,实现就好,沉迷设计(套路型),有时得不偿失。(个人不成熟的小观点)

(三)代码实现:

说明:本块重点描述 Puzzle(拼图)类与游戏运行类的具体实现及实体通讯:

拼图的构造方法

1.赋值 :

public Puzzle(Image Img,int Width, Diff GameDif)

// 拼图的图片,宽度(解释:正方形的边长,单位是像素,名字有歧义,抱歉),游戏的难度 

 游戏的难度决定你分割的程度,分割的程度,决定你存储的数组的大小,如简单对应3行3列,普通对应5行5列,困难对应9行9列


 switch(this._gameDif)
  {
  case Diff.simple:    //简单则单元格数组保存为3*3的二维数组
   this.N = 3;
   node=new Node[3,3];
   break;
  case Diff.ordinary:   //一般则为5*5
   this.N = 5;
   node = new Node[5, 5];
   break;
  case Diff.difficulty:  //困难则为9*9
   this.N = 9;
   node = new Node[9, 9];
   break;
  }

2.分割图片


 //分割图片形成各单元保存在数组中
  int Count = 0;
  for (int x = 0; x < this.N; x++)
  {
  for (int y = 0; y < this.N; y++)
  {

   node[x, y].Img = CaptureImage(this._img, this.Width / this.N, this.Width / this.N, x * (this.Width / this.N), y * (this.Width / this.N));
   node[x, y].Num = Count;
   Count++;
  }
  }

其实对单元数组进行赋值的过程,使用双层for循环对二维数组进行遍历操作,然后按序赋值编号node[x,y].Num; 

然后对node[x,y].Img,也就是单元的小图片赋值,赋值的方法是,C#的图像的类库,写一个截图方法,使用这个方法,将大图中对应对位置的对应大小的小图截取下来,并保存在node[x,y].Img中;

width/N是什么?是边长除以行数,也就是间隔嘛,间隔也就是每个单元的边长嘛!然后起始坐标(X,Y)起始就是在说,隔了几个单元后,我的位置,

即 :(x,y)=(单元边长*距离起始X轴相距单元数,单元边长*距离起始点Y轴相距单元数);

关于此类问题,希望读者能够多画画图,然后自然就明白了;

public  Image CaptureImage(Image fromImage, int width, int height, int spaceX, int spaceY)

主要逻辑:利用DrawImage方法:


//创建新图位图 
 Bitmap bitmap = new Bitmap(width, height);
//创建作图区域 
Graphics graphic = Graphics.FromImage(bitmap);
//截取原图相应区域写入作图区 
 graphic.DrawImage(fromImage, 0, 0, new Rectangle(x, y, width, height), GraphicsUnit.Pixel);
//从作图区生成新图 
 Image saveImage = Image.FromHbitmap(bitmap.GetHbitmap());

分割了以后,我们要做一个特殊处理,因为我们知道,总有那么一个位置是白的吧?我们默认为最后一个位置,即node[N-1,N-1];

就是写改成了个白色的图片,然后四周的边线都给画成红色,已于被人发现,显著一些,之前的其他单元我也画了边线,但是是白色,也是为了在拼图的观赏性上得到区分。该代码不做介绍。

3.打乱图片:

其实就是将二维数组打乱,我们可以采取一些排序打乱方法,但是请注意!不是每一种打乱都能够复原的!

那么如何做到可行呢?方法理解起来很简单,就是让我们的电脑在开局之前,将完整的有序的单元按照规则中提供的行走方式进行无规则,大次数的行走!也就是说这种方法一定能走回去!

先理解,具体打乱方法,在后面讲解。 

移动方法(Move):

拼图游戏中方格的移动,其实就是两个相邻单元的交换,而这两个单元中,必定存在一个白色单元(即上面提到的node[N-1,N-1]单元,他的编号为N*N-1,建议自己动笔算一算)

所以我们的判断条件是,如果移动一个方块,他的上下左右四个方向中,一旦有一个相邻的是白色单元,即N*N-1号单元,则与其交换。这是基本逻辑,但不包括约束条件,当我们的数组达到边界的时候,我们就不能对越界数据进行访问,如当单元为node[0,0]时,你就不能对他上面和右面的数据进行访问,因为Node[-1,0] Node[0,-1]都会越界,发生异常

移动成功,返回TRUE

移动失败,返回FALSE


/// <summary>
 /// 移动坐标(x,y)拼图单元
 /// </summary>
 /// <param name="x">拼图单元x坐标</param>
 /// <param name="y">拼图单元y坐标</param>
 public bool Move(int x,int y)
 {
  //MessageBox.Show(" " + node[2, 2].Num);
  if (x + 1 != N && node[x + 1, y].Num == N * N - 1)
  {
  Swap(new Point(x + 1, y), new Point(x, y));
  return true;
  }
  if (y + 1 != N && node[x, y + 1].Num == N * N - 1)
  {
  Swap(new Point(x, y + 1), new Point(x, y));
  return true;
  }  
  if (x - 1 != -1 && node[x - 1, y].Num == N * N - 1)
  {
  Swap(new Point(x - 1, y), new Point(x, y));
  return true;
  } 
  if (y - 1 != -1 && node[x, y - 1].Num == N * N - 1)
  {
  Swap(new Point(x, y - 1), new Point(x, y));
  return true;
  }
  return false;
  
 }

交换方法(Swap):

交换数组中两个元素的位置,该方法不应该被类外访问,顾设置为private私有权限


 //交换两个单元格
 private void Swap(Point a, Point b)
 {
  Node temp = new Node();
  temp = this.node[a.X, a.Y];
  this.node[a.X, a.Y] = this.node[b.X, b.Y];
  this.node[b.X, b.Y] = temp;
 }

打乱方法:

前面提到,其实就是让电脑帮着乱走一通,说白了就是大量的调用Move(int X,int y)方法,也就是对空白位置的上下左右四个相邻的方块中随机抽取一个,并把它的坐标传递给Move使其进行移动,同样要进行越界考虑,这样的操作大量重复!代码自己看吧 ,利用随机数。


/// <summary>
 /// 打乱拼图
 /// </summary>
 public void Upset()
 {
  int sum = 100000;
  if (this._gameDif == Diff.simple) sum = 10000;
  //if (this._gameDif == Diff.ordinary) sum = 100000;
  Random ran = new Random();
  for (int i = 0, x = N - 1, y = N - 1; i < sum; i++)
  {
  long tick = DateTime.Now.Ticks;
  ran = new Random((int)(tick & 0xffffffffL) | (int)(tick >> 32)|ran.Next());
  switch (ran.Next(0, 4))
  {
   case 0:
   if (x + 1 != N)
   {
    Move(x + 1, y);
    x = x + 1;
   }
    
   break;
   case 1:
   if (y + 1 != N)
   {
    Move(x, y + 1);
    y = y + 1;
   } 
   break;
   case 2:
   if (x - 1 != -1)
   {
    Move(x - 1, y);
    x = x - 1;
   } 
   break;
   case 3:
   if (y - 1 != -1)
   {
    Move(x, y - 1);
    y = y - 1;
   }
   break;
  }

  }
 }

返回图片的方法:

当时怎么起了个这样的鬼名字。。。DisPlay。。。

这个方法与分割方法刚好相背,这个方法其实就是遍历数组,并将其进行组合,组合的方法很简单,就是将他们一个一个的按位置画在一张与原图相等大小的空白图纸上!最后提交图纸,也就是return一个Image;


 public Image Display()
 {
  Bitmap bitmap = new Bitmap(this.Width, this.Width);
  //创建作图区域 
  Graphics newGra = Graphics.FromImage(bitmap);
  for (int x = 0; x < this.N; x++)
  for (int y = 0; y < this.N; y++)
   newGra.DrawImage(node[x, y].Img, new Point(x * this.Width / this.N, y * this.Width / this.N));
  return bitmap;
 }

同样利用的是DrawImage方法,知道如何分割,这个应该很容易理解,自己算一算,在纸上比划比划就明白了;

判断方法:

该方法很容易理解,就是按序按序!遍历所有单元,如果他们的结果中有一个单元的编号

node[x, y].Num 不等于遍历的序号,那么说明,该单元不在原有位置上,即整个图片还没有完成,我们就可以直接返回假值false
如果所有遍历结果都正确,我们可认为,图片已复原,此时返回真值true


 public bool Judge()
 {
  int count=0;
  for (int x = 0; x < this.N; x++)
  {
  for (int y = 0; y < this.N; y++)
  {
   if (this.node[x, y].Num != count)
   return false;
   count++;
  }
  }
  return true;
 }

游戏运行窗口:即游戏玩耍时用于交互的窗口

这里只讲一个方法:即当接受用户鼠标点击事件时我们应该怎么处理并作出什么样反应

其实说白了就这句难懂:

puzzle.Move(e.X / (puzzle.Width / puzzle.N), e.Y / (puzzle.Width / puzzle.N))

调用了移动方法,移动方块

横坐标为:e.X / (puzzle.Width / puzzle.N)
纵坐标为:e.Y / (puzzle.Width / puzzle.N)

我们编程中的整数除法和数学里的除法是不一样的!比如10/4数学上等于2余2或者2.5,计算机里直接就是等于2了,只取整数部分

行数=行坐标 / 方块边长

列数=列坐标 / 方块边长

我们看P1,P2这两点

P1:40/30*30=1
P2:50/30*30=1

我们会发现同在一个单元格中,无论点击哪个位置,通过这个算法都能转化为
同一个坐标。

(e.x,e.y)为鼠标点击事件点击坐标


 private void pictureBox1_MouseClick(object sender, MouseEventArgs e)
 {
  if (puzzle.Move(e.X / (puzzle.Width / puzzle.N), e.Y / (puzzle.Width / puzzle.N)))
  {
  Num++;
  pictureBox1.Image = puzzle.Display();
  if (puzzle.Judge())
  { 
   if (MessageBox.Show("恭喜过关", "是否重新玩一把", MessageBoxButtons.OKCancel) == DialogResult.OK)
   {
   Num = 0;
   puzzle.Upset();
   pictureBox1.Image = puzzle.Display();
   
   }
   else
   {
   Num = 0;
   closefather();
   this.Close();
   }

  }

  }
  NumLabel.Text = Num.ToString();
 }

好,那么大体的逻辑,程序中最需要思考的算法已经讲完了,还有不太懂的地方,欢迎交流~么么哒~

加了点小功能 音乐历史成绩

以上是C#编写拼图游戏的图文代码分享(下)的详细内容。更多信息请关注PHP中文网其他相关文章!

声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
如何使用C#编写时间序列预测算法如何使用C#编写时间序列预测算法Sep 19, 2023 pm 02:33 PM

如何使用C#编写时间序列预测算法时间序列预测是一种通过分析过去的数据来预测未来数据趋势的方法。它在很多领域,如金融、销售和天气预报中有广泛的应用。在本文中,我们将介绍如何使用C#编写时间序列预测算法,并附上具体的代码示例。数据准备在进行时间序列预测之前,首先需要准备好数据。一般来说,时间序列数据应该具有足够的长度,并且是按照时间顺序排列的。你可以从数据库或者

如何使用Redis和C#开发分布式事务功能如何使用Redis和C#开发分布式事务功能Sep 21, 2023 pm 02:55 PM

如何使用Redis和C#开发分布式事务功能引言分布式系统的开发中,事务处理是一项非常重要的功能。事务处理能够保证在分布式系统中的一系列操作要么全部成功,要么全部回滚。Redis是一种高性能的键值存储数据库,而C#是一种广泛应用于开发分布式系统的编程语言。本文将介绍如何使用Redis和C#来实现分布式事务功能,并提供具体代码示例。I.Redis事务Redis

如何实现C#中的人脸识别算法如何实现C#中的人脸识别算法Sep 19, 2023 am 08:57 AM

如何实现C#中的人脸识别算法人脸识别算法是计算机视觉领域中的一个重要研究方向,它可以用于识别和验证人脸,广泛应用于安全监控、人脸支付、人脸解锁等领域。在本文中,我们将介绍如何使用C#来实现人脸识别算法,并提供具体的代码示例。实现人脸识别算法的第一步是获取图像数据。在C#中,我们可以使用EmguCV库(OpenCV的C#封装)来处理图像。首先,我们需要在项目

Redis在C#开发中的应用:如何实现高效的缓存更新Redis在C#开发中的应用:如何实现高效的缓存更新Jul 30, 2023 am 09:46 AM

Redis在C#开发中的应用:如何实现高效的缓存更新引言:在Web开发中,缓存是提高系统性能的常用手段之一。而Redis作为一款高性能的Key-Value存储系统,能够提供快速的缓存操作,为我们的应用带来了不少便利。本文将介绍如何在C#开发中使用Redis,实现高效的缓存更新。Redis的安装与配置在开始之前,我们需要先安装Redis并进行相应的配置。你可以

如何使用C#编写动态规划算法如何使用C#编写动态规划算法Sep 20, 2023 pm 04:03 PM

如何使用C#编写动态规划算法摘要:动态规划是求解最优化问题的一种常用算法,适用于多种场景。本文将介绍如何使用C#编写动态规划算法,并提供具体的代码示例。一、什么是动态规划算法动态规划(DynamicProgramming,简称DP)是一种用来求解具有重叠子问题和最优子结构性质的问题的算法思想。动态规划将问题分解成若干个子问题来求解,通过记录每个子问题的解,

如何实现C#中的图像压缩算法如何实现C#中的图像压缩算法Sep 19, 2023 pm 02:12 PM

如何实现C#中的图像压缩算法摘要:图像压缩是图像处理领域中的一个重要研究方向,本文将介绍在C#中实现图像压缩的算法,并给出相应的代码示例。引言:随着数字图像的广泛应用,图像压缩成为了图像处理中的重要环节。压缩能够减小存储空间和传输带宽,并能提高图像处理的效率。在C#语言中,我们可以通过使用各种图像压缩算法来实现对图像的压缩。本文将介绍两种常见的图像压缩算法:

C#开发中如何处理跨域请求和安全性问题C#开发中如何处理跨域请求和安全性问题Oct 08, 2023 pm 09:21 PM

C#开发中如何处理跨域请求和安全性问题在现代的网络应用开发中,跨域请求和安全性问题是开发人员经常面临的挑战。为了提供更好的用户体验和功能,应用程序经常需要与其他域或服务器进行交互。然而,浏览器的同源策略导致了这些跨域请求被阻止,因此需要采取一些措施来处理跨域请求。同时,为了保证数据的安全性,开发人员还需要考虑一些安全性问题。本文将探讨C#开发中如何处理跨域请

如何实现C#中的遗传算法如何实现C#中的遗传算法Sep 19, 2023 pm 01:07 PM

如何在C#中实现遗传算法引言:遗传算法是一种模拟自然选择和基因遗传机制的优化算法,其主要思想是通过模拟生物进化的过程来搜索最优解。在计算机科学领域,遗传算法被广泛应用于优化问题的解决,例如机器学习、参数优化、组合优化等。本文将介绍如何在C#中实现遗传算法,并提供具体的代码示例。一、遗传算法的基本原理遗传算法通过使用编码表示解空间中的候选解,并利用选择、交叉和

See all articles

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

AI Hentai Generator

AI Hentai Generator

免费生成ai无尽的。

热门文章

R.E.P.O.能量晶体解释及其做什么(黄色晶体)
2 周前By尊渡假赌尊渡假赌尊渡假赌
仓库:如何复兴队友
1 个月前By尊渡假赌尊渡假赌尊渡假赌
Hello Kitty Island冒险:如何获得巨型种子
4 周前By尊渡假赌尊渡假赌尊渡假赌

热工具

螳螂BT

螳螂BT

Mantis是一个易于部署的基于Web的缺陷跟踪工具,用于帮助产品缺陷跟踪。它需要PHP、MySQL和一个Web服务器。请查看我们的演示和托管服务。

VSCode Windows 64位 下载

VSCode Windows 64位 下载

微软推出的免费、功能强大的一款IDE编辑器

Dreamweaver Mac版

Dreamweaver Mac版

视觉化网页开发工具

SublimeText3 英文版

SublimeText3 英文版

推荐:为Win版本,支持代码提示!

记事本++7.3.1

记事本++7.3.1

好用且免费的代码编辑器