范文一:贪吃蛇游戏C#编程
基于 VC#.NET的 贪吃蛇游戏的 开发与设计
姓名:*锦超
学号:************ 班级:软件 ***班
指导教师:程铭
完成日期:2011年 6月 24日星期五
目录
一、 实验目的 ……………………………………………………………… 2
二、 实验任务与要求 …………………………………………………… 2 2.1实验内容……………………………………………………………… 2 2.2实验要求……………………………………………………………… 2 2.3实验环境……………………………………………………………… 2 三、 设计方案 ……………………………………………………………… 2 3.1程序功能……………………………………………………………… 3 3.2设计思想……………………………………………………………… 3 3.3设计总体流程图……………………………………………………… 4 3.4设计的具体实现……………………………………………………… 5 四、 程序测试 …………………………………………………………… 10 4.1测试内容与结果……………………………………………………… 10 4.2程序运行效果图……………………………………………………… 13 五、 实验总结 …………………………………………………………… 16参考文献 ……………………………………………………………………… 17附录 …………………………………………………………………………… 17附录 A :主要源程序 ………………………………………………………… 17 附录 B :软件使用说明书 …………………………………………………… 28
基于 VC#.NET的贪吃蛇游戏的开发与设计
一、 实验目的
1.1复习、 VC#.NET的基础知识 , 进一步加深对 VC#.NET技术的理解和掌 握;
1.2课程设计为学生提供了一个既动手又动脑 , 独立实践的机会 , 将课本 上的理论知识和实际有机的结合起来,锻炼学生的分析解决实际问 题 的能力。
1.3培养学生在项目开发中团队合作精神、创新意识及能力。
二、 实验任务与要求
2.1实验内容
贪吃蛇游戏开发, 首先它可以为大家提供一种当前比较流行的休闲 小游戏。贪吃蛇是家喻户晓的益智类小游戏,选择这个题目一是为了 将自己的所学知识加以运用,二是一直以来贪吃蛇这个游戏就深深地 吸引着很多人,它的制作方法对于很多同学而言都是很神秘的。所以 我们希望通过这学期所学知识把它剖析开来,真真正正的了解它的本 质和精髓。在这次学习中我们将从实践和实际的项目中提高自己的编 程能力。因此决定选择这个题目作为 VC#.NET的课程设计。
最后一部分就是游戏的模块划分, 根据分析, 贪吃蛇这个程序一共 要实现如下几个功能,包括游戏方面开始游戏、暂停游戏以及停止游 戏, 游戏设置 (蛇的颜色、 食物的颜色、 游戏难度设置) , 游戏帮助 (游 戏控制说明)与积分。
2.2实验要求
1)对系统进行功能模块分析、控制模块分析正确,符合课题要求,实 现相应功能;可以加以其他功能或修饰,使程序更加完善、合理; 2)系统设计要实用,采用模块化程序设计方法,编程简练、可用,功 能全面;
3)说明书、流程图要清楚;
4)记录设计情况(备查,也为编写设计说明书作好准备) ;
2.3实验环境
WindowsXP 操作系统, VS.NET2005开发环境。
三、 设计方案
3.1程序功能
1) 游戏选项:主要实现游戏的开始、暂停、退出 , 分别可以使用快捷键 F2、空格键、 F4。
2) 设置:
a) 游戏难度等级设置:初级、中级、高级三个难度等级,每个难度 等级又实现了三级加速, 主要包括一级加速、 二级加速、 三级加 速;
b) 蛇体颜色设置:绿色、红色、蓝色、黑色四种颜色可以选择;
c) 蛇体宽度设置:初始大小、放大两倍、放大三倍三种选择;
d) 食物颜色设置:橙色、黄色、紫色三种颜色可以选择。
3) 帮助:游戏操作说明, 可以使用快捷键 F3打开, 主要介绍游戏的操作 方法、以及版本信息、制作时间。
3.2设计思想
1) 贪吃蛇的游戏规则:蛇头不能反向移动,若反向则保持原来的运 动方向不变;不能碰到场地四周和自身,若碰到到四周墙壁或者 自身,则游戏结束;当吃到食物后,应在随机生成一个新的坐标 产生食物,并且食物不能再蛇身内生成;选择相应的游戏难度等 级,当分数累加到一定程度以后,速度增加,每个等级有分为三 个加速阶段。
2) 游戏场地的绘制:将 panel 控件设计为游戏的背景,场地、贪吃 蛇以及食物都是在 panel 控件中完成绘制。
3) 蛇身和食物的绘制:本实验主要使用 Graphics 类在 panel 控件中 绘制游戏的场地以及贪吃蛇、食物,食物模块、蛇身各模块的绘 制是在单元格中进行绘制的,这样蛇身移动和食物生成过程中不 需要重新绘制背景;根据蛇身模块宽度,可以改变蛇体的大小和 场地坐标图方格的大小;根据食物类和蛇类各自的颜色变量,再 通过菜单来改变其相应的颜色。
4) 游戏的动态刷新控制:使用 Timer 组件来实现贪吃蛇的移动,通 过设置该组件的 Interval 属性大小来控制移动的速度。
5) 蛇身移动的实现:蛇身的移动主要是用 ArrList 类来实现的,该 类的主要功能是使用大小可以根据需要动态增加数组,即建立动 态数组来存储蛇身, 本实验主要使用 ArrList 类的 Insert 方法和 RemoveAt 方法实现蛇模块的增加、蛇体移动的清除移动尾部。 3.3设计总体流程图
程序结构图:
程序流程图
3.4、设计的具体实现
1) 游戏界面设计 :主要是使用 panel1控件设计为游戏的背景,添加菜单项 目,积分显示 Label2,用于聚焦的 TextBox1控件,菜单控件 meuStrip1控件和 Timer1控件,如下图所示:
2) 创建一个 Snake 类 :
a) 在 Snake 类中定义全局变量
//设置蛇体模块大小
public static int SnakeBone =10;
//设置方向
public static int Direct=0;
//设置蛇体各模块的位置
public static Point [] Place={new Point (-1,-1),new
Point (-1,-1),new Point (-1,-1),new Point (-1,-1),new
Point (-1,-1)};
//是否有食物 , 有食物为 true
public static bool hasFood = false ;
//游戏是否结束 , 游戏结束为 true
publicstatic bool isGame = false ;
//场地的宽度、长度
public static int Field_with = 0;public static int Field_length= 0;
//记录绘制贪吃蛇的控件
public static Control control;
//记录 Time 组件
public static Timer timer;
//设置贪吃蛇身体颜色
public static SolidBrush SolidSnake = new SolidBrush (Color .Green); //设置背景颜色
public static SolidBrush SolidBack =new SolidBrush (Color .Green); //记录 label 控件
public static Label label;
//实例化一个 ArrayList 数组
public static ArrayList List = new ArrayList ();
Graphics g;//实例化 Graphics类
//实例化 Food 类
Food food = new Food ();
b) 定义贪吃蛇的起始位置和游戏场地初始化 GamePlaceSet()函数:主要实
现蛇的初始位置,以及游戏场地的初始化。
public void GamePlaceSet(Control con,int Bone)代码具体实现见附录
A 的 Snake 类中
c) 蛇的移动方法 SnakeMove()函数:主要实现蛇体的移动, 每移动一次需要
判定是否反向,如果反向则记录下来,原来移动方向不变,还需要判断
是否游戏结束,如果没有结束则重新绘制蛇体,吃东西,绘制食物。
public void SnakeMove(int N, Form1 control1)代码具体实现见附录 A
的 Snake 类中
d) 判断蛇是否反向方法 EstimateMove()函数:用于判断贪吃蛇的移动方向
是否向相反的方向移动,如果方向移动则返回 false 。
public bool EstimateMove(Point ES)代码具体实现见附录 A 的 Snake 类
中。
e) 判断游戏是否结束 GameEnd()函数:主要实现判断蛇体是否和自身、 四周
墙壁是否相撞,如果相撞则游戏结束。
public bool GameEnd(Point GE)代码具体实现见附录 A 的 Snake 类中。
f) 吃东西 Eatefood()函数:主要实现贪吃蛇吃东西,没吃到一个食物,分
数加 5, 当分数达到一定程度时候需要判断是否加速, 这里主要实现蛇的
三级加速,包括一级加速、二级加速、三级加速。
public void Eatefood(Form1control1) 代码具体实现见附录 A 的 Snake
类中
3) 创建一个 Food 类 :
a) 定义全局变量
//设置食物颜色
public static SolidBrush SolidFood = new
SolidBrush (Color .Orange);
//食物坐标的初始化
public static Point FoodPoint = new Point (-1, -1);
b) 随机生成坐标方法 RectPoint()函数:用 Random 类随机生成随机的纵坐 标和横坐标,用返回 Point 结构
public Point RectPoint(Control pon) 代码具体实现见附录 A 的 Food 类中。 c) 生成食物方法 Buildfood()函数:没有食物的时候需要生成食物, 生成食 物的位置不能和蛇体重叠,如果重叠则重新获取随机坐标。
public void Buildfood(bool hasFood, Control con) 代码具体实现见附录 A 的 Food 类中。
4) MainForm 窗体功能实现
a) 定义全局变量
//用于设置贪吃蛇的速度
public int speed=350;
//用于游戏是否开始
public static bool ifStart=false ;
//用于游戏是否暂停 , 游戏暂停为 true
public static bool pause =true ;
//实例化 Snake 类
Snake snake = new Snake ();
//实例化 Food 类
Food food = new Food ();
b) 绘制游戏场景单元格的绘制:主要实现 panel1控件中单元格绘制。 public void ProtractTable(Graphics G)代码具体实现见附录 A 的 MainForm 窗体的主要代码中。
c) 在 panel1控件里初始化游戏场地方法 panel1_Paint()函数:主要实 现 panel1控件中绘制蛇和食物,以及游戏的是否结束的判断。 private void panel1_Paint(object sender, PaintEventArgs e) 代 码具体实现见附录 A 的 MainForm 窗体的主要代码中。
d) 游戏开始、暂停、退出实现:选择开始,贪吃蛇在游戏场地运行;暂 停选项只在贪吃蛇移动可以使用, 其他情况下处于无效状态; 退出选
项只用当游戏处于非运行状态起作用,退出游戏并且添加了判断。
//开始
private void 开始 F2ToolStripMenuItem_Click(object sender, EventArgs e)
//暂停
private void 暂停 SpaceToolStripMenuItem_Click(object sender, EventArgs e)
//退出
private void 退 出 F4ToolStripMenuItem_Click(object sender, EventArgs e)
//控制游戏的开始、暂停、和退出
public void NoviceControl(int n)
代码具体实现见附录 A 的 MainForm 窗体的主要代码中。
e) 游戏等级实现:主要分为初级、中级、高级三个级别,分别设置相应 的刷新时间, 并且限制只能在游戏结束运行和开始没有开始游戏时可 以设置。
//初级
private void 初级 ToolStripMenuItem_Click_1(object sender, EventArgs e)
//中级
private void 中 级 ToolStripMenuItem_Click(object sender, EventArgs e)
//高级
private void 高 级 ToolStripMenuItem_Click(object sender, EventArgs e)
代码具体实现见附录 A 的 MainForm 窗体的主要代码中。
f) 游戏键盘操作游戏键盘操作:主要实现游戏的开始、暂停、退出 , 分 别可以使用快捷键 F2、空格键、 F4,游戏操作说明用 F4键打开;贪 吃蛇移动方向操作:右移按→键或 D 键, 左移按←键或 A 键, 上移按 ↑键或 W 键,下移按↓键或 S 键。
//键盘操作设置
private void Form1_KeyDown(object sender, KeyEventArgs e) 代 码具体实现见附录 A 的 MainForm 窗体的主要代码中。
g) 蛇体颜色和食物颜色设置实现:主要实现蛇的四种颜色设置和食物的 三种颜色设置, 并且限制只能在游戏结束运行和开始没有开始游戏时 可以设置。
// 蛇体颜色设置
private void 绿 色 ToolStripMenuItem_Click(object sender, EventArgs e)
private void 红 色 ToolStripMenuItem_Click(object sender, EventArgs e)
private void 蓝 色 ToolStripMenuItem_Click(object sender, EventArgs e)
private void 黑 色 ToolStripMenuItem_Click(object sender, EventArgs e)
//食物颜色设置
private void 橙 色 ToolStripMenuItem_Click(object sender, EventArgs e)
private void 黄 色 ToolStripMenuItem_Click(object sender, EventArgs e)
private void 紫 色 ToolStripMenuItem_Click(object sender, EventArgs e)
代码具体实现见附录 A 的 MainForm 窗体的主要代码中。
h) 蛇体宽度放大实现:主要实现蛇体宽度的大小蛇置, 同时相应放大单 元格的宽度, 并且限制只能在游戏结束运行和开始没有开始游戏时可 以设置。
private void 初始大小 ToolStripMenuItem_Click(object sender, EventArgs e)
private void 放大两倍 ToolStripMenuItem_Click(object sender, EventArgs e)
private void 放大三倍 ToolStripMenuItem_Click(object sender, EventArgs e)
代码具体实现见附录 A 的 MainForm 窗体的主要代码中。
i) 打开帮助 :主要是想打开游戏操作说明窗体, 并且限制只能在游戏结 束运行和开始没有开始游戏时可以设置。
private void 游戏操作说明 F3ToolStripMenuItem_Click_1(object sender, EventArgs e)
j) timer1_Tick的触 发 事件:主要 实现在 一 定的时 间间 隔内调用 SnakeMove()函数,实现蛇的动态移动。
private void timer1_Tick(object sender, EventArgs e) 代码具 体实现见附录 A 的 MainForm 窗体的主要代码中。
四、 程序测试
4.1、测试内容与结果
1) 贪吃吃东西的测试,测试贪吃蛇是否吃到食物时,显示累加分数。
出现的问题:出错提示存储蛇坐标的类型不匹配,积分始终为 0。 问题解决分析:的尾部添加蛇身的 List.Add(List.Count - 1) 修改为 List.Add(List[List.Count - 1]),前者存储的只是一个整型的数, 并非一个数组元素。
2) 贪吃蛇速度的控制设置测试, 测试蛇的速度控制是否合理, 是否便于 操作,如何实现蛇的加速移动功能。
出现的问题:蛇的速度过快, 难度级别秩序颠倒, 不能实现自动加速。 问题解决与分析:为了解决此问题,需要将蛇的速度统一化,只在 MainForm 窗体代码中之定义一个控制素的的变量,通过参数传递到 Snake 类中的吃东西 Eatefood(Form1control1) 函数中,在定义两个 中间变量 m,n ,分别记录传递过来的分数和速度
,代码具体实现如下:
int m,n; //记录分数和速度
if (((Point )List[0])==Food .FoodPoint) //如果蛇头吃到了食物
{
List.Add(List[List.Count - 1]);//在蛇的尾部添加蛇身
hasFood = false ; //没有食物
food.Buildfood(hasFood, control);//生成食物
m = Convert .ToInt32(label.Text);
if (m==10)//积分为 10时加速
{ n = control1.speed - 50;
timer.Interval = n;
label.Text = Convert .ToString(m + 5);//显示当前分数
g.DrawString(
}
else if (m == 20)//积分为 20时加速
{
g.FillRectangle(Snake .SolidBack, 0, 0, control.Width,
control.Height);
control1.ProtractTable(g);
g.DrawString(
n = control1.speed - 100;
timer.Interval = n;
label.Text = Convert .ToString(m + 5);//显示当前分数
}
else if (m == 30)//积分为 30时加速
{ g.FillRectangle(Snake .SolidBack, 0, 0, control.Width,
control.Height);
control1.ProtractTable(g);
g.DrawString(
FontStyle .Bold), new SolidBrush (Color .Red), new PointF (180, 30)); n = control1.speed - 150;
timer.Interval = n;
label.Text = Convert .ToString(m + 5);//显示当前分数
}
else label.Text = Convert .ToString(Convert .ToInt32(label.Text) + 5);
}
3) 贪吃蛇宽度放大测试,主要测试单元格划分是否合理。
出现的问题:在放大或者缩小蛇体宽度的时候,单元格明显无法均匀 划分,食物也无法对齐到单元格,导致蛇无法吃到。
问题解决与分析:首先调整窗体设计,使 panel1控件的宽高都是是蛇 体宽度的倍数,锁定窗体,其次将食物单独建立一个类 Food ,这样可 以实现每次调用食物生成函数 Buildfood () 函数就可以实现每次放大 或者缩小蛇宽度时候重新获取随机生成的食物坐标,不会在保留上次 随机生成的结果而造成的食物也无法对齐到单元格。
4) 食物的生成位置测试,主要测试食物随机生成的坐标是否和蛇体重 叠。
问题出现:总是出现随机生成的食物和蛇体重叠的现象,无法得到识 别,蛇头仍然可以顺利吃到食物。
问题解决与分析:主要循环语句判定条件逻辑上的问题,对食物重叠的 判断标志写在了 for 循环里边去了,导致无法识别食物与蛇体重叠,代 码修改如下:
Point tem_p = new Point (-1, -1);//定义坐标结构
bool tem_bool = false ; //计算出食物的位置位 true, 否则为 false
while (!tem_bool)//计算显示食物的位置
{ bool tem_b = false ; //用于确定生成的食物 是否和蛇体重叠
tem_p = RectPoint(con);//随机生成食物的位置
for (int i = 0; i < snake="" .list.count;="">
{//如果随机生成的食物和蛇体重叠
if ((Point ) Snake .List[i] == tem_p)
{ tem_b = true ; //记录重叠
break ; //重叠则停止 for 循环
}
}
if (tem_b == false ) //如果食物和蛇体没有重叠
{ tem_bool = true ; //计算出食物的位置位 true, 否则为 false
break ;
}
}
FoodPoint = tem_p;//记录食物的显示位置
5) 菜单控制操作的设置测试, 主要测试菜单是否使用灵活, 是否符合一 般游戏的要求,游戏在运行中,某些菜单是否还起作用。
问题的出现:暂停菜单选项,除了在游戏运行状态起作用,在其他状 态也起作用; 游戏设置选项和游戏操作说明选项在游戏进行中也起作 用。
问题分析与解决:游戏暂停函数设置限定条件,只允许暂停在蛇体移 动时候才可以使用; 在游戏设置选项和游戏操作说明选项的方法实现 过程函数中添加限制条件,只允许游戏非运行状态下可以使用,即游 戏界面刚启动和游戏结束状态,其他状态不可以使用。
4.2 、程序运行效果图
1) 启动游戏界面
2) 游戏运行
3) 游戏设置
a) 难度等级设置为中级
b) 蛇体颜色设置红色
c) 蛇体宽度设置放大三倍
d) 食物颜色设置紫色 运行结果如下图所示:
4) 帮助
游戏操作说明入下图所示
5) 游戏结束
蛇头撞墙游戏结束如下图所示:
蛇体相撞自身游戏结束如下图所示:
6) 退出游戏关闭
五、 实验总结
通过这次课程设计, 我对这学期所学的 VC#.NET语言程序设计
有一个更深刻的了解,将所学的知识应用于实践,由于所学知识有 限,为了使游戏能够能够实现课设的要求,我通过上网找代码和去 图书馆借书找程序,比较画好的流程图及功能模块,不断阅读修改 代码使程序达到预期所要实现的目标,完成课程设计后,可以感觉 到自己对 VC#.NET程序的又有了新的认识。 综合运用以前所学习的知 识, 设计一个 VC#.NET贪吃蛇游戏, 并能实现以下的功能:游戏的选项 主要是游戏的开始、暂停、退出,游戏的设置游戏难度等级的控制、蛇 的颜色、食物的颜色,扩展添加了一个蛇体宽度的设置,游戏帮助(游 戏控制说明)与积分显示。 总之,通过本次的课程设计,使我平时学到 的理论知识得到了很好地升华,理论与实际得到一次很好的结合,为我 今后参加工作打下了坚实的基础,使我受益匪浅。
参考文献
【 1】 《 C#高级编程(第 3版) 》清华大学出版社
【 2】 《 Visual C# 开发典型模块大全》人民邮电出版社出版社
附录
附录 A :主要源程序
1) 创建一个 Snake 类的代码
class Snake
{//在 snake 类中第一全局变量
public static int SnakeBone =10;//设置蛇体模块大小
public static int Direct=0;//设置方向
public static Point [] Place={new Point (-1,-1),new Point (-1,-1),new Point (-1,-1),new Point (-1,-1),new Point (-1,-1)};//设置蛇体各模块的位置 public static bool hasFood = false ; //是否有食物 , 有食物为 true
public static bool isGame = false ; //游戏是否结束 , 游戏结束为 true
public static int Field_with = 0; //场地的宽度
public static int Field_length= 0;//场地的长度
public static Control control; //记录绘制贪吃蛇的控件
public static Timer timer; //记录 Time 组件
//设置贪吃蛇身体颜色
public static SolidBrush SolidSnake = new SolidBrush (Color .Green); public static SolidBrush SolidBack =new SolidBrush (Color .Green);
public static Label label;//记录 label 控件
public static ArrayList List = new ArrayList ();//实例化一个 ArrayList 数组 Graphics g;//实例化 Graphics类
Food food = new Food ();
//定义一个函数用于绘制贪吃蛇的起始位置,以及对游戏场地进行初始化设置 public void GamePlaceSet(Control con,int Bone)
{ Field_with=con.Width;//获取场地的宽度
Field_length=con.Height;//获取场地的长度
SnakeBone=Bone;//记录蛇体模块大小
control=con;//记录背景控件
g=control.CreateGraphics();//创建背景控件的 Graphics 类
SolidBack=new SolidBrush (con.BackColor);//设置画刷颜色
for (int i=0;i<>
{//设置蛇初始位置的横坐标
Place[i].X = (Place.Length - i - 1) * SnakeBone;
//设置蛇初始位置的纵坐标 //temp.Y
Place[i].Y = (Field_length / 2) - SnakeBone;
g.FillRectangle(SolidSnake, Place[i].X+1, Place[i].Y+1 , SnakeBone-1 , SnakeBone-1); //绘制蛇体
}
List=new ArrayList (Place);//记录每一个模块的位置
isGame=false ; //停止游戏
Direct=0;//设置方向向右
}
//定义一个 SnakeMove 函数用于蛇体移动 , 根据蛇的位置, 判断是否吃到食物, 如果 吃到食物,重新生成食物
public void SnakeMove(int N, Form1 control1)
{ Point tem_point=new Point (-1,-1);//定义坐标结构
switch (N)
{ case 0:
{ tem_point.X=((Point )List[0]).X+SnakeBone;//蛇头右移动 tem_point.Y=((Point )List[0]).Y;
break ;
}
case 1:
{ tem_point.X=((Point )List[0]).X-SnakeBone;//蛇头左移动 tem_point.Y=((Point )List[0]).Y;
break ;
}
case 2:
{ tem_point.X=((Point )List[0]).X;
tem_point.Y=((Point )List[0]).Y-SnakeBone;//蛇头上移动 break ;
}
case 3:
{ tem_point.X=((Point )List[0]).X;
tem_point.Y=((Point )List[0]).Y+SnakeBone;//蛇头下移动 break ;
}
}
if (!EstimateMove(tem_point)) //如果没有向相反的方向移动
{ Direct=N; //改变贪吃蛇的方向 if (!GameEnd(tem_point)) //如果游戏没有结束 { ProtractSnake(tem_point); //重新绘制蛇体
Eatefood(control1); //吃食物
}
//绘制食物
g.FillRectangle(Food .SolidFood, Food .FoodPoint.X + 1,
Food .FoodPoint.Y + 1, SnakeBone - 1, SnakeBone - 1);
}
}
//吃东西
public void Eatefood(Form1 control1)
{int m,n; //记录分数和速度
if (((Point )List[0])==Food .FoodPoint) //如果蛇头吃到了食物
{ List.Add(List[List.Count - 1]);//在蛇的尾部添加蛇身
hasFood = false ; //没有食物
food.Buildfood(hasFood, control);//生成食物
m = Convert .ToInt32(label.Text);
if (m==10)//积分为 10时加速
{ n = control1.speed - 50;
timer.Interval = n;
label.Text = Convert .ToString(m + 5);//显示当前分数
g.DrawString(
FontStyle .Bold), new SolidBrush (Color .Green), new PointF (180, 30));
}
else if (m == 20)//积分为 20时加速
{ g.FillRectangle(Snake .SolidBack, 0, 0, control.Width, control.Height); control1.ProtractTable(g);
g.DrawString(
n = control1.speed - 100;
timer.Interval = n;
label.Text = Convert .ToString(m + 5);//显示当前分数
}
else if (m == 30)//积分为 30时加速
{ g.FillRectangle(Snake .SolidBack, 0, 0, control.Width,
control.Height);
control1.ProtractTable(g);
g.DrawString(
FontStyle .Bold), new SolidBrush (Color .Red), new PointF (180, 30));
n = control1.speed - 150;
timer.Interval = n;
label.Text = Convert .ToString(m + 5);//显示当前分数
}
else //显示当前分数
label.Text = Convert .ToString(Convert .ToInt32(label.Text) + 5);
}
}
//用于判断当前游戏是否结束
public bool GameEnd(Point GE)
{ bool tem_e = false ; //用于判断游戏是否结束
bool tem_body = false ; //用于判断蛇身是否重叠
for (int i=1;i<>
{//如果重叠则返回 ture ,游戏结束
if (((Point )List[0])==((Point )List[i]))
{ tem_body=true ;
}
}
//判断蛇头是否超出游戏场地
if (GE.X<=-20||ge.x>=control.Width-1||GE.Y<=-20||ge.y>=control.Height-1 ||tem_body)
{ //游戏结束提示
g.DrawString(
isGame = true ; //游戏结束
timer.Stop();//停止计时器
tem_e=true ;
}
return tem_e;
}
//用于判断贪吃蛇的移动方向是否向相反的方向移动,如果方向移动则返回 false public bool EstimateMove(Point ES)
{ bool tem_d = false ; //记录蛇头是否向相反的方向移动
//如果蛇头向相反的方向移动,则返回 true
if (ES.X == ((Point )List[0]).X && ES.Y == ((Point )List[0]).Y)
tem_d = true ;
return tem_d;
}
//重新画蛇
public void ProtractSnake(Point Pr)
{ bool tem_bool = false ; //用于确定是否清除移动后的蛇体
List.Insert(0,Pr);//根据蛇头的移动方向设置蛇头的位置
Point tem_point = ((Point )List[List.Count - 1]);//记录蛇头的位置 List.RemoveAt(List.Count - 1);//移除蛇的尾部
for (int i = 0; i < list.count-1;i++="">
{
if (tem_point==((Point )List[i]))
{
tem_bool = true ;
}
}
if (!tem_bool)
{//清除贪吃蛇移动前的尾部
g.FillRectangle(SolidBack, tem_point.X + 1, tem_point.Y+1, SnakeBone-1, SnakeBone-1);
}
for (int i=0; i < list.count;i++="">
{ g.FillRectangle(SolidSnake, ((Point )List[i]).X + 1,
((Point )List[i]).Y+1, SnakeBone - 1, SnakeBone - 1);
}
}
}
2) 创建一个 Food 类的代码
class Food
{ //设置食物颜色
public static SolidBrush SolidFood = new SolidBrush (Color .Orange);
public static Point FoodPoint = new Point (-1, -1); //食物坐标的初始化 //生成食物函数
public void Buildfood(bool hasFood, Control con)
{ if (hasFood == false ) //如果没有食物
{ Point tem_p = new Point (-1, -1);//定义坐标结构
bool tem_bool = false ; //计算出食物的位置位 true, 否则为 false while (!tem_bool)//计算显示食物的位置
{ bool tem_b = false ; //用于确定生成的食物 是否和蛇体重叠
tem_p = RectPoint(con);//随机生成食物的位置
for (int i = 0; i < snake="" .list.count;="">
{ //如果随机生成的食物和蛇体重叠
if ((Point ) Snake .List[i] == tem_p) { tem_b = true ; //记录重叠
break ; //重叠则停止 for 循环
}
}
if (tem_b == false ) //如果食物和蛇体没有重叠
{ tem_bool = true ; //计算出食物的位置位 true, 否则为 false break ;
}
}
FoodPoint = tem_p;//记录食物的显示位置
}
hasFood = true ; //有食物
}
//用 Random 类随机生成随机的纵坐标和横坐标,用返回 Point 结构
public Point RectPoint(Control pon)
{
int tem_R = pon.Width / Snake .SnakeBone; //获取场地的行数
int tem_C = pon.Height / Snake .SnakeBone; //获取场地的列数
Random RandomA = new Random ();//实例化 Random 类
tem_R = RandomA.Next(tem_R);//生成横坐标
tem_C = RandomA.Next(tem_C);//生成纵坐标
Point tem_RC = new Point (tem_R * Snake .SnakeBone, tem_C *
Snake .SnakeBone); //生成随机点的显示位置
return tem_RC;
}
}
3) MainForm 窗体的主要代码
public partial class Form1 : Form
{ public int speed=350; //用于设置贪吃蛇的速度
public static bool ifStart=false ; //用于游戏是否开始
public static bool pause =true ; //用于游戏是否暂停 , 游戏暂停为 true Snake snake = new Snake ();//实例化 Snake 类
Food food = new Food ();
public Form1()
{ InitializeComponent();
}
/// ///在 panel1控件的重绘事件中,判断游戏是否开始,如果游戏没有开始, ///设置场地及贪吃蛇的初始化信息;否则,根据模块的位置绘制蛇体 /// //绘制游戏场景 public void ProtractTable(Graphics G) { //绘制单元格的纵向线 for (int i = 0; i <= panel1.width="" nake="" .snakebone;="">=> { G.DrawLine(new Pen (Color .LightGreen, 1), new Point (i * Snake .SnakeBone, 0), new Point (i * Snake .SnakeBone, panel1.Height)); } //绘制单元格的横向线 for (int i = 0; i <= panel1.height="" snake="" .snakebone;="">=> { G.DrawLine(new Pen (Color .LightGreen, 1), new Point (0, i * Snake .SnakeBone), new Point (panel1.Width, i * Snake .SnakeBone)); } } //在 panel1控件里初始化游戏场地 private void panel1_Paint(object sender, PaintEventArgs e) { Graphics G= panel1.CreateGraphics();//创建 panel1控件的 Graphics 类 ProtractTable(G); //绘制游戏场景 if (!ifStart) //如果没有开始游戏 { Snake .timer = timer1; Snake .label = label2; //初始化场地及贪吃蛇信息; snake.GamePlaceSet(panel1, Snake .SnakeBone); } else { for (int i = 0; i < snake=""> { e.Graphics.FillRectangle(Snake .SolidSnake, ((Point ) Snake .List[i]).X +1, ((Point ) Snake .List[i]).Y +1,Snake .SnakeBone-1, Snake .SnakeBone-1); } //绘制食物 e.Graphics.FillRectangle(Food .SolidFood, Food .FoodPoint.X+1, Food .FoodPoint.Y+1, Snake .SnakeBone-1, Snake .SnakeBone-1); if (Snake .isGame) //如果游戏结束 { e.Graphics.DrawString( PointF (100, 150)); } } } //初级 private void 初级 ToolStripMenuItem_Click_1(object sender, EventArgs e) {//在游戏没有开始和游戏结束时才可以选择设置 if ((ifStart == false ) || Snake .isGame) {初级 ToolStripMenuItem.Checked = false ; //设置初级项被选中 中级 ToolStripMenuItem.Checked = false ; //设置中级项被选中 高级 ToolStripMenuItem.Checked = false ; //设置高级项目被选中 ((ToolStripMenuItem )sender).Checked = true ; //设置当前项选中 speed = 350; textBox1.Focus();//获得焦点 } } //中级 private void 中级 ToolStripMenuItem_Click(object sender, EventArgs e) { //在游戏没有开始和游戏结束时才可以选择设置 if ((ifStart == false ) || Snake .isGame) {初级 ToolStripMenuItem.Checked = false ; //设置初级项被选中 中级 ToolStripMenuItem.Checked = false ; //设置中级项被选中 高级 ToolStripMenuItem.Checked = false ; //设置高级项目被选中 ((ToolStripMenuItem )sender).Checked = true ; //设置当前项选中 speed = 250;//中级 textBox1.Focus();//获得焦点 } } //高级 private void 高级 ToolStripMenuItem_Click(object sender, EventArgs e) {//在游戏没有开始和游戏结束时才可以选择设置 if ((ifStart == false ) || (Snake .isGame)) {初级 ToolStripMenuItem.Checked = false ; //设置初级项被选中 中级 ToolStripMenuItem.Checked = false ; //设置中级项被选中 高级 ToolStripMenuItem.Checked = false ; //设置高级项目被选中 ((ToolStripMenuItem )sender).Checked = true ; //设置当前项选中 speed = 200;//高级 textBox1.Focus();//获得焦点 } } //开始 private void 开始 F2ToolStripMenuItem_Click(object sender, EventArgs e) {//控制游戏的开始、暂停、和退出 ifStart = false ; Graphics G = panel1.CreateGraphics();//创建 panel1控件的 Graphics 类 //刷新游戏场地 G.FillRectangle(Snake .SolidBack, 0, 0, panel1.Width, panel1.Height); ProtractTable(G);//绘制游戏场地 ifStart = true ; //开始游戏 //初始化场地以及贪吃蛇信息 snake.GamePlaceSet(panel1, Snake .SnakeBone); timer1.Interval = speed;//设置贪吃蛇的移动速度 timer1.Start();//启动计时器 pause = false ; //游戏初始状态,暂停取消 label2.Text = food.Buildfood(Snake .hasFood, Snake .control); //生成食物 textBox1.Focus();//获得焦点,便于键盘操作 } //暂停 private void 暂停 SpaceToolStripMenuItem_Click(object sender, EventArgs e) { Graphics G = panel1.CreateGraphics();//创建 panel1控件的 Graphics 类 //如果游戏正在运行 if ((pause == false && ifStart == true ) && (!Snake .isGame)) { G.DrawString( new SolidBrush (Color .Yellow), new PointF (100, 150)); timer1.Stop();//停止当前计时器 pause = true ; //当前游戏暂停 } //如果游戏正在运行 else if (pause == true && ifStart == true && (!Snake .isGame)) { G.FillRectangle(Snake .SolidBack, 0, 0, panel1.Width, panel1.Height); ProtractTable(G); timer1.Start();//启动计时器 pause = false ; //开始游戏 } } //退出 private void 退出 F4ToolStripMenuItem_Click(object sender, EventArgs e) { if ((ifStart == false ) || (Snake .isGame)) //如果游戏正在运行 {if (DialogResult .Yes == MessageBox .Show( { timer1.Stop(); //停止计时器 Application .Exit(); //停止运行,退出游戏 } } else MessageBox .Show( } private void 游戏操作说明 F3ToolStripMenuItem_Click_1(object sender, EventArgs e) { if ((ifStart == false ) || (Snake .isGame)) //如果游戏正在运行 { help temp = new help ();//初始 一个 help实例 if (temp.ShowDialog(this ) == DialogResult .OK) temp.Dispose(); } } //控制游戏的开始、暂停、和退出 public void NoviceControl(int n) { Graphics G = panel1.CreateGraphics();//创建 panel1控件的 Graphics 类 switch (n) {case 1://开始游戏 {ifStart = false ; //刷新游戏场地 G.FillRectangle(Snake .SolidBack, 0, 0, panel1.Width, panel1.Height); ProtractTable(G);//绘制游戏场地 ifStart = true ; //开始游戏 //初始化场地以及贪吃蛇信息 snake.GamePlaceSet(panel1, Snake .SnakeBone); timer1.Interval = speed;//设置贪吃蛇的移动速度 timer1.Start();//启动计时器 pause = false ; //暂停游戏 food.Buildfood(Snake .hasFood, Snake .control); //生成食物 label2.Text = break ; } case 2://暂停游戏 {/如果游戏正在运行 if ((pause == false && ifStart == true ) && (!Snake .isGame)) / { G.DrawString( FontStyle .Bold), new SolidBrush (Color .Yellow), new PointF (100, 150)); timer1.Stop();//停止当前计时器 pause = true ; //当前游戏暂停 } //如果游戏正在运行 else if (pause == true && ifStart == true && (!Snake .isGame)) { G.FillRectangle(Snake .SolidBack, 0, 0, panel1.Width, panel1.Height); ProtractTable(G); timer1.Start();//启动计时器 pause = false ; //开始游戏 } break ; } case 3://退出游戏 { if ((ifStart == false ) || (Snake .isGame)) //如果游戏正在运行 { timer1.Stop(); //停止计时器 Application .Exit(); //停止运行, 退出游戏 } break ; } case 4://打开帮助信息 { if ((ifStart == false ) || (Snake .isGame)) //如果游戏正在运行 { help temp = new help ();//初始 一个 help实例 if (temp.ShowDialog(this ) == DialogResult .OK) temp.Dispose(); } break ; } } } //键盘操作设置 private void Form1_KeyDown(object sender, KeyEventArgs e) { int tem_p = -1;//记录控件键值 if (e.KeyCode == Keys .F2) //如果按 F2 tem_p = 1;//开始游戏 if (e.KeyCode == Keys .Space) //如果按 F3 tem_p = 2;//暂停游戏或者继续游戏 if (e.KeyCode == Keys .F4) //如果按 F4 tem_p = 3;//关闭游戏 if (e.KeyCode==Keys .F3) tem_p = 4;//打开帮助信息 if (tem_p != -1)//如果当前是操作标识 NoviceControl(tem_p); //控制游戏的暂停和关闭 if (ifStart == true &&pause==false ) //如果游戏启动,键盘方向键才起作用 { int tem_n = -1;//记录移动键值 if (e.KeyCode == Keys .Right || e.KeyCode == Keys .D) //按下右键向右移动 tem_n = 0; if (e.KeyCode == Keys .Left || e.KeyCode == Keys .A) //按下左键向左移动 tem_n = 1; if (e.KeyCode == Keys .Up || e.KeyCode == Keys .W) //按下上键向上移动 tem_n = 2; if (e.KeyCode == Keys .Down || e.KeyCode == Keys .S) //按下键向下移动 tem_n = 3; if (tem_n != -1 && tem_n != Snake .Direct) { if (Snake .isGame == false ) {//如果移动的方向不是相反方向 if (!((tem_n == 0 && Snake .Direct == 1 || tem_n == 1 && Snake .Direct == 0) || (tem_n == 2 && Snake .Direct == 3 || tem_n == 3 && Snake .Direct == 2))) { Snake .Direct = tem_n;//记录移动的方向 snake.SnakeMove(tem_n, this ); //移动贪吃蛇 } } } } } // 蛇体颜色设置 private void 绿色 ToolStripMenuItem_Click(object sender, EventArgs e) { if ((ifStart == false ) || (Snake .isGame)) {绿色 ToolStripMenuItem.Checked = false ; 红色 ToolStripMenuItem.Checked = false ; 蓝色 ToolStripMenuItem.Checked = false ; 黑色 ToolStripMenuItem.Checked = false ; ((ToolStripMenuItem )sender).Checked = true ; //设置当前项选中 Snake .SolidSnake = new SolidBrush (Color .Green); } } private void 红色 ToolStripMenuItem_Click(object sender, EventArgs e) { if ((ifStart == false ) || (Snake .isGame)) {绿色 ToolStripMenuItem.Checked = false ; 红色 ToolStripMenuItem.Checked = false ; 蓝色 ToolStripMenuItem.Checked = false ; 黑色 ToolStripMenuItem.Checked = false ; ( (ToolStripMenuItem )sender).Checked = true ; //设置当前项选中 Snake .SolidSnake = new SolidBrush (Color .Red); } } private void 蓝色 ToolStripMenuItem_Click(object sender, EventArgs e) { if ((ifStart == false ) || (Snake .isGame)) {绿色 ToolStripMenuItem.Checked = false ; 红色 ToolStripMenuItem.Checked = false ; 蓝色 ToolStripMenuItem.Checked = false ; 黑色 ToolStripMenuItem.Checked = false ; ((ToolStripMenuItem )sender).Checked = true ; //设置当前项选中 Snake .SolidSnake = new SolidBrush (Color .Blue); } } private void 黑色 ToolStripMenuItem_Click(object sender, EventArgs e) { if ((ifStart == false ) || (Snake .isGame)) {绿色 ToolStripMenuItem.Checked = false ; 红色 ToolStripMenuItem.Checked = false ; 蓝色 ToolStripMenuItem.Checked = false ; 黑色 ToolStripMenuItem.Checked = false ; ((ToolStripMenuItem )sender).Checked = true ; //设置当前项选中 Snake .SolidSnake = new SolidBrush (Color .Black); } } //蛇体模块宽度设置 private void 初始大小 ToolStripMenuItem_Click(object sender, EventArgs e) { if ((ifStart == false ) || (Snake .isGame)) { 初始大小 ToolStripMenuItem.Checked = false ; 放大两倍 ToolStripMenuItem.Checked = false ; 放大三倍 ToolStripMenuItem.Checked = false ; ((ToolStripMenuItem )sender).Checked = true ; //设置当前项选中 Snake .SnakeBone = 10; } } private void 放大两倍 ToolStripMenuItem_Click(object sender, EventArgs e) { if ((ifStart == false ) || (Snake .isGame)) {初始大小 ToolStripMenuItem.Checked = false ; 放大两倍 ToolStripMenuItem.Checked = false ; 放大三倍 ToolStripMenuItem.Checked = false ; ((ToolStripMenuItem )sender).Checked = true ; //设置当前项选中 Snake .SnakeBone = 20; } } private void 放大三倍 ToolStripMenuItem_Click(object sender, EventArgs e) { if ((ifStart == false ) || (Snake .isGame)) {初始大小 ToolStripMenuItem.Checked = false ; 放大两倍 ToolStripMenuItem.Checked = false ; 放大三倍 ToolStripMenuItem.Checked = false ; ((ToolStripMenuItem )sender).Checked = true ; //设置当前项选中 Snake .SnakeBone = 30; } } //食物颜色设置 private void 橙色 ToolStripMenuItem_Click(object sender, EventArgs e) { if ((ifStart == false ) || (Snake .isGame)) {橙色 ToolStripMenuItem.Checked = false ; 黄色 ToolStripMenuItem.Checked = false ; 紫色 ToolStripMenuItem.Checked = false ; ((ToolStripMenuItem )sender).Checked = true ; //设置当前项选中 Food .SolidFood = new SolidBrush (Color .Orange); } } private void 黄色 ToolStripMenuItem_Click(object sender, EventArgs e) { if ((ifStart == false ) || (Snake .isGame)) {橙色 ToolStripMenuItem.Checked = false ; 黄色 ToolStripMenuItem.Checked = false ; 紫色 ToolStripMenuItem.Checked = false ; ((ToolStripMenuItem )sender).Checked = true ; //设置当前项选中 Food .SolidFood = new SolidBrush (Color .Yellow); } } private void 紫色 ToolStripMenuItem_Click(object sender, EventArgs e) { if ((ifStart == false ) || (Snake .isGame)) {橙色 ToolStripMenuItem.Checked = false ; 黄色 ToolStripMenuItem.Checked = false ; 紫色 ToolStripMenuItem.Checked = false ; ((ToolStripMenuItem )sender).Checked = true ; //设置当前项选中 Food .SolidFood = new SolidBrush (Color .Purple); } } private void timer1_Tick(object sender, EventArgs e) //timer1_Tick的触发事件 { snake.SnakeMove(Snake .Direct, this ); //移动贪吃蛇 } } 附录 B :软件使用说明书 启动游戏界面,选择 “ 游戏选项 ” 菜单有以下操作: 1)开始:可以使用快捷键 F2; 2)暂停:可以选择快捷键 space(空格键 ) ; 3)退出:可以使用快捷键 F4; 选择 “ 游戏设置 ” 菜单有以下操作: 1)难度等级设置:初级、中级、高级; 2)蛇体颜色设置:绿色、红色、蓝色、黑色; 3)蛇体宽度设置:初始大小、放大两倍、放大两倍; 4)食物颜色设置:橙色、黄色、紫色; 选择 “ 帮助 ” 菜单有以下操作: 1)游戏操作说明:可以使用快捷键 F3,介绍游戏的使用说明; 贪吃蛇移动方向操作: 右移:按→键或 D 键 左移:按←键或 A 键 上移:按↑键或 W 键 下移:按↓键或 S 键 C语言实训三:贪吃蛇游戏设计 1(游戏主画面及规则 2(描述蛇及食物的数据结构 蛇身体由多节构成,每节有不同的位置(坐标),蛇的节数由吃到食物多少决定。蛇 的信息可以由下面结构体描述: struct Snake{//描述的信息 int x[200];//每节的X坐标 int y[200];//每节的Y坐标 int count;//蛇的节数 int direct;//蛇移动方向 int isliving; //蛇是否活着,活着――1,死――0 int color;//蛇颜色 } snake; #define FNUM 3; //定义在画面上同时出现的食物数 struct Food{//描述食物的信息 int x;//X坐标 int y;//Y坐标 int type;//食物的类型 int score;//吃到此类食物的得分 } food; //如在画面上要同时出现多个食物,则用food[FNUM]; 3. 画游戏空间的边界(围墙) #define WALL_TOP 50 //围墙各边界的坐标(单位:象素) #define WALL_BOTTOM 550 #define WALL_LEFT 50 #define WALL_RIGHT 600 void DrawWall(void) //画围墙 { setcolor(11); setlinestyle(SOLID_LINE,0,THICK_WIDTH);/*设置线型*/ for(i= WALL_LEFT-10;i<= wall_right;i+="10)">=> { rectangle(i, WALL_TOP-10, i+10, WALL_TOP); //上边 rectangle(i, WALL_BOTTOM,i+10, WALL_BOTTOM +10);//下边 } for(i= WALL_TOP-10;i<= wall_bottom;i+="10)">=> { rectangle(WALL_LEFT-10, i, WALL_LEFT, i+10); //左边 rectangle(WALL_RIGHT, i, WALL_ RIGHT+10, i+10);;//右边 } } 4. 画蛇 用一个循环画出蛇的每一节,并通过画蛇的速度来控制游戏速度,也是蛇运动的速度 void DrawSnake( int color)//画由color指定颜色的蛇 { setcolor(color); //设蛇颜色 circle( snake.x[0]+5, snake.y[0]+5, 5, 5); //画蛇头,形状不同于蛇身 for(int i=1; i rectangle(snake.x[i], snake.y[i], snake.x[i]+10, snake.y[i]-10);//画蛇的一节 delay(m_speed);//控制游戏速度 setcolor(SPACECOLOR);//用背景色擦除蛇的的最后一节, 即擦除蛇运动的辙(痕迹) int n = snake.count-1; rectangle(snake.x[n],snake.y[n],snake.x[n]+10,snake.y[n]-10); } 5. 判断蛇是否死亡 蛇在两种情况下死亡:一是头撞到墙上,二是头撞到了自已身体, 蛇长小于四节时, 不可能拐这来撞自已身体 int IsSnakeLiving()//返回0,表示蛇死,返回1,表示蛇还活 { int x= snake.x[0]; int y= snake.y[0]; //蛇头位置 if(x return 0; //蛇是否撞到墙壁 for(i=3; i { if(x == snake.x[i] && y==snake.y[i])//撞到时,蛇头的坐标与某节的坐标相同 return 0;// } } 6. 蛇运动 蛇按蛇信息体中的direct成员指示,向某方向运动一步(节)。 void SnakeMove() { switch(snake.direct)/蛇头运动 {//1,2,3,4表示右,左,上,下四个方向,通过这个判断来移动蛇头 case 1:snake.x[0]+=10;break; case 2: snake.x[0]-=10;break; case 3: snake.y[0]-=10;break; case 4: snake.y[0]+=10;break; } for(i=snake.node-1;i>0;i--)//蛇的每个环节往前移动一节,到达前一节原位置 { snake.x[i]=snake.x[i-1]; snake.y[i]=snake.y[i-1]; } } 7(产生一个新食物 随机产生一个新食物,位置是随机的,类型也是随机的,信息填写到一个食物结构体变量中,并给出食物的分数 struct Food NewFood () { struct Food newfd; int width = WALL_RIGHT , WALL_LEFT;//空间宽 int hight = WALL_BOTTOM , WALL_TOP;//空间高 newfd.x =rand()%width + WALL_LEFT+1; //随机生成食物的位置坐标(在墙内) newfd.y =rand()%hight + WALL_TOP; //调整食物位置,使在整格内, 这样才可以让蛇吃到 while(newfd.x%10!=0) newfd.x++; while(newfd.y%10!=0) newfd.y++; //本可以随机生成食物类型,但简化了,只生成一种 newfd.type=1; newfd.score=10; return newfd; } 8. 画一个食物 根据食物的类形,在屏幕上画不同的图形,来表示蛇要去吃的食物 void DrawFood( struct Food fd, int color) { //可以用switch(fd.type)来分别针对不同类食物,画不同的图 //为了简化,这里只画一种类型食物 setcolor(color); rectangle(food.x, food.y, food.x+10, food.y-10); } 9. 蛇是否吃到食物 判断蛇是否吃到食物的依据是蛇头移动到了食物位置(坐标相同),吃到了,蛇的节数增1,把食物分数加入得分,同时清除食物,随机生成另一新食物。 int CanEatFood() { if(snake.x[0]==food.x && snake.y[0]==food.y)//吃到食物 { DrawFood( food, SPACECOLOR);//用背景色把画面上的食物东西擦掉 Snake.count++; //蛇增长一节 snake.x[snake.count]= snake.x[snake.count-1];//新的一节与原未节重又能叠 snake.y[snake.count]= snake.y[snake.count-1];//下一次运动时才伸展开 m_score+= food.score; //游戏加分 , 在这里调用显示分数的函数,刷新得分 sound(100); delay(4000); nosound();//发音庆祝一下 food=NewFood ();//生成新食物 } , 10. 游戏控制过程 游戏开始时,生成食物、初始化蛇(两节),画食物,画蛇。当玩家没有按箭头键时,蛇按游戏设定的速度,沿确定的方向自行运动,玩家按箭头键时,蛇将改变方向,没新的 方向运动。 void RunGame(void) { char key; randomize();//初始化随机数发生器 food = NewFood ();//产生食物数据信息 DrawFood(food);//在屏幕上画食物 //初始化蛇信息,两节 snake.isliving=1; //活着 snake.direct=1; //方向往右 snake.x[0]=100;snake.y[0]=100; //蛇头 snake.x[1]=110;snake.y[1]=100;//一节蛇身 snake.count=2;/*节数*/ m_score = 0;//初始分为0 ,在这里调用显示分数的函数,刷新得分 while(1)// 游戏重复继续, 按ESC键结束当前一回合 { while(!kbhit()) //在没有按键的情况下,蛇自己移动身体 { SnakeMove();//蛇自行移动 DrawFood(food, 10);//画上食物 if (! IsSnakeLiving())//如果蛇已死,退出本回合 return; CanEatFood();//判断能否吃到食物,吃到就加分,生成新食物,并显示 DrawSnake(GREEN);//画绿色的蛇 } char key =bioskey(0);//接收玩家按键*/ if(key==ESC)//按ESC键退出 return; else if(key==UPKEY&&snake.direct!=4)//判断是否反向移动,不许反向移动 snake.direct=3;//改变方向,向上 else if(key==RIGHTKEY&&snake.direct!=2) snake.direct=1; else if(key==LEFTKEY&&snake.direct!=1) snake.direct=2; else if(key==DOWNKEY&&snake.direct!=3) snake.direct=4; } } 11. 显示分数 功能:在屏幕规定位置显示游戏分数 void ShowScore() { char str[10]; //setfillstyle(SOLID_FILL,YELLOW); //bar(50,15,220,35); setcolor(8); settextstyle(0,0,2); sprintf(str,"积分:%d",score);//生成分数字符串 outtextxy(55,20,str);//在规定的坐标处显示分数 } 12(系统运行环准备 使用绘图函数前,必须设置显示模式为图形模式,如下: void Initialize(void)// { int GraphDriver , GraphMode ; /* 用于读x和y方向纵横比*/ GraphDriver = DETECT; /* 自动检测显示器*/ initgraph( &GraphDriver, &GraphMode, "" ); cleardevice();//清屏 } 用close graph()关闭图形模式 12(宏定义及全局变量 在文件头部定义程序中多次使用的宏,以便使程序有较好的可读性及参数调束性。 #define LEFTKEY 0x4b00 //定义各方向键 #define RIGHTKEY 0x4d00 #define DOWNKEY 0x5000 #define UPKEY 0x4800 #define ESC 0x011b #define SPACECOLOR 0xeeeeff //游戏空间颜色 int m_score=0; //游戏得分 int m_speed=100000;//游戏速度 13(主函数main的设计 int main() { initialize();/* 设置系统进入图形模式 */ 欢迎页面 读入游戏等级,根据用户输入确定speed的值 while(1) { cleardevice();//清屏 自行设计游戏画面,显示相关提示信息 初始化分数 DrawWall(); 等待玩家按S键开始 RunGame(); 询问玩家是否再玩一局,不玩就return退出 消除游戏空间 } 欢迎下次再玩~ closegraph();/*系统关闭图形模式*/ return(0); /*结束程序*/ } 附: 图形模式下的基本图形处理函数 1(图形初始化函数 显示模式控制函数initgraph 函数原型:void far initgraph(int far *graphdriver, int far *graphmode, char far *path)函数的功能,把图形适配器设置为一种图形模式,将选择的图形驱动程序(在BGI文件中)装入到内存。 检测显示器硬件函数detectgraph 函数原型:void far detectgraph(int far *graphdriver,int far *);函数功能:完成对适配器的检查,获取显示器类型号及相应的显示模式,把显示类型号赋予graphdriver,显示模式赋予graphmode。graphdriver和graphmode的意义与函数initgraph相同。 清屏函数cleardevice 函数原型:void far cleardevice(void); 函数功能:清除屏幕上的其它信息,函数作用范围为整个屏幕。 恢复显示方式closegraph 函数原型: void far closegraph(void);函数功能:当图形处理工作结束后,关闭图形系统,返回文本工作方式 恢复工作模式函数restorecrtmode 函数原型: void far restorecrtmode(void);函数功能:恢复屏幕显示模式为调用initgraph 前的设置设置图形工作模式函数setgraphmode 函数原型: void far setgraphmode(void);函数功能:将系统设置成图形模式并清屏。注意: 以上两个函数常交互使用,可使显示器工作方式在图形和文本方式之间来回切换,这在编制菜单程序和说明程序时很有用处。 2、基本图形处理函数 画点函数原型: void far putpixel(int x,int y,int color); /*画象素点函数*/ 函数putpixel在点(x,y)处画一个颜色为color的象素点。 画线函数原型: void far line(int startx,int starty,int endx,int endy); /*画线函数*/ 函数line在(startx, starty)和(endx,int endy)两点之间画一条直线,颜色为系统当前颜色。 画多边形函数原型如下: void far drawpoly(int numpoints,int far *points); 函数功能:用当前颜色画一个顶点数为numpoints的多边形,各个顶点坐标(x,y)连续存放 在points指向的整型数组中。 画矩形函数原型如下: void far rectangle(int left, int top, int right, int bottom);函数功能:用当前颜色画一个以(left, top)为左上角坐标,以(right, bottom)为右下角坐标的矩形。 画条形图函数有两个,其原型分别是: void far bar(int left,int top, int right, int bottom); /*二维实心条形图*/ void far bar3d(int left, int top, int right, int bottom, int depth, int topflag); /*三维条形图*/ 函数bar是用当前颜色,画一个以(left, top)为左上角坐标,以(right, bottom)为右下角坐标的矩形(同rectangle函数)。矩形填充模式及颜色可以设定,形成一个没有边框的实心条形图。若没有设定填充模式及颜色,则是用缺省模式。 函数bar3d用当前颜色,画一个厚度为depth个像素点三维条形图 画圆函数原型: void far circle(int x,int y,int radius);函数功能:用当前颜色画一个以(x, y)为圆心, radius为半径的画一个圆。 画圆弧函数原型: void far arc(int x,int y,int start,int end,int radius);函数功能:用当前颜色,以x,y为圆心,以start、end为起止角,以radius为半径画圆弧。 画扇形函数原型: void far pieslice(int x,int y,int start,int end,int radius);函数功能:用当前颜色,以x,y为圆心,以start、end为起止角,以radius为半径画扇形。 画椭圆函数原型: void far ellipse(int x,int y,int start,int end,int xradius,int yradius);函数功能:用当前画线的颜色,以x,y为圆心,以xradius、yradius为两轴半径,以start、end为起止角画椭圆。 前景颜色设置函数setcolor void far setcolor(int color); 函数的功能:用颜色值color设置当前画图颜色。 背景颜色设置函数setbkcolor void far setbkcolor(int color); 函数的功能:用颜色值color设置背景颜色。颜色值color即可用值表示,也可用大写的宏名表示。 填色函数setfillstyle void far setfillstyle(int pattern,int color); 函数的功能:函数setfillstyle将用设定的color颜色和pattern图模式对后面画出的轮廓图进行填充 封闭图形的填充函数floodfill 函数原型:void far floodfill(int x,int y,int bordercolor); 函数功能:根据封闭图形之中的任意一点(x,y),对任意封闭图形进行填充。 3、图形模式下字符输出函数 当前位置文本输出函数 函数的原型:void far outtext(char *str); 函数的功能:在当前位置输出由str指定的字符串,使用outtext函数的主要优点是它能用不同的字型、大小,用不同的方向输出文字。 注意:在图形方式下,没有可见光标。但在屏幕上还是存在着当前显示位置,就好像有一个可见光标一样。 字符输出定位函数 函数的原型:void far outtextxy(int x,int y,char *str); 函数的功能:将str指定的字符串输出到窗口指定坐标位置(x,y),如果x或y或两者均超过了窗口边界,则不会显示。 改变字型、大小和方向的函数 函数原型: void far settextstyle(int font,int direction,int charsize); 函数的功能:设置输出文本字体、大小和方向的函数 基于 DSP 的贪吃蛇游戏设计 班 级: 13级 7班 学 号:13006220730 姓 名:梁 检 满 一、实验目的 1.熟练掌握 C6713 的中断结构和对中断的处理过程。 2.熟练掌握 C6713 定时器的控制和使用方法。 3.熟练掌握键盘的使用原理及编程方法。 4.熟练掌握使用 C6713DSP 的扩展空间控制外围设备信息的方法;掌握蜂鸣器发声原理 和音乐发生方法;掌握液晶显示器的显示控制原理及编程方法。 5.掌握 C6713的系统自启动设计方法。 6.熟练掌握 C 语言开发 DSP 程序的流程及调试方法。 二、实验设备 计算机, ICETEK-C6713-EDU 实验箱,示波器。 三、实验内容 3.1贪吃蛇概述 “贪吃蛇”是一个产生于 1970年代中后期的计算机游戏,也叫贪吃蛇。在游戏中,玩家 操控一条细长的蛇爬行于一个带边界的平面之上,一路拾起碰到之事物或其他类似的物件, 并要避免触到自身或者包围着游戏区的“墙” 。每次贪吃蛇吃到一件食物,它的身体便增长一 些,这让游戏的难度逐渐变大。操控贪吃蛇时,玩家操控贪吃蛇头部的朝向(向上、向下、 向左或向右)控制贪吃蛇的进行方向,贪吃蛇的身体将跟着其头部移动。另外,玩家不可在 游戏中途停止贪吃蛇的进行。随着贪吃蛇的成功移植到手机系统,贪吃蛇的操作按键要求与 实际按键的完美匹配,使贪吃蛇游戏风靡起来。 玩家控制贪吃蛇在屏幕上移动,角色只能向左、右方向 90度转弯,一旦碰到墙壁或身任 何部位就失败。玩家通过吃随机出现的食物来获取分数,同时贪食蛇的长度也会随着增加。 吃的越多,获得的分数越高,同时蛇身也越长。 3.2贪吃蛇设计原理 本次课程设计是基于 ICETEK-C6713-A 评估板的贪吃蛇设计。 设计关键在于表示蛇的图形及蛇的移动。用一个小矩形快表示蛇的一节身体,身体每长 一节,增加一个矩形块,蛇头用一节表示。移动时必须从蛇头开始,所以蛇不能向相反的方 向移动,如果不按任意键,蛇自行在当前方向上前移,但按下有效方向键后,蛇头朝着该方 向移动,一步移动一节身体,所以按下有效方向键后,先确定蛇头的位置,而后蛇的身体随 蛇头移动,图形的实现是从蛇头新位置开始画出蛇,这时,由于未清屏的原因,原来的蛇的 位置和新蛇的位置差一个单位,所以看起来蛇多一节身体,所以将蛇的最后一节用背景色覆 盖。食物的出现与消失也是画矩形块和覆盖矩形块。为了便于理解,定义两个结构体:食物 与蛇。 3.2.1 贪吃蛇的数据结构位数组 #include #define BITMASK(b) (128 < ((b)="" %=""> #define BITSLOT(b) ((b) / CHAR_BIT) #define BITSET(a, b) ((a)[BITSLOT(b)] |= BITMASK(b)) #define BITCLEAR(a, b) ((a)[BITSLOT(b)] &= ~BITMASK(b)) #define BITTEST(a, b) ((a)[BITSLOT(b)] & BITMASK(b)) #define BITNSLOTS(nb) ((nb + CHAR_BIT - 1) / CHAR_BIT) //nb从 1开始, nb-1从 0开始,符合一个字节里的 8位在一个 CHAR_BIT里的要求。 + CHAR_BIT 是为了符合 C 的数组第一个元素都是从 0开始的要求。 3.2.2食物的随机产生 void food() { srand((unsigned) time(0)); SnakeFoodPosition=rand()%(64*128-1); while (BITTEST(Ground.bitarray, SnakeFoodPosition)) { SnakeFoodPosition+=SnakeLong; SnakeFoodPosition=SnakeFoodPosition%(64*128-1); } BITSET(Ground.bitarray,SnakeFoodPosition); draw(BITSLOT(SnakeFoodPosition)); } 3.3 中断原理 外中断区别于计时器等片内设备中断,它来源于 DSP 片外, 属于硬件中断。 外中断信号 通过 DSP 器件封装上的专用管脚输入 DSP ,属于可屏蔽中断。 TMS3206713DSP 有三个外中 断:EXT_INT4~EXT_INT7,如果 CPU 允许,这四个信号线上的低脉冲信号会中断 CPU 。 3.3.1 ICETEK-C6713-A 板的外中断 在外中断 EXT_INT4~EXT_INT7 中 ICETEK-C6713-A 板占用了 EXT_INT4, 其他中断引 脚被引到扩展插座 P4 上供用户扩展、使用。 3.3.2 程序编制 由一个不含中断处理程序的工程通过改写加入中断处理程序部分大致需要如下操作 (假设 使用 EXT_INT5): (1)编制中断服务程序:可以用 C 语言程序实现 (参见实验程序 ) ,编写单独的一个函数 xint5,此函数使用 interrupt 修饰,没有参数和返回值。 (2)构造中断向量表:可以用汇编语言构造,编写一个汇编语言模块程序 vector.asm ,在程 序头上声明段名为“ .vectors ”;定义外部标号 _xint5,由于汇编语言要使用 C 语言程序中定义 的标号 xint5,需要在这个标号前加底线;在中断向量表该中断对应位置写汇编语言语句跳转 语句。 (3)修改连接命令文件:在 MEMORY 小节中开辟单独的地址段用以存放中断向量表;在 SECTIONS 小节中指定 .vectors 段到前步开设的内存段中。 (4)主程序中进行初始化设置:CSR 寄存器的使能位、 ISTP 寄存器中、 IML 和 IMH 、 IER 和 IFR 中相应位。 3.3.3 中断函数程序 void InitInterrupt(void) { // 设置中断控制寄存器 CSR&=0xfffffffe; // 关中断 GIE=0 ISTP=0x00000c00; // 重置中断向量表到 0C00h IMH=0x0; // 指定 xint5中断 IML=0x0a0; ICR=0xff; // 清除等待的中断 IER=0x23; // 使能 xint5中断 CSR=CSR|1; // 开中断 } void interrupt XINT5() //中断响应函数 { int key; key=Getkey(); switch(key) { case SCANCODE_2: SnakeDirection=down; break; case SCANCODE_8: SnakeDirection=up; break; case SCANCODE_4: SnakeDirection=left; break; case SCANCODE_6: SnakeDirection=right; break; } } 3.4 液晶显示器显示原理 3.4.1 液晶显示器显示控制 ICETEK-C6713-A 是一块以 TMS320C6713DSP 为核心的 DSP 扩展评估板,它通过扩展接口 与实验箱的显示 /控制模块连接,可以控制其各种外围设备。液晶显示模块的访问、控制是由 C6713DSP 对 CE1 空间的特定地址的操作完成。 LCD 控 制 寻 址 :命 令 发 送 的 地 址 为 0901f0002H , 数 据 发 送 的 地 址 为 0901f0006H 和 0901f0008H ,辅助控制的地址为 0901f0004H 。 显示控制方法: -液晶显示模块中有两片显示缓冲存储器,分别对应屏幕显示的象素,向其中写入数值将 改变显示,写入“ 1”则显示一点,写入“ 0”则不显示。其地址与象素的对应方式如下: -发送控制命令:向液晶显示模块发送控制命令的方法是通过向命令控制地址写入命令控 制字,然后再向辅助控制地址写入 0。由于液晶模块相对于 DSP 来讲是慢速设备, 在命令之间 可能需要增加延时语句,或将 CE1 空间的 EMIF 访问控制寄存器设置的访问延时加大。 -写显示数据:在使用命令控制字选择操作位置 (页数、列数 ) 之后,可以将待显示的数据 写入液晶显示模块的缓存。 将数据发送到相应数据控制地址即可。 由于液晶模块相对于 DSP 来 讲是慢速设备,在命令之间可能需要增加延时语句,或将 CE1 空间的 EMIF 访问控制寄存器 设置的访问延时加大。 3.4.2 液晶显示程序 void draw(int index) //液晶显示程序 { int page; int row; page=index%8; row=index/8; LCDCMD(7-page+0x0b8); // 设置操作页 if (row<> { LCDCMD(row+LCDCMDVERADDRESS); // 起始列 LCDWriteLeft(Ground.LCDarray[index]); } else { LCDCMD(row-64+LCDCMDVERADDRESS); // 起始列 LCDWriteRight(Ground.LCDarray[index]); } } 3.5 定时器原理 3.5.1 通用定时器介绍及其控制方法 C6000 系列 DSP 在片内集成了 32 位的通用定时器,可以实现: *事件计数 *事件定时 *产生脉冲信号 *产生 CPU 中断信号 *产生 DMA 同步事件 定时器的控制寄存器: Timer0 Timer1 寄存器名 功能 01940000 01980000 定时器控制寄存器 CTL 设置定时器的工作模式 监视定时器状态 设置 TOUT 管脚功能 01940004 01980004 定时器周期寄存器 PRD 设置定时器的计数周期 决定 TSTAT 信号的频率 01940008 01980008 定时器计数寄存器 CNT 定时器当前计数值 C6000 的定时器执行的是加计数,真正的计数工作在 CNT 寄存器中进行,当计数寄存器 中的值达到 PRD 寄存器的值时,自动重新清 0。 3.5.2中断控制 中断是为使 CPU 具有对外界异步事件的处理能力而设置的。通常 DSP 工作在包含多个 外界异步事件环境中,当这些事件发生时, DSP 应及时执行这些事件所要求的任务。中断就 是要求 CPU 暂停当前的工作,转而去处理这些事件,处理完成后,再回到原来被中断的地方 继续原来的工作。显然,服务一个中断包括保存当前处理现场,完成中断服务,恢复各寄存 器和现场,然后返回继续执行被暂时中断的程序。请求 CPU 中断的请求源称为中断源。这些 中断源可以是片内的,如定时器等,也可以是片外的,如 A/D 转换及其他片外装置。片外中 断请求连接到芯片的中断管脚,并且在这些管脚处的电平上升沿产生。如果这个中断被使能, 则 CPU 开始处理这个中断,将当前程序流程转向中断服务程序。当几个中断源同时向 CPU 请 求中断时, CPU 会根据中断源的优先级别, 优先响应级别最高的中断请求。 TMS320C6000 有 11 个寄存器管理中断服务: *控制状态寄存器 CSR 控制全局使能或禁止中断 *中断使能寄存器 IER 使能或禁止中断处理 *中断标志寄存器 IFR 指示有中断请求但未被响应的中断发生 *中断设置寄存器 ISR 手动设置 IFR 中的标志位 *中断清除寄存器 ICR 手动清除 IFR 中的标志位 *中断服务表指针 ISTP 指向中断服务表的起始地址 *不可屏蔽中断返回指针 NRP 包含从不可屏蔽中断返回的地址,该中断返回通过 B NRP 指令完成 *可屏蔽中断返回地址 IRP 可屏蔽中断的返回地址 *中断选择寄存器 IML 可选择 CPU 中断 10-15 号对应的中断源 *中断选择寄存器 IMH 可选择 CPU 中断 4-9 号对应的中断源 *外中断极性选择寄存器 EIP 选择外中断 (INT4-INT7)的触发极性 3.5.3 中断响应过程 外设事件要引起 CPU 中断, 必须保证:CSR 使能中断, IER 相应位被使能 (置 1) , ST1 寄存器中的 INTM 使能(置 0),中断服务表相应位置放置服务程序入口地址转移指令,相应 中断源放入 IML 或 IMH 适当位置。当 CPU 响应中断时, PC 指针指向中断向量表中对应中断 的地址,进入中断服务子程序。中断向量表是 DSP 存放中断服务程序的一段内存区域,大小 为 80H 。在中断向量表中,每一个中断占用 32 个字的空间,一般情况是将一条跳转或延时跳 转指令存放于此。中断向量表的位置是可以改变的,修改 ISTP 寄存器中的中断向量表基地址 可以实现这一点。 3.5.4 中断程序设 计 -程序中应包含中断向量表。 -向量表中每项为 32 个字,存放跳转指令,跳转指令中的地址为相应服务程序入口地址。 第一个向量表的首项为复位向量,即 CPU 复位操作完成后自动进入执行的程序入口。 -程序中包含相应的中断服务程序,应将其入口地址加入相应中断向量表中。 -在程序中重定位中断向量表到本程序的中断向量表地址;设置中断源 (IML,IMH)、中断 屏蔽寄存器 IER __· 3.6 键盘操作原理 ICETEK-C6713-A 是一块以 TMS320C6713DSP 为核心的 DSP 扩展评估板,它通过扩展 接口与实验箱的显示 /控制模块连接,可以控制其各种外围设备,也可以接收外设发送的各种 数据、信息。键盘的扫描码由 DSP 的扩展地址 0901f0002H 给出,当有键盘输入时,读此端 口得到扫描码,当无键被按下时读此端口的结果为 0。读取的方法如下: nScanCode=*((unsigned short int *)0901f0002); // nScanCode 中为扫描码 nnn=*((unsigned short int *)0901f0004); // 读操作清除键盘缓冲区 ICETEK-C6713-EDU 实 验 箱 对 TMS320C6713 DSP 存 储 空 间 的 使 用 地 址 范 围 901F0000h — 901FFFFFh 数据宽度:16 位 901F0000h : ICETEK-CTR 全局控制寄存器写地址 901F0002h :读取键盘扫描码地址,液晶命令写地址 901F0004h :清除键盘缓冲区读地址,液晶控制写地址 901F0006h :液晶左侧显示数据写地址 901F0008h :液晶右侧显示数据写地址 901F000Ah :发光二极管阵列数据写地址 901F000Eh :发光二极管阵列使能、交通灯状态、 PWM 状态写地址、直流电机使能位 其它地址:保留 键盘的扫描码由 DSP 的扩展地址 0901f0002H 给出,当有键盘输入时,读此端口得到扫 描码,当无键被按下时读此端口的结果为 0。 // ICETEK-CTR #define CTRKEY (*(unsigned short int *)0x0901f0002) unsigned char GetKey() { unsigned char dbReturn; dbReturn=CTRKEY; dbClearKey=CTRCLKEY; return dbReturn; } 4 软件设计 4.1设计思路 一条蛇在 64*128密闭的围墙内,在围墙内随机出现一个食物,通过按键盘上的四个光标 键控制蛇向上下左右四个方向移动,蛇头撞到食物,则表示食物被蛇吃掉,这时蛇的身体长 一节,接着又随机出现食物,等待被蛇吃掉,如果蛇在移动过程中,撞到墙壁或身体交叉蛇 头撞到自己的身体游戏结束。 4.1总体流程图 图 4-1总体流程图 4.2相关函数定 义 函数定义是对各个基础函数的定义 , 并且设置需要运用的信息,便于调用 void food();//产生食物的函数 void Init();// void move();//贪吃蛇的运动 void draw(int index);//显示 void InitInterrupt(void);//中断函数 4.3函数 main( )程序流程图 主函数是程序的主流程,首先定义使用到的常数、全局变量及函数原型说明,然后初始 化图形系统 . 图 4-2 main()函数流程图 主函数模块主调用各个程序,像清除屏幕显示内容 . 延时子程序 . 打开显示。以及全局初 始化。同时还有控制显示的方式位。 显示与清屏函数 init_emif(); // 初始化 emif InitCTR(); // 初始化 ICETEK-CTR TurnOnLCD(); // 打开显示 LCDCLS(); // 清除显示内存 LCDCMD(LCDCMDSTARTLINE); // 设置显示起始行 =0 游戏背景的显示流程图 图 4-2游戏背景显示流程图 union { char bitarray[BITNSLOTS(64*128 )]; unsigned char LCDarray[64*128/8]; }Ground; //游戏背景 int SnakeFoodPosition,SnakeLong;//蛇身节点 5.程序调试。 5.1相关头文件 #include #include 5.2程序调试及下载 1.打开工程文件 (1)双击桌面上“ CCS 2(‘ C6000) ” ,启动 Code Composer Studio 2.2。 (2)打开菜单“ Project ”的“ New ?”项;在“ Project ”项中输入 snack ,在“ Location ” 中选择 C:\snack 目录,单击“完成”建立 snack.pjt 。 (3)在工程中添加文件 snack.c (源程序见附录)和 snack.cmd 。源程序见附录 2.设置工程文件 (1)打开设置窗口:选择菜单“ Project ”的“ Build Options?”项。 (2)选择编译设置:单击“ Compiler ”属性页,将“ Basic ”栏中“ Target Version”项 改成“ 671x ” 。 (3)选择链接设置:单击“ Linker ”属性页,进行 c 语言程序的设置: -“ Autoinit Model”项设置成“ Autoinitialization ” -“ Code Entry Point”项中输入“ start ” 。 (4)退出设置窗口:单击“确定”按钮。 3.编译源文件,下载可执行程序 (1)单击菜单“ Project ” 、 “ Rebuild All” 。 (2)执行 File Load Program ,在随后打开的对话框中选择刚刚建立的 snack.out 文件。 完成后,系统自动打开源程序文件 snack.c 。 4.打开观察窗口 (1)开启 CPU 寄存器观察窗口:单击菜单“ View ” 、 “ Registers ” 、 “ Core Registers” 。 (2)在内存观察窗口中观察变量的值: 选择“ View ”菜单中“ Memory ?”项,在“ Memroy Window Options ”窗口中的“ Adress ” 项中输入 Ground ,单击“ OK ”完成设置;在随后显示的“ Memory ”窗口中单击鼠标右键, 选择“ Float In Main Window”项。 5.观察程序运行结果 6.生成内存映像文件 (1)单击菜单“ Project ” 、 “ Options ?” ,启动“ Build Options”工程设置对话框。 (2)单击“ Linker ”属性页,在“ Map Filename ”项中输入需要生成的 map 文件名,比如 可以输入 snack.map (3)单击“确定” ,完成设置。 (4)选择菜单“ Project ” 、 “ Rebuild All” ,重新编译工程,生成新设置的 map 文件。 总结 在这一学期的 DSP 技术实习,让我们对 DSP 技术有了一个更深的了解,以前总认为学习 DSP 很枯燥, 认为我们所学没有什么用处, 但现在通过设计贪吃蛇游戏这个程序使我们懂得了 如何将所学的知识运用于生活当中。虽然在刚开始设计程序时不太明白如何去设计这程序, 但当我们用 C 语言做出这个贪吃蛇程序并用 DSP 实现后,让我们深深感受到 DSP 及 C 语言的 神奇。 在设计这个程序中我们主要学会了如何运用以下有关 C 语言的知识: 1) 函数定义是十分重要的,它是对对读程序的人正确认识程序十分重要,在修改这个程 序的过程中也能很快找到程序各模块的作用,大大加了程序的可读性。 2) 在 C 语言中函数先从 main()函数开始从 main 函数结束。 main()函数是 C 源程序编译 时的开始,从 main()函数开始读函数可将其他函数的功能理解得更透彻。 3) 在做程序的时候先列框架,将这个程序所要达到的目的功能分析出来,选择正确的数 据结构然后在将程序模块化,按照模块编写函数更加简单合理。 4) 我们还了解了很多的库函数的作用,如字符串函数中有很多对字符串进行处理的函 数,起功能我都有所了解。 5)在 c 语言 中变量和数据要先定义后使用。 附录 贪吃蛇源程序: #include #include #define BITMASK(b) (128>>((b) % CHAR_BIT)) #define BITSLOT(b) ((b) / CHAR_BIT) #define BITSET(a, b) ((a)[BITSLOT(b)] |= BITMASK(b)) #define BITCLEAR(a, b) ((a)[BITSLOT(b)] &= ~BITMASK(b)) #define BITTEST(a, b) ((a)[BITSLOT(b)] & BITMASK(b)) #define BITNSLOTS(nb) ((nb + CHAR_BIT - 1) / CHAR_BIT) #define win 2 #define fail 1 #define running 0 #define up 1 #define down 2 #define left 3 #define right 4 void food();//声明食物产生函数 void Init();//声明 Init 函数 void move();//声明运动函数 void draw(int index); //声明显示函数 void InitInterrupt(void); //声明中断函数 int SnakeFoodPosition,SnakeLong;//定义蛇的长度及食物位置 int SnakeBody[20],SnakeHead,SnakeTail,GameStatus,SnakeDirection,SnakeTailIndex; //定义蛇的身体、蛇头、蛇尾、游戏状态,运动方向等 union { char bitarray[BITNSLOTS(64*128 )]; unsigned char LCDarray[64*128/8]; }Ground;//定义游戏背景 int SnakeFoodPosition,SnakeLong; main() { init_emif(); // 初始化 emif InitCTR(); // 初始化 ICETEK-CTR TurnOnLCD(); // 打开显示 LCDCLS(); // 清除显示内存 Init(); InitInterrupt();//中断服务函数 while(1) //定义一个 while 死循环 { switch (GameStatus) //switch语句实现游戏状态 { case win: //游戏成功 Init(); break; case fail: //游戏失败 Init(); //调用 Init 函数 break; case running: //游戏开始 move(); //调用运动函数 draw(BITSLOT(SnakeBody[SnakeHead]));//显示蛇头位置 draw(BITSLOT(SnakeTailIndex)); //显示蛇尾位置 Delay(500); //调用延时函数 } } } void Init() { int i; for (i=0;i<64*128>64*128> { Ground.bitarray[i]=0; } //memset(Ground.LCDarray, 0, 4*16); for(i=0;i<64;i++) 构造游戏背景的左、右边框="">64;i++)> BITSET(Ground.bitarray,i); BITSET(Ground.bitarray,127*64+i); } for(i=1;i<127;i++) 构造游戏背景的上、下边框="">127;i++)> BITSET(Ground.bitarray,i*64); BITSET(Ground.bitarray,i*63+63); } BITSET(Ground.bitarray,330); //蛇的初始长度 BITSET(Ground.bitarray,331); BITSET(Ground.bitarray,332); SnakeBody[0]=330; //蛇的位置 SnakeBody[1]=331; SnakeBody[2]=332; SnakeHead=2; //蛇头的位置 SnakeTail=0; //蛇尾的位置 SnakeLong=3; //设置蛇的初始长度为 3 food(); //调用食物函数 SnakeDirection=right; //设置蛇的开始运动方向为 right GameStatus=running; /设置蛇的开始状态 for (i=0;i<64*128>64*128> { draw(i); //调用显示函数 draw } } void food() //定义食物函数 food { srand((unsigned) time(0)); SnakeFoodPosition=rand()%(64*128-1); while (BITTEST(Ground.bitarray, SnakeFoodPosition)) { SnakeFoodPosition+=SnakeLong; SnakeFoodPosition=SnakeFoodPosition%(64*128-1); } BITSET(Ground.bitarray,SnakeFoodPosition); draw(BITSLOT(SnakeFoodPosition)); } void move() //定义运动函数 move { int i; //定义变量 i i=SnakeBody[SnakeHead]; //暂存蛇头上一位置 SnakeHead++; SnakeHead=SnakeHead%20; switch (SnakeDirection) //用 switch 语句实现各个方向的分支 { case up: SnakeBody[SnakeHead]=i-64; break; case down: SnakeBody[SnakeHead]=i+64; break; case left: SnakeBody[SnakeHead]=i-1; break; case right: SnakeBody[SnakeHead]=i+1; break; } if (!BITTEST(Ground.bitarray,SnakeBody[SnakeHead])) { BITSET(Ground.bitarray,SnakeBody[SnakeHead]); BITCLEAR(Ground.bitarray,SnakeBody[SnakeTail]); SnakeTailIndex=SnakeBody[SnakeTail]; SnakeTail++; SnakeTail=SnakeTail%20; } else if (SnakeBody[SnakeHead]==SnakeFoodPosition) { BITSET(Ground.bitarray,SnakeBody[SnakeHead]); SnakeLong++; food(); if (SnakeLong==20) GameStatus=win; } else GameStatus=fail; } void draw(int index) //定义显示函数 draw { int page; int row; page=index%8; row=index/8; LCDCMD(7-page+0x0b8); // 设置操作页 if (row<> { LCDCMD(row+LCDCMDVERADDRESS); // 起始列 LCDWriteLeft(Ground.LCDarray[index]); } else { LCDCMD(row-64+LCDCMDVERADDRESS); // 起始列 LCDWriteRight(Ground.LCDarray[index]); } } void InitInterrupt(void) //定义中断服务函数 { // 设置中断控制寄存器 CSR&=0xfffffffe; // 关中断 GIE=0 ISTP=0x00000c00; // 重置中断向量表到 0C00h IMH=0x0; // 指定 xint5中断 IML=0x0a0; ICR=0xff; // 清除等待的中断 IER=0x23; // 使能 xint5中断 CSR=CSR|1; // 开中断 } void interrupt XINT5() //中断响应函数 { int key; key=Getkey(); //调用键盘函数 switch(key) //当检测有键按下,判断键值,根据键值实现各个方向转变 { case SCANCODE_2: SnakeDirection=down; //向下 break; case SCANCODE_8: SnakeDirection=up; //向上 break; case SCANCODE_4: SnakeDirection=left; //向左 break; case SCANCODE_6: SnakeDirection=right; //向右 break; } } 作者:冷月 呵呵……功能基本上都有~ 我没事的时候做的~小小的自我夸奖一下~发表了希望大家能够参考 //主类 package com.test; import java.awt.*; import javax.swing.*; import java.awt.event.*; import java.io.*; import java.util.ArrayList; public class Worm extends JFrame implements ActionListener{ JMenuBar jmb; JMenu jm1; JMenuItem jmi1,jmi2,jmi3,jmi4,jmi5,jmi6; Wormpanel wp; JPanel jp; public static void main(String args[]) { Worm w=new Worm(); } public Worm() { jmb=new JMenuBar(); jm1=new JMenu("菜单"); jmi6=new JMenuItem("新游戏"); jmi6.addActionListener(this); jmi1=new JMenuItem("继续"); jmi1.addActionListener(this); jmi2=new JMenuItem("暂停"); jmi2.addActionListener(this); jmi3=new JMenuItem("存盘退出"); jmi3.addActionListener(this); jmi5=new JMenuItem("接着上一次玩"); jmi5.addActionListener(this); jmi4=new JMenuItem("退出"); jmi4.addActionListener(this); jp=new JPanel() { public void paint(Graphics g){ g.setFont(new Font("宋体",Font.BOLD,40)); g.drawString("play", 250, 200); } }; jm1.add(jmi6); jm1.add(jmi1); jm1.add(jmi2); jm1.add(jmi3); jm1.add(jmi5); jm1.add(jmi4); jmb.add(jm1); this.setJMenuBar(jmb); this.add(jp); this.setSize(650,500); this.setVisible(true); this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); } public void actionPerformed(ActionEvent e) { if(e.getSource()==jmi6) {System.out.println("ok"); wp=new Wormpanel(); this.addKeyListener(wp); this.add(wp); this.setVisible(true); } if(e.getSource()==jmi4) { System.exit(0); } if(e.getSource()==jmi2) { this.wp.setSpeed(0); } if(e.getSource()==jmi1) { this.wp.setSpeed(1); } if(e.getSource()==jmi3) { ArrayList FileWriter fw=null; BufferedWriter bw=null; try { fw=new FileWriter("D:/Recording.txt"); bw=new BufferedWriter(fw); for(int i=0;i { Wormpoint wm=al.get(i); if(i==0) { int dire=wm.getDirestion(); bw.write(dire+"\r\n"); } int x=wm.getX(); int y=wm.getY(); bw.write(x+" "+y+"\r\n"); } } catch (IOException args) { args.printStackTrace(); }finally{ try { bw.flush(); bw.close(); fw.close(); System.exit(0); } catch (IOException args) { args.printStackTrace(); } } } if(e.getSource()==jmi5) { wp=new Wormpanel(); this.addKeyListener(wp); this.add(wp); this.setVisible(true); ArrayList FileReader fr=null; BufferedReader br=null; try { fr=new FileReader("D:/Recording.txt"); br=new BufferedReader(fr); String st; st=br.readLine(); Wormpoint head=new Wormpoint(); head.setDirestion(Integer.parseInt(st)); st=br.readLine(); String shead[]=st.split(" "); head.setX(Integer.parseInt(shead[0])); head.setY(Integer.parseInt(shead[1])); al.add(head); while((st=br.readLine())!=null) { Wormpoint wm=new Wormpoint(); String str[]=st.split(" "); wm.setX(Integer.parseInt(str[0])); wm.setY(Integer.parseInt(str[1])); al.add(wm); } this.wp.vp=al; } catch (IOException args) { args.printStackTrace(); }finally{ try { br.close(); fr.close(); } catch (IOException args) { args.printStackTrace(); } } } } } //面板类 进行界面操作 package com.test; import java.awt.*; import java.awt.List; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import java.util.*; import java.util.Timer; import javax.swing.*; public class Wormpanel extends JPanel implements KeyListener{ ArrayList Wormpoint point; Wormpoint wp; String reco; int speed; boolean ismove=true; Recorder recor; public int getSpeed() { return speed; } public void setSpeed(int speed) { this.speed = speed; } public Wormpanel() { speed=1; recor=new Recorder(); recor.filereader(); reco=recor.recorder; point=new Wormpoint(); point.setWidth(10); point.setX((int)(Math.random()*450)); point.setY((int)(Math.random()*380)); for(int i=0;i { wp=new Wormpoint(); wp.setX(20); wp.setY(20-wp.getWidth()); vp.add(wp); } Timer t=new Timer(); t.schedule(new Mytask(), 0,100); } public void touch() {boolean b=false; Wormpoint w=vp.get(0); switch(w.getDirestion()) {case 1: if((w.getX() .getX())||(w.getX()+w.getWidth() { if(w.getY() { System.out.print("touch"); ismove=false; point=new Wormpoint(); point.setWidth(10); point.setX((int)(Math.random()*450)); point.setY((int)(Math.random()*380)); Wormpoint.length++; recor.cadd(); } } if(recor.getcount()*10>Integer.parseInt(reco)) { recor.filewriter(); } break; case 2: if((w.getX() { if(w.getY()+w.getWidth() { ismove=false; point=new Wormpoint(); point.setWidth(10); point.setX((int)(Math.random()*450)); point.setY((int)(Math.random()*380)); Wormpoint.length++; recor.cadd(); } } if(recor.getcount()*10>Integer.parseInt(reco)) { recor.filewriter(); } break; case 3: if((w.getY()>point.getY()&&w.getY() { if(w.getX()>point.getX()&&w.getX() { ismove=false; point=new Wormpoint(); point.setWidth(10); point.setX((int)(Math.random()*450)); point.setY((int)(Math.random()*380)); Wormpoint.length++; recor.cadd(); } } if(recor.getcount()*10>Integer.parseInt(reco)) { recor.filewriter(); } break; case 4:if((w.getY()>point.getY()&&w.getY() { if(w.getX()+w.getWidth()>point.getX()&&w.getX()+w.getWidth() { ismove=false; point=new Wormpoint(); point.setWidth(10); point.setX((int)(Math.random()*450)); point.setY((int)(Math.random()*380)); Wormpoint.length++; recor.cadd(); } } if(recor.getcount()*10>Integer.parseInt(reco)) { recor.filewriter(); } break; } } public void paint(Graphics g) { super.paint(g); g.fillRect(0, 0,450, 400); g.setColor(Color.cyan); if(!point.isIseate()) {g.fillOval(point.getX(), point.getY(), point.getWidth(), point.getWidth()); } g.setColor(Color.DARK_GRAY); g.setFont(new Font("宋体",Font.BOLD,20)); g.drawString("我的得分:"+recor.getcount()*10, 460, 50); g.drawString("我的记录:"+reco, 460, 80); Wormpoint w; for(int i=0;i {w= vp.get(i); g.setColor(Color.red); g.fill3DRect(w.getX(), w.getY(), w.getWidth(),w.getWidth() ,false); g.setColor(Color.black); g.drawRect(w.getX(), w.getY(), w.getWidth(),w.getWidth() ); } } class Mytask extends TimerTask{ public void run() { Wormpoint w=vp.get(0); Wormpoint xin=new Wormpoint(); int direct=w.getDirestion(); switch(direct) { case 1: xin.setX(w.getX()); xin.setDirestion(w.getDirestion()); xin.setY(w.getY()-w.getWidth()*speed); if(xin.getY()<=0)>=0)> xin.setY(400-xin.getWidth()); break; //up case 2: xin.setX(w.getX()); xin.setDirestion(w.getDirestion()); xin.setY(w.getY()+w.getWidth()*speed); if(xin.getY()+xin.getWidth()>=400) xin.setY(0); break; //down case 3: xin.setX(w.getX()-w.getWidth()*speed); xin.setDirestion(w.getDirestion()); xin.setY(w.getY()); if(xin.getX()<> xin.setX(450-xin.getWidth()); break; //left case 4: xin.setX(w.getX()+w.getWidth()*speed); xin.setDirestion(w.getDirestion()); xin.setY(w.getY()); if(xin.getX()+xin.getWidth()>=450) xin.setX(0); break; //right } vp.add(0,xin); touch(); if(ismove&&speed!=0) vp.remove(vp.size()-1); ismove=true; repaint(); } } public void keyPressed(KeyEvent e) { if(e.getKeyCode()==e.VK_UP) { vp.get(0).setDirestion(1); //up } if(e.getKeyCode()==e.VK_DOWN) { vp.get(0).setDirestion(2);//down } if(e.getKeyCode()==e.VK_LEFT) { vp.get(0).setDirestion(3);//left } if(e.getKeyCode()==e.VK_RIGHT) { vp.get(0).setDirestion(4); } } public void keyReleased(KeyEvent e) { // TODO Auto-generated method stub } public void keyTyped(KeyEvent e) { // TODO Auto-generated method stub } } //单元类 package com.test; public class Wormpoint { private int x; private int y; private int direstion=2; static int length=5; private boolean iseate=false; public boolean isIseate() { return iseate; } public void setIseate(boolean iseate) { this.iseate = iseate; } public int getLength() { return length; } public void setLength(int length) { this.length = length; } private int width=8; public int getWidth() { return width; } public void setWidth(int width) { this.width = width; } public int getDirestion() { return direstion; } public void setDirestion(int direstion) { this.direstion = direstion; } public int getX() { return x; } public void setX(int x) { this.x = x; } public int getY() { return y; } public void setY(int y) { this.y = y; } } //记录类 package com.test; import java.io.*; public class Recorder { private int count=0; public String recorder; public void cadd() { count++; } public int getcount() { return count; } public void filereader() { FileReader fr; BufferedReader br; try { fr=new FileReader("D:/Recorder.txt"); br=new BufferedReader(fr); recorder=br.readLine(); } catch (Exception e) { e.printStackTrace(); } } public void filewriter() { FileWriter fw=null; BufferedWriter bw=null; try { fw=new FileWriter("D:/Recorder.txt"); bw=new BufferedWriter(fw); bw.write(""+(this.getcount()*10)); } catch (IOException e) { e.printStackTrace(); }finally{ try { bw.flush(); bw.close(); fw.close(); } catch (IOException e) { e.printStackTrace(); } } } } 第 6章 Android 游戏入门(一) 第一部分 本次上机任务 贪吃蛇游戏实现 第二部分 任务实现 贪吃蛇游戏是一款经典的老游戏, 很多游戏教程都会用该游戏作为游戏入门的示例。 今 天我们也采取该游戏作为游戏入门示例。 在理论课上已经对于如何使用 View 组件进行游戏 开发有了初步了解,下面通过 View 组件实现完成的贪吃蛇游戏。 掌握要点: 掌握将 View 组件作为游戏开发组件的基本流程 掌握当手机屏幕发生改变时数据的保存 任务说明: 1、游戏的基本流程。 游戏加载时界面如图: 当点击模拟器上的向上的方向键,开始游戏,界面如下: 此时点击上下左右方向键,可以控制“蛇”按照特定的方式移动,每吃一个食物,蛇身 会长一格,同时在背景内会再次提供一个新的食物,当蛇头碰到“墙壁”时,游戏结束。 2、游戏的场景实现 通过上面的截图可以看出, 整个游戏的场景有红黄绿三种颜色的小图片构成, 故在此编 写 TileView ,该类作为整个场景的基础类,代码如下: 在该类中 loadTile()方法进 行图片 的加载 , resetTiles()方法设置图片 数组的 大小, onSizeChanged()方法进行游戏开始前相关元素初始化工作, onDraw()方法在 onSizeChanged()方法调用后被调用,完成图形的绘制工作。 3、游戏主类 SnakeView 的实现。该类继承 TileView ,在该类中完成游戏图形渲染以及 游戏碰撞检测等主要功能。 首先在该类中进行状态设置常量代码编写: 第二步,编写 initSnakeView()方法,负责游戏开始时初始化游戏场景。代码如下: 第三步编写内部类 Coordinate ,该类主要的功用是负责图片移动坐标的维护。代码如下: 第四步编写内部 Handler 负责 UI 的更新。代码如下: 第五步构建添加食物方法,代码如下: 当表示蛇的集合存在当前食物的坐标,表示发生了碰撞了。代码如下: 第七步编写 initNewGame 方法,程序第一次加载显示的游戏场景。代码如下: 该方法清空 “蛇” 的图片和食物图片, 而后六章图片组成一个初始的蛇, 而且蛇最开始向北 移动,而后随机的添加两个食物。 第八步编写 oordArrayListToArray 和 coordArrayToArrayList 将数组和集合中进行转换的方 法。代码如下: 通过这两个方法将整型数组和集合框架进行方便的转换。 第九步编写食物被吃掉后,更新食物方法。代码如下: 第十步编写更新外围“墙”的方法,代码如下: 从上述代码中可以看出墙四面铺满了绿色的图片。 第十一步编写更新蛇的方法,代码如下: 该方法是该类中最核心的方法,其设置加载图片,以及相应的碰撞检测。 第十二步编写游戏状态设置的方法, setMode()。游戏在不同的状态时,进行不同的处理动 作。代码如下: 第十三步,游戏状态发生改变时,需要进行整个游戏场景的更新处理。编写 update 方 法,更新背景“墙”,更新食物,更新蛇等处理。代码如下: 第十四步,事件处理。玩家点击不同的方向键,蛇向不同的方向移动。代码如下: 4、 MainActivity 游戏界面的实现。 首先,布局文件的实现。该布局文件使用刚刚定义的 SnakeView 作为视图组件,代码 如下: MainActivity 的代码如下: 通过以上步骤的编写,贪食蛇就可以在模拟器上运行了。 第三部分 作业 在上机的示例中, 应用程序运行时没有任何问题的, 但是一旦屏幕的状态发生改变, 那 么游戏将重新开始无法保存原来的数据。 怎样才能将屏幕状态改变时, 将贪食蛇原来的 状态进行保存呢? 在 SnakeView 中编写如下状态保存的代码: 通过 saveState()方法保存状态,通过 resoreState()方法恢复上次的状态。 在 MainActivity 中编写如下代码: 通过上述的方式, 可以保证在屏幕状态发生改变的时候, 原有状态的数据被保存下来。范文二:贪吃蛇游戏设计
范文三:贪吃蛇游戏设计
范文四:贪吃蛇游戏代码
范文五:贪吃蛇游戏分析