贪吃蛇课程设计报告 2

基于VC#.NET的

贪吃蛇游戏的

目录

1. 实验目的………………………………………………………………

2. 实验任务与要求………………………………………………………

2.1实验内容………………………………………………………………

2.2实验要求………………………………………………………………

2.3实验环境………………………………………………………………

3. 设计方案…………………………………………………………………

3.1程序功能………………………………………………………………

3.2设计思想………………………………………………………………

3.3设计总体流程图………………………………………………………

3.4设计的具体实现………………………………………………………

4. 程序测试…………………………………………………………………

4.1测试内容与结果………………………………………………………

4.2程序运行效果图………………………………………………………

5. 实验总结……………………………………………………………… 参考文献…………………………………………………………………… 附录…………………………………………………………………………… 附录A:主要源程序……………………………………………………… 附录B:软件使用说明书…………………………………………………

1. 实验目的:

? 复习、巩固VC#.NET的基础知识,进一步加深对VC#.NET技术的理

解和掌握;

? 课程设计为学生提供了一个既动手又动脑,独立实践的机会,将课

本上的理论知识和实际有机的结合起来,锻炼学生的分析解决实际问题的能力。

? 培养学生在项目开发中团队合作精神、创新意识及能力

2. 实验任务与要求

2.1 实验内容

贪吃蛇游戏开发,首先它可以为大家提供一种当前比较流行的休闲小游戏。贪吃蛇是家喻户晓的益智类小游戏,选择这个题目一是为了将自己的所学知识加以运用,二是一直以来贪吃蛇这个游戏就深深地吸引着很多人,它的制作方法对于很多同学而言都是很神秘的。所以我们希望通过这学期所学知识把它剖析开来,真真正正的了解它的本质和精髓。在这次学习中我们将从实践和实际的项目中提高自己的编程能力。因此决定选择这个题目作为VC#.NET的课程设计。

最后一部分就是游戏的模块划分,根据分析,贪吃蛇这个程序一共要实现如下几个功能,包括游戏方面开始游戏、暂停游戏以及停止游戏,游戏设置(蛇的颜色、食物的颜色、游戏难度设置),游戏帮助(游戏控制说明)与积分。

2.2实验要求

? 对系统进行功能模块分析、控制模块分析正确,符合课题要求,实

现相应功能;可以加以其他功能或修饰,使程序更加完善、合理; ? 系统设计要实用,采用模块化程序设计方法,编程简练、可用,功

能全面;

? 说明书、流程图要清楚;

? 记录设计情况(备查,也为编写设计说明书作好准备);

2.3实验环境

WindowsXP操作系统,VS.NET2005开发环境。

3. 设计方案

3.1程序功能

游戏界面:

1)开始游戏(加载并开始)

2)暂停游戏(从当前进度结束)

3)重新开始(在某一时刻暂停后可以继续接着玩)

4)停止游戏(停止正在玩的游戏,再次开始时是重新开始)

游戏设置:

1) 蛇的颜色(白色、蓝色、黄色)

2) 游戏难度设置(1级、2级、3级)

3) 游戏帮助(游戏控制说明)

4) 积分(每次吃掉一个食物增加5分,并显示出来)

3.2设计思想

首先,该游戏有两个类,以及一个枚举类型,列举了蛇块移动的四个方向;一个蛇块类,用来描述蛇块的信息和将自身绘制到画布上;另一个是画蛇类,画蛇类里要有开始函数,调用开始函数可以使游戏开始,游戏死亡函数,让游戏会结束,既然是贪吃蛇,就少不了蛇的移动,食物的生成,游戏是动态的,在贪吃蛇吃食物的过程中,每吃一个界面就需要更新一下,当然需要用到定时更新函数和刷新画板,保持游戏的动态和连贯性。大概思想是这样的,里面需要注意的是,蛇什么情况下死亡,有两种情况,第一,蛇撞到墙,第二,舌头蛇尾相接。蛇在移动时,如果遇到食物,就把食物吃了,蛇的身子会长一节,如果前方没有食物,就响应键盘,按照方向移动。蛇可以沿着墙壁走,但是沿着墙壁走和撞墙是有区别的,这些细节要区分开。

其次,设计主界面,即一个Form框,在框里放一个PictureBox框,设置画布类来作为贪吃蛇游戏的背景。界面里要有开始按钮,暂停、重新开始、退出这些按钮。开始就是要初始化并加载游戏,暂停是当游戏在进行时,可以短时间内终止,重新开始,是继续暂停时的游戏,退出要将该游戏系统关闭,便可以退出。为了满足游戏的动态画面效果和适应不同级别的人群,要设置游戏蛇块颜色的菜单,食物颜色的菜单,以及游戏等级菜单,来最大可能地满足不同需求的人群,扩大市场的需求,为了更细心体贴,再设置一个帮助菜单更好,引导人们更方便地使用一些功能。

大体上就是这样吧,有了思想在敲打代码的过程中就有了目标,思路会清晰,不过一些细节性的问题要注意。

3.3设计总体流程图

贪吃蛇课程设计报告2

3.4设计的具体实现

1)、蛇块类

把蛇分成一块一块,一个蛇相当于有若干个块,蛇块类就是用来描述每块的信息。

a)、蛇块类的私有变量:

private Color _color;//颜色

private int _size;//大小

private Point _point;//坐标

b)、构造函数:

public snakeBlock(Color color, int size, Point p)

c)、获取记录蛇块的坐标:public Point Point//属性

d)、Paint函数:public virtual void Paint(Graphics g)

Paint函数根据坐标以及蛇块大小计算出实际绘图的坐标,进行绘图,蛇块是一个正方形(矩形),我们用矩形绘制,在代码中我们看到,绘制的大小比size小1个像素,这么做的原因是为了突出蛇块与蛇块之间的间隙,也就是说蛇块与蛇块之间有1个像素的间隙,这就是我们看到游戏效果图中的蛇块是一段一段的原因。

2)、画蛇类

画蛇类是真个游戏的核心处理类,里面定义了画布的大小,背景色,蛇块列表以及游戏速度,移动方向等属性另外还提供了timer计时器,用于定时更新蛇块坐标位置,以及如何在画面上画图的函数。

a)、Start函数用于开始游戏,这个函数的内部其实就是设定食物,以及触发计时器。这里面定义了计时器的事件触发间隔,是通过level然后找到speed数组里的对应毫秒值设定的。

然后触发事件是OnBlockTimedEvent,最后是启动计时器开始计时。代码如下: public void Start()

{

this._food = GetFood();//生成一个食物

timerBlock = new System.Timers.Timer(_speed[this._level]); timerBlock.Elapsed += new

System.Timers.ElapsedEventHandler(OnBlockTimedEvent);

timerBlock.AutoReset = true;

timerBlock.Start();

}

b)、OnBlockTimedEvent函数是计时器的执行函数,这个函数用于更新蛇块信息列表,以及检测游戏是否结束,函数内部调用了两个函数,一个是Move函数,一个是CheckDead函数。代码如下:

private void OnBlockTimedEvent(object source, ElapsedEventArgs e)//定时更新

{

this.Move();

if (this.CheckDead())

{

this.timerBlock.Stop(); //停止引发事件

this.timerBlock.Dispose(); //释放资源

int score=(this._blocks.Count-1)*5;

System.Windows.Forms.MessageBox.Show("游戏失败,得分为:" + score, "Game Over!");

}

}

c)、checkDead函数用于检测游戏是否结束,检测是否结束分两种情况,一种是撞上边界,即蛇头坐标超出画布范围,一种是蛇头撞上了蛇身,也就是蛇头的坐标和蛇身的某个坐标重合,通过for循环来检查第二种情况。代码如下: private bool CheckDead() //检查游戏是否结束

{

snakeBlock head = (snakeBlock)(this._blocks[0]);//超出画布范围

if (head.Point.X-1 < 0 || head.Point.Y-1 < 0 ||

head.Point.X >= this._width -1|| head.Point.Y >= this._height-1) return true;

for (int i = 1; i < this._blocks.Count; i++)

{

snakeBlock b = (snakeBlock)this._blocks[i];//是否头尾相接

if (head.Point.X == b.Point.X && head.Point.Y == b.Point.Y)

{

this._isGameOver = true;

return true;

}

}

this._isGameOver = false;

return false;

}

d)、GetFood函数用于生成下一个食物,其实就是一个蛇块,生成的规则就是,坐标要在画布范围内,并且食物的坐标不能和贪吃蛇的坐标重合,具体代码如下通过for循环检查食物坐标是否和贪吃蛇的蛇块列表ArrayList里的蛇块有冲突。代码如下:

private snakeBlock GetFood() //生成下一个食物,也就是下一节蛇块 {

snakeBlock food = null;

Random r = new Random();

bool redo = false;

while (true)

{

redo = false;

int x = r.Next(this._width);

int y = r.Next(this._height);

for (int i = 0; i < this._blocks.Count; i++) {

snakeBlock b = (snakeBlock)(this._blocks[i]); if (b.Point.X == x && b.Point.Y == y)

{

redo = true;

}

if (redo == false)

{

food = new snakeBlock(Color.Gold, this._size, new Point(x, y));

break;

}

}

return food;

}

}

e)、Move函数用于更新整个蛇块的坐标,我们前面通过将蛇块信息放到

ArrayList里来表示贪吃蛇的整个信息,其中根据下标从0到Count-1依次表示各个蛇块的信息,实际代码中增加了吃食物的情况判断,也就是说如果新蛇头的坐标和食物的坐标一致,那么就不做删除原蛇尾的操作了,这样产生的效果就是蛇块列表的长度增加了一个,达到吃食物变长的效果。代码如下:

private void Move() //蛇体移动

{

Point p = new Point(); //下一个位置

snakeBlock head = (snakeBlock)(this._blocks[0]); if (this._direction == Direction.Up)

p = new Point(head.Point.X, head.Point.Y - 1); else if (this._direction == Direction.Down)

p = new Point(head.Point.X, head.Point.Y + 1);

else if (this._direction == Direction.Left)

p = new Point(head.Point.X - 1, head.Point.Y); else

p = new Point(head.Point.X + 1, head.Point.Y); //生成新坐标,将成为蛇头

snakeBlock b = new snakeBlock(Color.Red, this._size, p);//如果下一个坐标不是当前食物坐标,那么从蛇块信息列表中删除最后一个蛇块 if (this._food.Point != p)

this._blocks.RemoveAt(this._blocks.Count - 1); //如果下一个坐标和食物坐标重合,那么就生成一个新食物

else

{

this._food = this.GetFood(); //把下一个坐标插入到蛇块信息列表的第一个,使其成为蛇头

fen += 5;

}

this._blocks.Insert(0, b); // 将元素插入指定索引处

this.PaintPalette(this._gpPalette); //更新画板

}

f)、PaintPalette函数需要一个参数,也就是绘图句柄,然后在这个画布上画图也就是我们看到的游戏效果,首先用背景色清空画布,然后画食物,其次是通过for循环将贪吃蛇的每个蛇块画在画布上,以此达到游戏效果。代码如下: public void PaintPalette(Graphics gp) //更新画板

{

gp.Clear(this._bgColor); //清除整个绘图面并以指定背景色填充

this._food.Paint(gp);

foreach (snakeBlock b in this._blocks)

{

b.Paint(gp);

}

g)、构造函数很简单就是设定类属性值,还有初始化蛇块列表ArrayList,然后需要说明的是你得手动生成一个蛇块放到列表里面,否则连个蛇头都没有怎么玩啊。this._blocks.Insert(0, (new Block(Color.Red, this._size, new Point(width / 2, height / 2))));

这句话就是在画布的正中央产生一个只有一节的贪吃蛇。代码如下:

public snakeMode(int width, int height, int size, Color bgColor, Graphics g,int lvl) // 构造函数

{

this._width = width;

this._height = height;

this._bgColor = bgColor;

this._gpPalette = g;

this._size = size;

this._level = lvl;

this._blocks = new ArrayList();

this._blocks.Insert(0, (new snakeBlock(Color.Red, this._size, new Point(width / 2, height / 2))));

this._direction = Direction.Right;

}

4.程序测试

4.1测试内容与结果

游戏方面:

1) 开始:程序运行后,会出来只有一节的红色蛇和一个黄色的食物,并且蛇是静止的。点击开始按钮后,红色的蛇开始运动。所以开始按钮使用正确。

2) 方向键和WSAD键:蛇开始运行之后,当点击上下左右键(或WSAD)蛇就会按相应的方向运动,由于现实中蛇不会180度的转弯,所以就进行设置,让蛇块只能进行90度的转弯。程序检测时蛇确实会按点击方向运动且不会向相反方向运动,所有方向键使用正确。

3) 暂停/继续:当程序正在运行时,点击暂停/继续按钮,蛇停在当前的位置,再次点击蛇又从当前位置继续运动。此按钮符合要求,所以暂停/继续按钮使用正确,功能能够实现。

4) 重新开始:当游戏结束或想重新开始一局时,点击此按钮,游戏又回到最初的状态,一个食物,一个运动着的红色蛇块,且游戏积分清零。所以重新开始功能能够实现。

5) 退出:点击退出按钮,游戏窗口关闭,回到程序界面。所以退出功能可以实现。

游戏设置:

1) 游戏等级设置:当在游戏还没开始时点击了菜单上的不同等级选项后,再点击开始按钮,蛇会以不同的速度运动。从1级到5级蛇的速度逐渐加大。所以等级设置正确,此游戏能够实现不同速度的运动。

2) 蛇颜色设置:当在游戏还没开始时,点击了菜单上的蛇颜色设置按钮中的不同颜色选项后,再点击开始按钮,会出现不同颜色的蛇。比如:点击白色,开始之后会出现一个运动着的白色蛇块。所以蛇的颜色设置正确,可以实现不同颜色的贪吃蛇。

3) 游戏帮助:打开此项菜单,包括游戏等级设置说明和蛇颜色设置说明两项。

4) 积分:当蛇吃了一块食物后,积分文本框内会相应的实现加10分的功能,并且当游戏结束之后会弹出一个对话框,显示游戏结束和总积分。

4.2程序运行效果图

游戏方面:

贪吃蛇课程设计报告2

游戏设置:

1)等级设置:

贪吃蛇课程设计报告2

2)蛇颜色设置:

贪吃蛇课程设计报告2

3)积分:

贪吃蛇课程设计报告2

5.实验总结

通过这次试验,让我学到很多这个学期没有接触过的很多知识,完成一个贪吃蛇的项目;第一步就是先理清思路,设计主界面,主界面是以一具封闭的 画布,并在画布的设计蛇体和食物。蛇头如果碰到墙或者与蛇身的某一部分碰到就会死亡;如果蛇头碰到食物,就会自动增加一节蛇身,并更改得分。

想要写出好的程序,还是要有扎实的基础,这样遇到问题就不会一筹莫展来。在编程时我们要有想象力,不要拘泥于现有的贪吃蛇游戏,而是要把自己想到的再加上所学的知识,遇到问题不能怕,而是要专心找出问题的原因,再回来看看书本的知识,不懂的还可以问问老师。编程过程还要养成良好的编程习惯,这样不仅自己能够很好看懂自己的代码,也让人家容易看懂自己的代码,而不至于代码知识自己会看,出来问题只有自己会解决而其他人都不会解决;遇到问题时要考虑最简单的解决方案,只有简单的方案不能满足要求时再考虑复杂的方案。最主要编程还是要多与人沟通,不会的不能立即问别人,但是一段时间后解决不了时还是应该向别人请教请教,“三人行必有我师”正是这个意思。

通过这次实验,我深刻地体会到了面向对象与面向过程的区别。程序在最初设计时由于面向对象的思想不够成熟,导致在最后想要增加一些新的功能时,出现了一些麻烦,

6.参考文献

Vsiual C#.NET程序设计教案 清华大学出版社。邱锦伦

附录A:主要源程序

1)snakeBlock类:

class snakeBlock//蛇块信息

{

private Color _color;//颜色

private int _size;//大小

private Point _point;//坐标

public snakeBlock(Color color, int size, Point p) {

this._color = color;

this._size = size;

this._point = p;

}

public Point Point//属性

{

get { return this._point; }

}

//绘制自身到画板

public virtual void Paint(Graphics g)

{

SolidBrush sb = new SolidBrush(_color); lock (g)

{

try

{

g.FillRectangle(sb,

this.Point.X * this._size, this.Point.Y * this._size, this._size - 1,

this._size - 1);

}

catch{ }

}

}

2)snakeMod类:

变量设置:

private int _width = 20; //宽度

private int _height = 20; //高度

public Color _bgColor; //背景色

private Graphics _gpPalette; //画布

private ArrayList _blocks; //蛇块列表 private Direction _direction ; //前进方向

public Timer timerBlock; //更新器

private int _level = 1; //游戏等级

public int le

{

get { return _level; }

set {_level = value; }

}

private snakeBlock _food; //当前食物

private int _size = 20; //单位大小

private bool _isGameOver;

private int[] _speed = new int[] { 800, 450, 400, 350,150,250,200,100, 50 };//游戏速度

构造函数:

public snakeMode(int width, int height, int size,Color bgColor, Graphics g, int lvl) // 构造函数

{

this._width = width;

this._height = height;

this._bgColor = bgColor;

this._gpPalette = g;

this._size = size;

//this.color = color;

this._level = lvl;

this._blocks = new ArrayList();

this._blocks.Insert(0, (new snakeBlock(Color.Blue, this._size, new Point(width / 2, height / 2))));

this._direction = Direction.Right;

}

Start()函数:

public void Start()

{

this._food = GetFood();//生成一个食物

timerBlock = new System.Timers.Timer(_speed[this._level]);

//timerBlock = new System.Timers.Timer(se);

timerBlock.Elapsed += new

System.Timers.ElapsedEventHandler(OnBlockTimedEvent);

timerBlock.AutoReset = true;

timerBlock.Start();

}

定时更新:

private void OnBlockTimedEvent(object source, ElapsedEventArgs e)//定时更新 {

this.Move();

if (this.CheckDead())

{

this.timerBlock.Stop(); //停止引发事件

this.timerBlock.Dispose(); //释放资源

int score=(this._blocks.Count-1)*10;

System.Windows.Forms.MessageBox.Show("score:" + score, "Game Over!");

}

}

检查游戏是否结束:

private bool CheckDead() //检查游戏是否结束

{

snakeBlock head = (snakeBlock)(this._blocks[0]);//超出画布范围 if (head.Point.X == 0 || head.Point.Y == 0 || head.Point.X == this._width +1|| head.Point.Y == this._height+1)

return true;

for (int i = 1; i < this._blocks.Count; i++)

{

snakeBlock b = (snakeBlock)this._blocks[i];//是否头尾相接 if (head.Point.X == b.Point.X && head.Point.Y == b.Point.Y) {

this._isGameOver = true;

return true;

}

}

this._isGameOver = false;

return false;

}

生成下一个食物,也就是下一节蛇:

private snakeBlock GetFood() // private snakeBlock GetFood() //生成下一个食物,也就是下一节蛇块

{

snakeBlock food = null;

Random r = new Random();

bool redo = false;

while (true)

{

redo = false;

int x = r.Next(this._width);

int y = r.Next(this._height);

for (int i = 0; i < this._blocks.Count; i++)

{

snakeBlock b = (snakeBlock)(this._blocks[i]);

if (b.Point.X == x && b.Point.Y == y)

{

redo = true;

}

if (redo == false)

{

food = new snakeBlock(Color.Gold, this._size, new Point(x, y));

break;

}

}

return food;

}

}

蛇体移动:

private void Move() //蛇体移动

{

Point p = new Point(); //下一个位置

snakeBlock head = (snakeBlock)(this._blocks[0]);

if (this._direction == Direction.Up)

p = new Point(head.Point.X, head.Point.Y - 1);

else if (this._direction == Direction.Down)

p = new Point(head.Point.X, head.Point.Y + 1);

else if (this._direction == Direction.Left)

p = new Point(head.Point.X - 1, head.Point.Y);

else

p = new Point(head.Point.X + 1, head.Point.Y); //生成新坐标,将成为蛇头

snakeBlock b = new snakeBlock(Color.Red, this._size, p);//如果下一个坐标不是当前食物坐标,那么从蛇块信息列表中删除最后一个蛇块

if (this._food.Point != p)

this._blocks.RemoveAt(this._blocks.Count - 1); //如果下一个坐标和食物坐标重合,那么就生成一个新食物

else

{

this._food = this.GetFood(); //把下一个坐标插入到蛇块信息列表的第一个,使其成为蛇头

fen += 10;

}

this._blocks.Insert(0, b); // 将元素插入指定索引处 this.PaintPalette(this._gpPalette);

}

更新画板:

public void PaintPalette(Graphics gp) //更新画板

{

gp.Clear(this._bgColor); //清除整个绘图面并以指定背景色填充

this._food.Paint(gp);

foreach (snakeBlock b in this._blocks)

{

b.Paint(gp);

}

}

方向设置:

enum Direction //枚举四个方向

{

Left,

Right,

Up,

Down

}

public Direction Direction

{

get { return this._direction; }

set { this._direction = value ;}

}

Form1类:

重新绘制:

private void pictureBox1_Paint(object sender, PaintEventArgs e)

{

if ( p!= null)

{

p.PaintPalette(e.Graphics);

}

}

键盘响应的重写函数::

protected override bool ProcessDialogKey(Keys keyData)

{

if (keyData == Keys.Up || keyData == Keys.Down ||

keyData == Keys.Left || keyData == Keys.Right)

return false;

else

return base.ProcessDialogKey(keyData);

}

键盘响应:

private void Form1_KeyDown(object sender, KeyEventArgs e)

{

if ((e.KeyCode == Keys.D || e.KeyCode == Keys.Right )&& p.Direction != Direction.Left)

{

p.Direction = Direction.Right;

textBox1.Text = p.fen.ToString();

return;

}

if ((e.KeyCode == Keys.W || e.KeyCode == Keys.Up) && p.Direction != Direction.Down)

{

p.Direction = Direction.Up;

textBox1.Text = p.fen.ToString();

return;

}

if ((e.KeyCode == Keys.S || e.KeyCode == Keys.Down) && p.Direction != Direction.Up)

{

p.Direction = Direction.Down;

textBox1.Text = p.fen.ToString();

return;

}

if ((e.KeyCode == Keys.A || e.KeyCode == Keys.Left) && p.Direction != Direction.Right)

{

p.Direction = Direction.Left;

textBox1.Text = p.fen.ToString();

return;

}

}

开始事件:

private void button1_Click(object sender, EventArgs e)

{

//定义画布长,宽,以及每个蛇块的大小

int width, height, size;

width = height = 20;

size = 15;

//设定游戏窗口的大小

this.pictureBox1.Width = width * size;

this.pictureBox1.Height = height * size;

this.Width = pictureBox1.Width + 300;

this.Height = pictureBox1.Height + 300;

//定义一个新画板(宽度,高度,单位大小,蛇体颜色,背景色,绘图句柄,游戏等级等)

p = new snakeMode(width, height, size, this.pictureBox1.BackColor, Graphics.FromHwnd(this.pictureBox1.Handle), 5);

//游戏开始

p.Start();

textBox1.Text = p.fen.ToString();

}

暂停/继续及退出事件:

private void button2_Click(object sender, EventArgs e)//暂停

{

p.timerBlock.Stop();

}

private void button3_Click(object sender, EventArgs e)// 退出

{

this.Close();

}

private void button4_Click(object sender, EventArgs e)// 继续

{

p.timerBlock.Start();

}

蛇的颜色设置:

private void 白色ToolStripMenuItem_Click(object sender, EventArgs e)

{

p.color =Color.White;//设置贪吃蛇身体的颜色

}

private void 绿色ToolStripMenuItem_Click(object sender, EventArgs e) {

p.color = new SolidBrush(Color.Blue);//设置贪吃蛇身体的颜色 }

private void 红色ToolStripMenuItem_Click(object sender, EventArgs e) {

p.color = new SolidBrush(Color.Red);//设置贪吃蛇身体的颜色 }

游戏等级设置:

private void 级ToolStripMenuItem_Click(object sender, EventArgs e) {

if ( m == false)//如果游戏没有开始

{

级ToolStripMenuItem.Checked = false;//设置该项没有被选中 级ToolStripMenuItem1.Checked = false;//设置该项没有被选中 级ToolStripMenuItem2.Checked = false;//设置该项没有被选中 ((ToolStripMenuItem)sender).Checked = true;//设置当前项被选中 switch

(Convert.ToInt32(((ToolStripMenuItem)sender).Tag.ToString()))

{

case 1:

{

_speed = 800;//设置移动的速度

break;

}

case 2:

{

_speed = 600;

break;

}

case 3:

{

_speed = 300;

break;

}

}

}

}

附录B:软件使用说明书

相关推荐