媒介:在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#编写拼图游戏的图文代码分享(下)的细致内容,更多请关注ki4网别的相干文章!