南京工程学院
游戏编程基础课程
综合作业
题 目 贪吃蛇游戏设计
班 级 学 生 姓 名 学 号
20xx年 1 月 4 号
一、程序功能与设计思路
整体设计目标:含有游戏场景、游戏角色、动作控制、动画、交战(可选)、人工智能、音效(可选)、网络支持(可选)等各部分内容,界面风格及应用方法自己根据游戏的特色综合考虑。
1、游戏实现功能说明:
编写C语言程序实现贪吃蛇游戏,贪吃蛇游戏是一个深受人们喜欢的游戏:一条蛇在密闭的围墙内,在围墙内随机出现一个食物,通过键盘上的四个光标键控制蛇向上下左右四个方向移动,蛇头撞到食物,则表示食物被吃掉,这时蛇的身体长一节,同时计1分;接着又出现食物,等待被蛇吃掉,如果蛇在移动过程中,撞到墙壁或身体交叉(蛇头撞到自己的身体)游戏结束。
2、界面效果显示:
程序运行时边框表示围墙,黄色星星块代表蛇,红色小方块代表食物。 W、S、A、D表示蛇移动的上下左右四个方向,按任意键暂停,每吃到一个红色 小方块,蛇身长度加一,分数加一。撞到透明围墙,则显示失败。
3、主界面设计思路(简单结构框图)
这个程序的关键点是表示蛇的图形以及蛇的移动。用一个小矩形块表示蛇的一节身体,身体每长一节,增加一个矩形块,蛇头用两节表示。移动从蛇头开始,所以蛇不能向相反方向移动,也就是蛇尾不能改作蛇头。如果不按任何键,蛇自行在当前方向上前移,当游戏者按了有效的方向键后,蛇头朝着指定的方向移动,一步移动一节身体,所以当按了有效的方向键后,先确定蛇头的位置,然后蛇身体随着蛇头的方向移动,图形的实现是从蛇头的新位置开始画出蛇,这时,由于没有清屏的原因,原来蛇的位置和新蛇的位置差一个单位,所以看起来蛇会多一节身体,所以将蛇的最后一节用背景色来覆盖。食物的出现和消失是以画矩形块和覆盖矩形块来表示的。
二、相关原理知识介绍
实现的原理说明,包括函数介绍、算法、循环、消息、DirectX的相关技术说明
等。要含有必要的框图,流程图,变量、函数参数及功能列表。
1、
游戏功能模块
(1)贪吃蛇的控制功能:通过各种条件的判断,实现对游戏蛇的左移、右移、下移、上移、自由移动,贪吃蛇的加长功能。
(2)游戏显示更新功能:当贪吃蛇左右移动、上下移动,以及身体加长时要清除先前的贪吃蛇图像,用新坐标重绘贪吃蛇。
(3)游戏分数更新功能:在游戏玩家进行游戏过程中,需要按照一定的游戏规则给游戏玩家计算游戏分数。比如,贪吃蛇每吃到一个事物就加一分。
2、游戏流程图
3、函数参数以及变量
(1)用户定义了COORD pos ,那么pos 其实是一个结构体变量,其中X和Y是它的成员,通过修改pos.X和pos.Y的值就可以实现光标的位置控制。
SetConsoleCursorPosition是API中定义光标位置的函数。
(2)GetStdHandle是一个Windows API函数。它用于从一个特定的标准设备(标准输入、标准输出或标准错误)中取得一个句柄。
GetStdHandle(STD_OUTPUT_HANDLE) 标准输出的句柄
(3)SetConsoleTextAttribute是API设置字体颜色和背景色的函数。
三、设计步骤说明
1.开发环境配置(路径、头文件、静态或动态链接库等)
(1)新建项目,选择Visual C++,Win32的Win32控制台应用程序
(2)编译文件名称,选择预编译头,则得到stdafx.h头文件
(3)添加源代码即可实现这个游戏
2.自定义类及其成员函数的说明
(1) 定义位置函数的代码如下: void gotoxy(int x, int y)
{
COORD pos; pos.X = 2 * x; pos.Y = y; SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), pos); }
(2)定义颜色函数的代码如下:
void color(int a)
{
} (3)初始化函数(初始化围墙、显示信息、苹果)的代码如下: void init(int apple[2])
{
int i, j; //初始化围墙 int wall[N + 2][N + 2] = { { 0 } }; for (i = 1; i <= N; i++)
-5- SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), a);
}
{ } color(11); for (i = 0; i<N + 2; i++) { } gotoxy(N + 3, 1);//显示信息 color(20); std::cout << "按 W S A D 移动方向" << std::endl; gotoxy(N + 3, 2); color(20); std::cout << "按任意键暂停" << std::endl; gotoxy(N + 3, 3); color(20); std::cout << "得分:" << std::endl; apple[0] = rand() % N + 1;//苹果 apple[1] = rand() % N + 1; gotoxy(apple[0], apple[1]); color(12); std::cout << "●" << std::endl; for (j = 0; j<N + 2; j++) { } std::cout << std::endl; if (wall[i][j]) std::cout << "■"; else std::cout << "□"; for (j = 1; j <= N; j++) wall[i][j] = 1;
3、实现功能的关键代码及代码含义 int main()
{
int i, j; int** snake = NULL; int apple[2]; int score = 0; int tail[2]; int len = 3; //蛇长 char ch = 'p'; srand((unsigned)time(NULL)); init(apple);
snake = (int**)realloc(snake, sizeof(int*)*len); for (i = 0; i<len; i++) { } while (1)//进入消息循环 { tail[0] = snake[len - 1][0]; tail[1] = snake[len - 1][1]; gotoxy(tail[0], tail[1]); color(11); std::cout << "■" << std::endl; for (i = len - 1; i>0; i--) { } if (_kbhit()) { } switch (ch) { case 'w':snake[0][1]--; break; case 's':snake[0][1]++; break; case 'a':snake[0][0]--; break; case 'd':snake[0][0]++; break; default: break; } gotoxy(snake[0][0], snake[0][1]); color(14); std::cout << "★" << std::endl; Sleep(abs(300 - 0.5*score)); //数值越大,速度越慢 if (snake[0][0] == apple[0] && snake[0][1] == apple[1])//吃掉苹果后蛇分数加1,蛇长加1 gotoxy(0, N + 2); ch = _getche(); snake[i][0] = snake[i - 1][0]; snake[i][1] = snake[i - 1][1]; gotoxy(snake[i][0], snake[i][1]); color(14); std::cout << "★" << std::endl; snake[i][0] = N / 2; snake[i][1] = N / 2 + i; gotoxy(snake[i][0], snake[i][1]); color(14); std::cout << "★" << std::endl; snake[i] = (int*)malloc(sizeof(int)* 2); for (i = 0; i<len; i++)
} } {
} if (snake[0][1] == 0 || snake[0][1] == N || snake[0][0] == 0 || snake[0][0] == N)//撞{ } gotoxy(N / 2, N / 2); color(30); std::cout << "失败!!!" << std::endl; for (i = 0; i<len; i++) free(snake[i]); Sleep(INFINITE); exit(0); score++; len++; snake = (int**)realloc(snake, sizeof(int*)*len); snake[len - 1] = (int*)malloc(sizeof(int)* 2); apple[0] = rand() % N + 1; apple[1] = rand() % N + 1; gotoxy(apple[0], apple[1]); color(12); std::cout << "●" << std::endl; gotoxy(N + 5, 3); color(20); std::cout << score << std::endl; 到围墙后失败 return 0;
四、 运行结果及操作方法
(1)刚运行的时候,蛇的初始位置不变,而食物的初始位置是随机的
(2)控制方向键移动蛇身,每吃到一个食物,分数加1,蛇身加一节(本身蛇长三节)
(3)撞到墙壁则显示失败
五、经验与总结
一开始的时候,不知道要怎么编写有图形界面的贪吃蛇,后来在网上找了一些关于图形界面编程的案例,通过组成编写,写出了一个简单的贪吃蛇小游戏。虽然存在一些BUG,但经过调试运行之后,特别是怎么吃到食物,并且蛇身增长的地方花了好
多时间,后来BUG基本上被找出来了,蛇就能吃到食物了。
通过这段时间的学习和摸索,对C语言又有了更深一步的了解。从一点不会做游戏,到现在慢慢的摸索,还是学到了很多书本上没有的东西,也对编写游戏有了更深一层的兴趣。
第二篇:贪吃蛇游戏设计报告(C++)
青岛理工大学琴岛学院
设 计 报 告
课题名称:贪吃蛇游戏设计
学 院:青岛理工大学琴岛学院
专业班级:计算机网络技术091
学 号:200903120017
学 生:王三鹏
指导教师:张秀国、宋传磊
青岛理工大学琴岛学院教务处
20##年12月29日
目 录
一、 需求分析……………………………………… 4
二、 设计说明……………………………………… 4
1. 设计思路…………………………………………… 4
2. 程序框图…………………………………………… 5
3. 主要函数…………………………………………… 7
三、 代码实现……………………………………… 6
1. 构造函数…………………………………………… 6
2. 放置食物…………………………………………… 6
3. 碰撞检测…………………………………………… 7
四、 问题解决……………………………………… 9
五、 总结展望………………………………………10
六、 参考文献………………………………………10
一、需求分析
1.选题
接到课程设计任务后,我们对曾经非常感兴趣的几款经典小游戏作了分析并筛选,然后发现使用MFC类库都可以完成这些游戏的编译,但是我们刚刚学习,还不能完全掌握MFC类的使用和复杂的算法,但是我们相信通过一个简单的贪吃蛇游戏可以让我们对MFC得到充分的锻炼。
2.功能
使用MFC编译的游戏,我们预计实现游戏的开始、暂停、继续、等级的提升、难度的增加。
3.重要的MFC
1) CWnd:窗口,它是大多数“看得见的东西”的父类(Windows里几乎所有看得见的东西都是一个窗口,大窗口里有许多小窗口),比如视图CView、框架窗口CFrameWnd、工具条CToolBar、对话框CDialog、按钮CButton,etc;一个例外是菜单(CMenu)不是从窗口派生的。
2) CDocument文档,负责内存数据与磁盘的交互。最重要的是OnOpenDocument(读入),OnSaveDocument(写盘),Serialize(读写)
3) CView视图,负责内存数据与用户的交互。包括数据的显示、用户操作的响应(如菜单的选取、鼠标的响应)。最重要的是OnDraw(重画窗口),通常用CWnd::Invalidate()来启动它。另外,它通过消息映射表处理菜单、工具条、快捷键和其他用户消息。
4) CDialog对话框
5) CPen笔,画线
6) CBrush刷子,填充
7) CBitmap位图
8) CRgn区域,指定一块区域可以用于做特殊处理。
9) CString字符串。封装了C中的字符数组,非常实用。
10) CPoint点,就是(x,y)对
11) CRect矩形,就是(left,top,right,bottom)
二、总体设计说明
1.设计思路
在数据结构上,用数组存储蛇以及蛇的运动区域。在数组中,每个元素有四个云感动方向,分别为RIGHT,LEFT,UP,DOWN.在没有蛇身的区域,数组的元素值为0。在蛇身的每个节点,用一个数组元素存储当前蛇节点的运动方向。通过改变数组中元素的值来表示蛇身的移动。在程序中运用定时器来保证蛇的持续移动。每个一定的时间间隔,蛇移动一格。
在设计界面时。在Visual Studio C++6.0的环境下,建立基于对话框的工程,在工程下设计游戏界面,实现贪食蛇运行算法。在对话框中画出一个矩形,在大矩形中,用20x20的像素单位表示每一个蛇的节点。当数组节点的值不为0的时候,在大矩形中画出相应的蛇的节点矩形。并在蛇每移动一小格的时候,重绘整个窗口。在对蛇身进行绘画时。通过数组元素的值将蛇身在对话框的矩形中显示出来。
在蛇身运动时,首先在蛇头位置蛇身增长一个节点,然后判断蛇头是否到达食物所在位置,若在食物所在位置,则蛇尾不减一,蛇身继续移动,若蛇头不在食物位置,则蛇尾减一,蛇身继续移动。若蛇头越界或者蛇头与蛇身重叠,则游戏结束。
2.程序框图
操作控制:如图1所示
图1
游戏控制:如图2所示
图2
3.主要函数和变量:如表1表2所示
表1主要函数和变量
表2按钮控件资源清单
三、代码实现
1.构造函数:
通过定义构造函数CSnakeView::CSnakeView()调用IniGame()实现游戏界面的初始化,为开始游戏做好准备,所以在开始进行游戏之前,游戏界面就已经显示完整。
CSnakeView::CSnakeView()
{
……
IniGame();
……
}
CSnakeView::~CSnakeView()
{
}
2.消息处理食物位置:
在函数中声明一个(x,y)数对uAimX,uAimY,然后执行条件循环while(1),使用随机数函数rand()为uAimX,uAimY赋值,并在一个循环体中判断该点位置上是否有元素,若没有,则放置实物在该坐标上。
void CSnakeView::IniAim()
{
int uAimX,uAimY;
while(1)
{
uAimX=rand()%m_nHeight;
uAimY=rand()%m_nWidth;
int uTag=0;
for(int i=0;i<=m_aBody.GetUpperBound();i++)
{
CPoint uPoint=m_aBody.GetAt(i);
if(uPoint.x==uAimX || uPoint.y==uAimY)
{
uTag=1;
break;
}
}
if(uTag==0)
break;
}
m_pAim=CPoint(uAimX,uAimY);
}
3.碰撞检测:
这里碰撞检测是贯穿游戏始末的主要步骤,蛇神移动的每一步都要执行一次碰撞检测。第一步检测边界,如果由计时器控制的蛇身触界则游戏结束,否则进行第二步检测,检测该点是否有蛇身(蛇身分为两种,一种是地图上随机生成的实物,另一种是由时间轴控制的蛇)若该点有食物,则蛇身不减,计时器继续运行;若该点是蛇身则调用KillTimer(1)游戏结束。
碰撞检测实际上是计时器函数的后半部分。
……
if(uTag==0)
{
for(int i=0;i<m_aBody.GetUpperBound();i++)
{
CPoint uPoint1=m_aBody.GetAt(i);
if(uPoint1.x==uPoint.x && uPoint1.y==uPoint.y)
{
uTag=1;
break;
}
}
}
if(uTag==0)
{
m_aBody.InsertAt(0,uPoint);
ReDisplay(uPoint);
if(uPoint.x==m_pAim.x && uPoint.y==m_pAim.y)
{
m_nCount++;
IniAim();
Invalidate();
}
else
{
CPoint uPoint1=m_aBody.GetAt(m_aBody.GetUpperBound());
m_aBody.RemoveAt(m_aBody.GetUpperBound());
ReDisplay(uPoint1);
}
}
else
{
KillTimer(1);
MessageBox(strTemp,"游戏结束"); }
CView::OnTimer(nIDEvent);
运行界面:
①初始界面:如图3
图3 游戏初始界面
②运行时界面:如图4
图4 游戏运行时界面
③结束界面:如图5
图5 游戏结束时界面
四、问题解决:
1.找不到合适的方法实现食物随机放置:
游戏进行时,如果采用原来的方式在游戏进行到200秒以上时会出现没有食物的现象。我们通过while(1)循环控制的随机x,y坐标在地图上绘制一段蛇身来解决了这一问题。
2.在控制游戏选择性条件里食物与蛇身的关系:
一开始,我们的设想是碰撞一次食物,让蛇身加一节,后来实现时遇到了困难,就是不变化。然后我们决定让蛇每前进一步就加一节,然后每步都进行碰撞检测,如果碰撞到食物就不减一节,反之则减。这样,就把每一步都控制在了一个贯穿游戏始末的条件循环体中。
3.碰撞检测不能随计时器一起良好运行:
检测游戏时经常出现撞墙不死的情况,疑似碰撞检测没有正常运行。经改进,我们把碰撞检测加到计时器里面同时运行,这保证了每一步的正确性,又使游戏流畅。
五、总结展望:
参考资料和程序成品,我了解了游戏的最基本形式。ontimer()函数是控制游戏运行的根本函数,借助这一函数,能实现贪吃蛇、俄罗斯方块、赛车等多个游戏的开发。
随机数生成函数rand()的应用在贪吃蛇游戏中是一个关键步骤,通过不断的循环和调用,能保证游戏的持续进行和更新,其实不止是贪吃蛇,在大多数需要随机生成元素的游戏中,都可以用rand()函数生成随机数,再控制坐标等变量实现“随机生成”功能。
碰撞检测是一个逻辑性比较强的地方,这一段代码的实现,我们想了很多,也找了很多资料,不管是检测碰撞类型先后顺序,还是碰撞检测的位置都经过了多次试验,虽然成品游戏中的代码能够实现,但是我们还是改了又改,让它以我们最理想的形式实现。
通过这一次课程设计,我最大的收获就是分析问题解决问题能力的提高,发现问题,而后解决问题是生活的永续循环,这种能力会伴随我们一生;其次,程序的开发给了我很多经验,相信时间充裕的话,我能独立开发一个小游戏;最后,老师提出的对设计报告的种种要求也为我以后的各种报告的规范性做好了心理准备。
六、参考文献:
[1]孙 鑫.《VC++深入详解》.北京.电子工业出版社.2007.11
[2]谭浩强.《C++程序设计》.北京.清华大学出版社.2004.06
[3]刘春辉.《Visual C++程序设计学习笔记》.北京.电子工业出版社.2008.05
[4]Charles Petzold.《Windows 程序设计》.北京.北京大学出版社.1999.03
[5]曾凡锋.《MFC编译技巧与范例详解》.北京.清华大学出版社.2008.10