搜尋
首頁後端開發C#.Net教程C#編寫拼圖遊戲的圖文程式碼分享(下)

這篇文章主要為大家詳細介紹了C#拼圖遊戲的編寫程式碼下篇,具有一定的參考價值,有興趣的小夥伴們可以參考一下

前言:在C#拼圖遊戲編寫程式碼程式設計之C#實現《拼圖遊戲》(上),上傳了各模組程式碼,而在本文中將詳細剖析原理,使讀者更容易理解並學習,程式有諸多問題,歡迎指出,共同學習成長!

正文:

拼圖是一個非常經典的遊戲,基本上每個人都知道他的玩法,他的開始,運行,結束。那麼,當我們想要做拼圖的時候該如何入手呢?答案是:從現實出發,去描述需求(盡量描述為文件),當我們擁有了全面的需求,就能夠提供可靠的策略,從而在程式碼中實現,最終成為作品!

(一)需求: (這個需求書寫較為潦草,為廣大小白定制,按照最最普通人的思維來,按照參與遊戲的流程來)

   1.圖片:我們玩拼圖最起碼有個圖

   2.切割:拼圖不是一個圖,我們需要把一個整圖它切割成N*N的小圖

   3.打亂:把這N*N的小圖打亂順序,但是要保證透過遊戲規則行走能還原回來

   4.判斷:判拼圖成功

        6.展示原始圖片完整的

縮圖

#以上為基本功能,以下為擴充功能

     7.記錄步數:記錄完成需要多少步驟

     8.更換圖片:一個圖片玩久了我們是不是可以換一換啊哈哈

     9.選擇難度:太簡單?不要! 3*3搞定了有5*5,5*5搞定了有9*9,舍友挑戰最高難度

300

0多步,心疼我的滑鼠TAT

(二)分析:

有了需求,我們就可以分析如何去實現它(把現實需求映射在電腦中),其中包括:

1.開發平台:這裡選擇C#語言

 1).儲存:其中包括我們要存什麼?我們用什麼結構存?我們反觀需求,會發現,有一些需要儲存的資源

      圖片:使用Image

物件

#      子單元(原始圖片切割後的圖片集合定義

結構體

struct Node ,其中包括Image物件用來儲存單元小圖片,和用整形儲存的編號(切割以後,每個小單元都弄個編號,利於檢驗遊戲是否完成)。       各單元(原圖片切割後的子圖像集合):使用

二維數組

(像拼圖,五子棋,消消樂,連連看,俄羅斯方塊等平面點陣遊戲都可以用他來存儲,為什麼? 變數 int Num存儲

  有了存儲,我們就可以去思考模組的劃分(

正確的邏輯劃分已於擴展,也可以使通信變得更加清晰

)並搭建,並實現各模組所涉及的特定演算法##      首先程式的模組分為四個:

 

邏輯型:

    1.拼圖類別:用於描述拼圖

    2.配置類別:儲存配置變數# 

互動型:

    3.遊戲選單視窗:進行選單選項

    4.遊戲運作視窗:遊戲的主要介面

  1.通過遊戲的主要介面

  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#封装)来处理图像。首先,我们需要在项目

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

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

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

如何使用C#编写背包问题算法如何使用C#编写背包问题算法Sep 19, 2023 am 09:21 AM

如何使用C#编写背包问题算法背包问题(KnapsackProblem)是一个经典的组合优化问题,它描述了一个给定容量的背包以及一系列物品,每个物品都有自己的价值和重量。目标是找到一种最佳策略,使得在不超过背包容量的情况下,装入背包的物品总价值最大。在C#中,可以通过动态规划方法来解决背包问题。具体实现如下:usingSystem;namespace

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.能量晶體解釋及其做什麼(黃色晶體)
3 週前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳圖形設置
3 週前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.如果您聽不到任何人,如何修復音頻
3 週前By尊渡假赌尊渡假赌尊渡假赌
WWE 2K25:如何解鎖Myrise中的所有內容
3 週前By尊渡假赌尊渡假赌尊渡假赌

熱工具

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

DVWA

DVWA

Damn Vulnerable Web App (DVWA) 是一個PHP/MySQL的Web應用程序,非常容易受到攻擊。它的主要目標是成為安全專業人員在合法環境中測試自己的技能和工具的輔助工具,幫助Web開發人員更好地理解保護網路應用程式的過程,並幫助教師/學生在課堂環境中教授/學習Web應用程式安全性。 DVWA的目標是透過簡單直接的介面練習一些最常見的Web漏洞,難度各不相同。請注意,該軟體中

VSCode Windows 64位元 下載

VSCode Windows 64位元 下載

微軟推出的免費、功能強大的一款IDE編輯器

SublimeText3 英文版

SublimeText3 英文版

推薦:為Win版本,支援程式碼提示!

SAP NetWeaver Server Adapter for Eclipse

SAP NetWeaver Server Adapter for Eclipse

將Eclipse與SAP NetWeaver應用伺服器整合。