>  기사  >  백엔드 개발  >  C#에서 직소 퍼즐을 작성하기 위한 그래픽 및 텍스트 코드 공유(2부)

C#에서 직소 퍼즐을 작성하기 위한 그래픽 및 텍스트 코드 공유(2부)

黄舟
黄舟원래의
2017-04-18 09:11:331547검색

이 글에서는 주로 C# 퍼즐 게임 코드 작성의 두 번째 부분을 자세히 소개합니다. 관심 있는 친구는

서문을 참조할 수 있습니다. C# 직소 게임 작성 코드 프로그래밍 중 "직소 게임"(1부)의 업로드 이 글에서는 독자들이 이해하기 쉽도록 원리를 자세히 분석하여 설명하겠습니다. 이 프로그램에는 지적할 수 있는 많은 질문이 있으며 우리는 함께 배우고 성장할 수 있습니다!

텍스트:

퍼즐은 기본적으로 게임 방법과 시작 방법을 모두 알고 있습니다. 끝. 그렇다면 퍼즐을 만들고 싶을 때 어떻게 시작해야 할까요? 대답은: 현실에서 시작하여 요구 사항을 설명하는 것입니다.(문서로 설명하려고 노력하십시오.) 포괄적인 요구 사항이 있으면 코드에서 구현되어 결국 작업이 될 수 있는 신뢰할 수 있는 전략을 제공할 수 있습니다.

(1) 요구 사항: (이 요구 사항은 다소 엉성하게 작성되었으며 초보자를 대상으로 맞춤 제작되었습니다. 가장 일반적인 사람들의 생각과 게임에 참여하는 과정을 기반으로 합니다. )

 1. 그림: 퍼즐을 할 때 최소한 그림은 있어요

2. 자르기: 퍼즐은 그림이 아니라 잘라야 합니다. 전체 그림을 N*N개의 작은 조각으로 분해 그림

3. 방해: N*N개의 작은 그림의 순서를 섞되, 게임 규칙에 따라 걸어가면 복원할 수 있는지 확인하세요

4. 판단: 퍼즐이 성공했는지 판단

5. 상호 작용: 어떤 상호 작용 방법을 사용합니까? 여기서는 마우스 클릭을 선택합니다

6. 퍼즐의 전체 썸네일을 표시합니다. 원본 이미지

위는 기본 기능, 다음은 확장 기능입니다

7. 단계 수 기록: 완료하는 데 몇 단계가 필요한지 기록

8. 사진 바꾸기 : 한 장의 사진을 오랫동안 가지고 놀다 보면 바꿀 수 있을까요 ㅎㅎ

9. 난이도 선택 : 너무 쉬우나요? 원하지 않는다! 3*3 다음은 5*5, 5*5 다음은 9*9 입니다. 룸메이트가 0걸음 이상으로 최고 난이도300에 도전했습니다.

(2) 분석:

수요를 바탕으로 다음을 포함하여 이를 실현하는 방법(컴퓨터에서 실제 수요 매핑)을 분석할 수 있습니다.

1. 개발 플랫폼: 여기서 C# 언어를 선택하세요

1) 저장 공간: 무엇을 저장하고 싶은가요? 그것을 저장하기 위해 어떤 구조를 사용합니까? 요구 사항을 다시 살펴보면

이미지를 저장해야 하는 일부 리소스가 있음을 알 수 있습니다. 저장하려면 이미지

개체

를 사용합니다. 원본 이미지를 잘라낸 후 하위 이미지): 이후 유닛의 작은 그림을 저장하는 데 사용되는 이미지 객체와 정수로 저장된 숫자를 포함하는 구조 구조체 노드를 정의합니다(절단 후 각 작은 유닛에는 게임 완료 여부를 쉽게 확인할 수 있도록 숫자가 부여됩니다.

각 유닛(원본 사진을 잘라낸 후 하위 이미지 모음): 2차원 배열 사용(예: 퍼즐, 주사위 놀이, 제거 음악, Lianliankan, Tetris 및 기타 평면 도트) 매트릭스 게임) 저장하는 데 사용할 수 있는데 왜 비슷해 보이나요? ) 저장하는 방법

 난이도: 사용자 지정 열거 유형(쉬움 및 일반 및 어려움)을 사용하여 저장

 단계 수 : shaping Variable int Num Storage

Storage를 사용하면 모듈의 구분을 생각해볼 수 있습니다(올바른 논리 구분이 확장되어 통신도 더 명확해집니다) 이를 빌드하고 각 모듈에 관련된 특정 알고리즘을 구현합니다

먼저 프로그램 모듈은 4가지로 구분됩니다.

논리 유형:

1. 퍼즐 클래스: 퍼즐을 설명하는 데 사용

2. 구성 클래스: 스토리지 구성 변수

인터랙티브:

3. 게임 메뉴창 : 메뉴 옵션 만들기

4. 게임 실행창 : 게임의 메인 인터페이스

1 . 게임을 통해 메뉴는 난이도나 그래픽과 같은 구성을 조작할 수 있습니다.

2. 실행 중인 창은 게임 구성에 액세스하고 이를 얻을 수 있으며 해당 구성 퍼즐 개체를 사용할 수 있습니다.

3. 실행 중인 창을 통해 사용자가 상호작용하여 간접적으로 퍼즐 객체가 이동 메소드를 호출하고 패턴 메소드를 얻도록 합니다

 코드를 읽는 학생들이 가장 문제가 되고 불합리한 부분은 열거형의 난이도를 퍼즐 수업에서 작성해야 한다는 점, 아니면 구성 클래스에서 작성해야 한다는 점이라고 생각합니다. 독자 여러분 스스로 변경할 수 있습니다


rree

구성 클래스는 데이터 저장소와 같다고 생각하면 되고, 퍼즐 클래스는 논리 처리로 사용된다고 보면 됩니다. 그리고 메뉴와 실행창은 상호 작용을 위한 프리젠테이션으로 사용됩니다. 이 디자인이 그다지 합리적이지 않다는 점은 인정하지만 문제의 규모가 크지 않은 경우 디자인을 과도하게 고려하면 프로그램이 부풀어오르게 될까요? 어느 정도 정도는 있을 거라 생각하는데, 이 프로그램은 그냥 구현하고, 디자인(루틴형)에 집착하는 게 때로는 득실도 더 크다고 생각해요. (개인적인 미숙한 의견)

(3) 코드 구현:

설명: 이 블록은 Puzzle 클래스와 구체적인 구현 및 게임 런닝 클래스의 엔터티 통신:

퍼즐의 구성 방법:

과제:

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

// 퍼즐 그림, 너비(설명: 정사각형의 한 변의 길이, 단위는 픽셀, 이름은 Ambiguity, 죄송합니다), 게임 난이도

게임 난이도에 따라 분할 정도가 결정됩니다. 예를 들어 단순은 3에 해당합니다. 행 3열, 보통은 5행 5열에 해당합니다. 난이도는 9행 9열에 해당합니다


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

2. 사진 분할


 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;
  }

실제로 단위 배열에 값을 할당하는 과정은 이중 계층 for 루프를 사용하여 순회합니다. 2차원 배열을 만든 다음 node[x,y].Num을 순서대로 할당합니다.

그런 다음 장치의 작은 그림인 node[x, y].Img에 값을 할당합니다. 할당 방법은 C#의 이미지 클래스 라이브러리에 스크린샷 메소드를 작성하는 것입니다. 이 방법을 사용하여 그림에서 해당 위치에 해당하는 크기를 잘라서 노드[에 저장합니다. x,y].Img;

너비/N이란 무엇인가요? 변의 길이를 행의 개수로 나눈 값이 바로 간격이고, 간격은 각 단위의 변의 길이입니다! 그러면 시작 좌표(X, Y)는 몇 단위 후에 내 위치가

, 즉 (x, y) =임을 의미합니다. (유닛 측면 길이 *유닛 수 X축 시작점부터 단위변 길이 *Y축 시작점부터 단위수)

이런 문제는 독자들이 그림을 더 많이 그려주셨으면 좋겠습니다. 그러면 자연스럽게 이해하게 될 것입니다.

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

기본 논리: DrawImage 메서드 사용:


 //分割图片形成各单元保存在数组中
  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++;
  }
  }

분할 후에는 특별한 프로세스를 수행해야 합니다. 흰색 위치죠? 기본적으로 마지막 위치는 node[N-1, N-1]입니다.

가 쓰여지고 흰색 그림으로 바뀌며, 주변 가장자리는 빨간색으로 칠해집니다. 다른 것들은 더 눈에 띄게 만들기 위해 다른 이전 유닛에도 테두리를 그렸지만 장식적인 가치 측면에서 퍼즐을 차별화하기 위해 흰색이었습니다. 이 코드는 도입되지 않습니다.

3. 그림 스크램블:

실제로는 2차원 배열을 스크램블하는 것입니다. 방법이 있지만 주의하시기 바랍니다! 모든 혼란을 복구할 수 있는 것은 아닙니다!

그럼 어떻게 할 수 있나요? 방법은 이해하기 매우 간단합니다. 게임이 시작되기 전에 규칙 없이 규칙에 제공된 걷기 방법에 따라 컴퓨터가 완전하고 질서 있는 유닛을 여러 번 걷게 하는 것입니다! 즉, 이 방법으로 확실히 돌아갈 수 있습니다!

먼저 이해하시고, 구체적인 방해 방법은 나중에 설명하겠습니다.

이동 방법(Move) :

퍼즐 게임에서 사각형의 이동은 실제로 인접한 두 유닛을 교환하는 것으로, 이 두 유닛 중에는 반드시 흰색 단위가 있습니다(즉, 위에서 언급한 노드[N-1,N-1] 단위, 해당 번호는 N*N-1이므로 직접 계산하는 것이 좋습니다)

그래서 우리의 판단은 조건 네, 블록을 이동할 때 상하좌우 4방향으로 인접한 흰색 유닛, 즉 유닛번호 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#에서 직소 퍼즐을 작성하기 위한 그래픽 및 텍스트 코드 공유(2부)의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.