连连看程序设计报告

更新时间:2024-03-31 03:32:01 阅读量: 综合文库 文档下载

说明:文章内容仅供预览,部分内容可能不全。下载后的文档,内容与下面显示的完全一致。下载之前请确认下面内容是否您想要的,是否完整无缺。

《计算机应用基础与程序设计》

三级项目设计报告

项目名称: 连连看

班 级: 2014级软件工程8班 学 号: 140120010226 姓 名: 郭鹏飞

日 期: 2014年12月31日

说明:报告内容可以根据自己的设计实践自行拟定和修改,内容尽量翔实,页数不够可以加页,内容过于单薄将影响得分。建议写报告之前自己上网或在图书馆查阅相关资料进行报告书写方法的自学。

一、项目分析

项目分析简要说明项目主要用户需求、设计思路及模块划分。 1、用户需求分析

在当今社会,人们的生活压力越来越大,也没有太多时间去玩大型的游戏,所以需要一些简单的小游戏来缓解大家的工作和学习压力。而连连看是一款简单,容易理解,大多数人都会玩的小游戏,耗费时间也不是太长,所以特意设计此款游戏来满足用户放松心情的需求。

2、系统设计思路

(1)必须先导入图片,当俩张图片一样时,便可以消除; (2)窗体基本的菜单栏必须有; (3)有时间轴控制时间,确定输赢;

(4)当时间到时,不能继续游戏;赢了的话,要保存游戏者姓名和判断胜负的时间;

(5)最好有历史记录,记录下来用户每一次的得分情况。 3、系统模块划分

板块一:图片导入。连连看应该有用来点击的图片,使用户避免因为只有数字而感到无聊。

板块二:图片标记。连连看需要点击俩次图片,所以必须记录第一次点击的图片是哪一个。

板块三:图片连线。该过程是整个游戏的核心过程,是最重要的一个过程,也是耗时最长的一个过程。连线方式主要分为三种方式,即直线连接、一个折点连接、俩个折点连接。如果用户前后点击的图片可以通过三种方式中的任意一种连接,并且图片完全一样,则达到消除图片的效果。

板块四:判断成功。通过判断图片是否完全消除,判断游戏是否胜利。

1

板块五:判断失败。如果时间到的话,用户失败,增加了游戏的竞争性。 板块六:重新开始。但用户游戏结束或想要重新开始时,可以重新开始新的游戏。

板块七:游戏暂停。通过游戏暂停,使用户有事时可以暂停游戏,之后可以继续游戏。

板块八:游戏难度。满足不同程度的用户的不同需求,共分为“简单”、“一般”、“复杂”三个难度,享受不同的挑战难度。

板块九:历史记录。将用户玩过的记录保存下来,同时可以使用户名和游戏时间都保存下来,并且和游戏难度一一对应。

板块十:背景音乐。为了使用户在游戏过程中更加享受,增加背景音乐和消除音乐,并且任何时候可以控制音乐的开始和结束。

二、项目设计

详细介绍项目各个子模块的设计方法及设计核心技术。 1、各个子模块的设计方法

板块一:图片导入。图片的导入有很多种方法,在该游戏中我采用了ImageList的方法。添加一个ImageList控件,Name为ImageList1,在其Image的属性中添加图片,之后再将图片导入窗口。同时,为了设计简单,我使用了二维Button来承载图片,在i,j的循环中添加代码,具体代码如下:

MyButton[i, j].Name = i.ToString();//为了判断图片是否相同 MyButton[i, j].ImageList = imageList1;

MyButton[i, j].ImageIndex = i - 1;

板块二:图片标记。首先判断是否已有图片选中,如果有则判断是否满足条件;如果没有,则该图片被选中,同时做出标记,我的方法是使背景变为红色,所以在导入图片时没有使图片放大。具体代码如下:

Button button = (Button)sender; button.BackColor = Color.Red; if (BeforeButton == null) {

BeforeButton = button;

2

button = null; } else {

if (button == BeforeButton)//如果前后选择图片为图一张,则取消选择

{

BeforeButton = null;

button.BackColor = this.BackColor; } else

//判断是否可以消除

板块三:图片连线。连线时游戏的核心代码。首先判断直连,如果俩张相同的图片中间没有别的图片则满足条件可以消除。此处通过网上查阅资料,用到了返回值。具体代码如下:

//竖直方向的连线

bool Y_line(int Y1, int Y2,int x) {

if (Y1 > Y2)//保证Y1

int t = Y1; Y1 = Y2; Y2 = t; }

for (int m = Y1 + 1; m <= Y2; m++) {

if (m == Y2) break;

if (MyButton[x, m].Visible !=false) {

return false; } }

return true;

3

}

//水平方向的连线

bool X_line(int X1, int X2, int y) {

//保证X1 X2) {

int t = X1; X1 = X2; X2 = t; }

for (int m = X1 + 1; m <= X2; m++) {

if (m == X2) break;

if (MyButton[m, y].Visible !=false) return false; }

return true;

}

然后是一个折点,需要判断被选中的俩个图片的相对位置,此处直接应用了直连的部分代码:

bool OneCorner(int X1, int Y1, int X2, int Y2) {

if (Y1 > Y2) {

int t = Y1; Y1 = Y2; Y2 = t; t = X1; X1 = X2; X2 = t; }

if (Y1 < Y2) {

if (X1 < X2) {

if (MyButton[X1, Y2].Visible == false)//左下 {

if (X_line(X1, X2, Y2) == true)

4

{

if (Y_line(Y1, Y2, X1) == true) {

z1.X = MyButton[X1, Y2].Left + length / 2; z1.Y = MyButton[X1, Y2].Top + length / 2; return true; } } }

if (MyButton[X2, Y1].Visible == false)//右上 {

if (X_line(X1, X2, Y1) == true) {

if (Y_line(Y1, Y2, X2) == true) {

z1.X = MyButton[X2, Y1].Left + length / 2; z1.Y = MyButton[X2, Y1].Top + length / 2; return true; } } } }

if (X1 > X2) {

if (MyButton[X1, Y2].Visible == false)//右下 {

if (X_line(X2, X1, Y2) == true)//经过直连后 X1 X2大小发生变化

{

if (Y_line(Y1, Y2, X1) == true) {

z1.X = MyButton[X1, Y2].Left + length / 2; z1.Y = MyButton[X1, Y2].Top + length / 2; return true; } }

5

}

if (MyButton[X2, Y1].Visible == false)//左上 {

if (X_line(X2, X1, Y1) == true) {

if (Y_line(Y1, Y2, X2) == true) {

z1.X = MyButton[X2, Y1].Left + length / 2; z1.Y = MyButton[X2, Y1].Top + length / 2; return true; } } } } }

return false;

}

接着是俩个折点的代码,首先先通过上下左右四个方向分开论述,其代码如下: //二折连——右

bool right(int X1, int X2, int Y1, int Y2) {

for (int x = X1 + 1; x <= Horizontal+1; x++) {

if (X_line(X1, x, Y1) == true && MyButton[x, Y1].Visible == false) {

if (OneCorner(x, Y1, X2, Y2) == true) {

z1.X = MyButton[x, Y1].Left + length / 2; z1.Y = MyButton[x, Y1].Top + length / 2; z2.X = MyButton[x, Y2].Left + length / 2; z2.Y = MyButton[x, Y2].Top + length / 2; return true; } }

if (X_line(X1, Horizontal + 1, Y1) == true) {

6

if (X_line(X2, Horizontal + 1, Y2) == true) {

z1.X = MyButton[Horizontal + 1, Y1].Left + length / 2; z1.Y = MyButton[Horizontal + 1, Y1].Top + length / 2; z2.X = MyButton[Horizontal + 1, Y2].Left + length / 2; z2.Y = MyButton[Horizontal + 1, Y2].Top + length / 2; return true; } } }

return false; }

//二折连——左

bool left(int X1, int X2, int Y1, int Y2) {

for (int x = X1 - 1; x >= 0; x--) {

if (X_line(X1, x, Y1) == true && MyButton[x, Y1].Visible == false) {

if (OneCorner(x, Y1, X2, Y2) == true) {

z1.X = MyButton[x, Y1].Left + length / 2; z1.Y = MyButton[x, Y1].Top + length / 2; z2.X = MyButton[x, Y2].Left + length / 2; z2.Y = MyButton[x, Y2].Top + length / 2; return true; } } }

if (X_line(X1, 0, Y1) == true) {

if (X_line(X2, 0, Y2) == true) {

z1.X = MyButton[0, Y1].Left + length / 2; z1.Y = MyButton[0, Y1].Top + length / 2;

7

z2.X = MyButton[0, Y2].Left + length / 2; z2.Y = MyButton[0, Y2].Top + length / 2; return true; } }

return false; }

//二折连——上

bool up(int X1, int X2, int Y1, int Y2) {

for (int y = Y1 - 1; y >= 0; y--) {

if (Y_line(Y1, y, X1) == true && MyButton[X1, y].Visible == false) {

if (OneCorner(X1, y, X2, Y2) == true) {

z1.X = MyButton[X1, y].Left + length / 2; z1.Y = MyButton[X1, y].Top + length / 2; z2.X = MyButton[X2, y].Left + length / 2; z2.Y = MyButton[X2, y].Top + length / 2; return true; } } }

if (Y_line(Y1, 0, X1) == true) {

if (Y_line(Y2, 0, X2) == true) {

z1.X = MyButton[X1, 0].Left + length / 2; z1.Y = MyButton[X1, 0].Top + length / 2; z2.X = MyButton[X2, 0].Left + length / 2; z2.Y = MyButton[X2, 0].Top + length / 2; return true; } }

8

return false; }

//二折连——下

bool down(int X1, int X2, int Y1, int Y2) {

for (int y = Y1 + 1; y <= Upright+1; y++) {

if (Y_line(y, Y1, X1) == true && MyButton[X1, y].Visible == false) {

if (OneCorner(X1, y, X2, Y2) == true) {

z1.X = MyButton[X1, y].Left + length / 2; z1.Y = MyButton[X1, y].Top + length / 2; z2.X = MyButton[X2, y].Left + length / 2; z2.Y = MyButton[X2, y].Top + length / 2; return true; } } }

if (Y_line(Y1, Upright + 1, X1) == true) {

if (Y_line(Y2, Upright + 1, X2) == true) {

z1.X = MyButton[X1, Upright + 1].Left + length / 2; z1.Y = MyButton[X1, Upright + 1].Top + length / 2; z2.X = MyButton[X2, Upright + 1].Left + length / 2; z2.Y = MyButton[X2, Upright + 1].Top + length / 2; return true; } }

return false;

}

然后将二折连的四种分类综合在一起,代码如下:

bool TwoCorner(int X1, int X2, int Y1, int Y2)

9

{

if (right(X1, X2, Y1, Y2) == true) return true;

if (left(X1, X2, Y1, Y2) == true) return true;

if (up(X1, X2, Y1, Y2) == true) return true;

if (down(X1, X2, Y1, Y2) == true) return true; else

return false;

}

最后将直连、一折连、二折连的代码统统综合起来,代码如下:

bool Path() {

if (X1 == X2) {

if (Y_line(Y1, Y2, X1) == true) {

LinkWay = GamePath.line; return true; } }

if (Y1 == Y2) {

if (X_line(X1, X2, Y1) == true) {

LinkWay = GamePath.line; return true; } }

if (OneCorner(X1, Y1, X2, Y2) == true) {

LinkWay = GamePath.OneCorner; return true;

10

}

if (TwoCorner(X1, X2, Y1, Y2) == true) {

LinkWay = GamePath.TwoConcer; return true; } else

return false;

}

通过这些代码,该游戏连线的判定就解决了。

板块四:判断成功。当窗口中没有图片时,便可以弹出窗口通知用户已完成游戏,并记录游戏者姓名。记录姓名在板块九会详细介绍,下面是判断是否完成游戏的代码:

private bool Win() {

for (i = 1; i <= Horizontal; i++) {

for (j = 1; j <= Upright; j++) {

if (MyButton[i, j].Visible == false) continue; else

return false; } }

if (i == Horizontal+1) return true; else

return false;

}

板块五:判断失败。该过程比较简单,当时间结束时游戏即失败,代码如下:

private bool Lose() {

11

if (time <= 0) {

timer1.Enabled = false; return true; } else

return false;

}

该代码应该在时间空间Timer的Tick时间中执行。

板块六:重新开始。在窗口中添加“主菜单”按钮,用户可以通过返回主菜单重新开始游戏。在该事件下加游戏初始化的代码,具体如下:

private void initialise()//初始化游戏 {

score = 0; //时间轴 time = 60;

toolStripProgressBar1.Maximum = time; toolStripProgressBar1.Minimum = 0; toolStripProgressBar1.Value = time; //时间轴

toolStripStatusLabel1.Text = \你的分数:\ + score.ToString(); toolStripStatusLabel2.Text = \所剩时间:\ + time.ToString(); buttonMean.Visible = true; buttonBegin.Enabled = true;

for (i = 0; i <= Horizontal+1; i++) {

for (j = 0; j <= Upright+1; j++) {

MyButton[i, j] = new Button();

MyButton[i, j].Size = new Size(length, length); MyButton[i, j].Left = 3 * distance + (i - 1) * length; MyButton[i, j].Top = 3 * distance + (j - 1) * length + menuStrip1.Height;

MyButton[i, j].Visible = false; Controls.Add(MyButton[i, j]);

12

} }

for (i = 1; i <= Horizontal; i++) {

for (j = 1; j <= Upright; j++) {

Button button = new Button(); button = MyButton[i, j];

button.Click += new EventHandler(Mybt_Click);

MyButton[i, j].Name = i.ToString();//为了判断图片是否相同 MyButton[i, j].ImageList = imageList1; MyButton[i, j].ImageIndex = i - 1;

MyButton[i, j].Visible = true; //导入图片 } }

//调整各个控件 窗体的位置 location();

}

板块七:游戏暂停。当游戏暂停时,该按钮应该显示“继续”,同时时间停止,用户不能看到游戏界面;继续游戏后,按钮显示“暂停”,同时时间开始继续减少。时间停止与继续通过控制Timer的Enable属性。为了遮盖游戏界面,用了PictureBox空间,通过控制该空间的Visibl属性控制游戏界面的可见性,同时可以在游戏开始时显示该图片,使界面更加美观。

板块八:游戏难度。该设计也比较简单,通过控制图片的多少来控制难度。游戏难度可以在开始游戏之前选择,不再赘述。

板块九:历史记录。该过程主要应用了文件的读取和窗体之间变量的传递。记录必须保存在当地的文件里,否则游戏关闭后记录将不再存在,都取时也必须从当地文件读取。其中文件应分为简单游戏的姓名,分数,一般难度的姓名、分数和困难难度的姓名和分数构成,以简单游戏为例。同时窗口间变量的传递可以将要传递的变量写在form后的括号中,然后承接的窗口要有接受的变量,类似于事件之间的传递。

13

接着是保存记录,当游戏胜利,弹出FormWin窗口,可以记录游戏者姓名和时间,代码如下:

{

FileStream fs = new FileStream(@\, FileMode.Append); StreamWriter sw = new StreamWriter(fs, Encoding.UTF8);//解决乱码

FileStream fsTime = new FileStream(@\, FileMode.Append); StreamWriter swTime = new StreamWriter(fsTime, Encoding.Default); sw.WriteLine(textBox1.Text); swTime.WriteLine(time.ToString()); sw.Close(); fs.Close(); swTime.Close(); fsTime.Close(); }

这样便可以把记录存到当地的文件夹里。

然后是点击排行榜是需要读取文件,还是以简单模式为例,弹出新的窗口FormTop,代码如下:

{

StreamReader sr = new StreamReader(@\, false); StreamReader srTime = new StreamReader(@\, false); MyName = sr.ReadToEnd();//读取全部 time = srTime.ReadToEnd(); sr.Close(); srTime.Close();

label1.Text += \ + MyName;

label2.Text += \ + time; }

板块十:游戏音乐。游戏音乐分为背景音乐和消除音乐。消除音乐内存比较小,可以直接用WAV格式,代码也比较简单,当消除成功时执行如下代码:

SoundPlayer SP = new SoundPlayer(); SP.SoundLocation = @\;

14

SP.Play();

然后北京音乐如果用WAV格式的话内存太大,为减小游戏内存,所以将北京音乐换为WMA格式,同时上面的代码不可用,所以我通过百度找到一些代码,虽然不是太懂,但为了游戏的完整性,还是使用了。首先要声明空间,即:

using System.Runtime.InteropServices; using System.Media;//播放音乐用的空间

接着,在class FormMain中添加如下代码:

public static uint SND_ASYNC = 0x0001; // play asynchronously

public static uint SND_FILENAME = 0x00020000; // name is file name

[DllImport(\)]

public static extern uint mciSendString(string lpstrCommand,

string lpstrReturnString, uint uReturnLength, uint hWndCallback); //播放音乐 //没看懂。。。。

最后在你想要播放音乐的位置添加如下代码:

mciSendString(@\, null, 0, 0);

mciSendString(@\,

null, 0, 0);

mciSendString(\, null, 0, 0); (其中.\\Sound\\BackMusic.wma为文件位置)

我在FormMain中添加了一段,实现了北京音乐的播放,为了实现音乐的暂停,当用户关闭背景音乐时,执行相似代码,将文件换为空白文件即可实现。

2、设计核心技术

核心技术主要是连线的实现,上面已经阐述,同时为了美观,添加

15

了一些基本功能。 (1)GDI+画图

当连线条件满足时,画出俩个图片之间的连线方式,首先需要找出图片的中心,如果是直连,可以直接连接,一折连和二折连需要找出他们的折点然后连线,之后停顿几秒后连线要消失。 连线的代码如下:

private void DrawLine(int X1, int X2, int Y1, int Y2,Color myColor,GamePath LinkWay) {

Graphics g = this.CreateGraphics(); Pen myPen = new Pen(myColor, 2);

int x1 = MyButton[X1, Y1].Left + length / 2; int y1 = MyButton[X1, Y1].Top + length / 2; int x2 = MyButton[X2, Y2].Left + length / 2;

int y2 = MyButton[X2, Y2].Top + length / 2;//调整连线的点为Button的中点

switch (LinkWay) {

case GamePath.line:

g.DrawLine(myPen, x1, y1, x2, y2); break;

case GamePath.OneCorner:

g.DrawLine(myPen, x1, y1, z1.X, z1.Y); g.DrawLine(myPen, z1.X, z1.Y,x2, y2); break;

case GamePath.TwoConcer:

g.DrawLine(myPen, x1, y1, z1.X, z1.Y); g.DrawLine(myPen, z1.X, z1.Y, z2.X, z2.Y); g.DrawLine(myPen, z2.X, z2.Y, x2, y2); break; } }

如果消除条件满足,执行该事件。

16

(2)用到了枚举类型,将游戏的难易程度分开,以便统计,具体代码如下:

public enum style { easy, common, hard }; public enum Rank {easy,common,hard };

(3)应用了进度条来显示时间的多少,进度条的Maximum表示进度条最大时代表的值,必然,Minimum代表最小时代表的值,而通过使

Value

Value

当前值。可以

的减少来减少时间,为了增加游戏的可玩性,当消除成功时增

加2秒,失败后减少3秒。这些过程都通过控制Value的大小实现。 其他的一些核心技术在设计方法中已经介绍,不再赘述。

三、项目测试

17

简要介绍采用的测试方法和测试要点。 1、个人局部测试

方法:每次实现一个小功能后测试一下是否能够实现,如果出错,可以在前面添加MessageBox来显示感觉可能出错的地方,最终找出错误并且正确修改。

要点:要善于使用局部调试,找到自己的错误。

如:在判断图片是否相同时,开始直接比较俩个Button的Image是否相同,但执行不了,后来发现比较Image很复杂,所以我给相同的图片取相同的Name,通过判断Name来判断图片是否相同,解决了问题。

2、项目完成后整体调试

方法:完成后不断地自己运行该程序,不断的发现错误和不足。 要点:注意一些无法执行的控件,即没有写代码的控件,在整体调试时一定要删除。

3、请同学调试

主要是让同学玩,随便点,发现各种错误,现总结如下:

(1)问题:先选好难度,再点主菜单,再点开始,暂停,然后就会出现空白。

解决:在ButtonMean的Click事件中,将ButtonBegin的Enable属性设置为false;

(2)问题:时间到后,MessageBox点击确定关闭后,点暂停,再点继续,可以重新开始,时间只有1秒。

解决:如果输了,MessageBox.Show();之后将ButtonPause的Enable属性设置为false。

(3)问题:设置中的音乐开关没有标识。

解决:具体条件下具体分析,可以把“开”或“关”改成“开 ?”或“关 ?”。

(4)问题:退出按钮没有阴影效果;

18

解决:将ButtonClose的Cursor属性改为Hand。

四、结论

简要总结项目的主要工作、主要结果、心得感受主要发现以及下一步应当开展的主要工作等。

1、主要工作和结果。项目实现了连连看的基本功能,达到了最初为工作学习劳累的人减轻压力的目的。同时增加了背景音乐,增加了用户的享受性。通过困难程度的分类,扩大了游戏的使用人群,使不同技术水平的人都可以玩此游戏。最后,通过历史记录的实现,使用户可以看到自己的玩游戏过程中的进步。

2、新的感受。任何一个项目都不是简单完成的,而且要用到的知识不一定是你学习过的,所以在做项目时要善于利用自己身边的资源,通过网络、图书等途径获取知识。同时,同学之间应该相互学习,相互交流,互相促进自己的项目趋于完美。

3、下一步工作:游戏只是实现了历史记录,却没有实现排行榜功能,所以我希望今后可以实现排行榜的功能。

19

本文来源:https://www.bwwdw.com/article/d24r.html

Top