计算机实习报告
题目:贪吃蛇实验报告
邮箱:yych2009@bupt.edu.cn
贪吃蛇实验报告
一.功能说明
1.1总体功能说明
游戏分单人和双人两种。单人游戏中有一条小蛇,不停地在屏幕上游走,“吃”掉界面上随即出现的果子,每吃一个果子,蛇的身体长度就增长一个单位。当吃下的果子积累到一定程度时,蛇的运动速度会相应地增加,等级也提高了。随着蛇的长度的增长,游戏界面会出现随机产生的毒刺“★”,毒刺固定不动。只要蛇头碰到屏幕四周,或者碰到自己的身子,或者碰到毒刺,活着小蛇就立即毙命,游戏结束。在双人游戏中,有两条蛇头分别为红色和黄色的小蛇,争吃果子,比比谁吃的果子多。只要任意一条蛇的蛇头碰到屏幕四周,或者碰到自己的身子(碰到对方的身体没关系),或者任一条蛇碰到毒刺,游戏就会结束。
该游戏的特色是:一、添加了障碍“毒刺”,同时增加游戏等级这一项,让蛇的运动速度随分数的增加而适当增加,相比传统的贪吃蛇游戏增加了难度;二、增加双人游戏,使其具有竞赛性,让玩家在与他人竞赛中获得乐趣。三、游戏的背景画面更为细腻。
1.2用户界面
游戏开始和退出菜单
选择单人、双人游戏的菜单
1.3使用方法
在单人游戏中,用小写的w、s、a、d四个按键分别控制蛇头上下左右四个方向的转向。双人游戏中,w、s、a、d按键控制红蛇的方向,上下左右四个方向键控制黄蛇的方向。
二.程序设计说明
2.1 总体设计框架
[包括程序执行流程,模块划分等,需要有文字说明和框图表示]
2.2 关键算法描述
算法1:[算法输入参数和输出参数,算法功能,使用什么存储结构,在主程序中起什么作用,可以使用框图或伪代码表示。
算法1:蛇身运动函数
算法1:
if(snake.direct[0]==97)
{
textout(handle,--(--snake.s_x[0]),snake.s_y[0],wColors+10,1,"◆");
Sleep(speed);
PRODUCT_FRUIT(snake.s_x,snake.s_y,&snake.length,&fruit.f_x,&fruit.f_y,wColors,handle); MOVE(snake.s_x,snake.s_y,snake.length,wColors+2,handle);
这是蛇的运动算法,以单人游戏中的向上运动为例。当蛇的结构中的方向变量为97(即“w”)时,蛇头的纵坐标增加一个单位,并延时一定时间。接着运行进行PRODUCT_FRUIT函数,该函数的作用是判断蛇头坐标是否与果子坐标重合,若是,则生成新的果子,且蛇的长度加一。接着再运行move函数,该函数的作用为令蛇身最后一格处显示背景颜色,蛇身的其它部分随蛇头向前移动一位,整体上看就像是蛇向前移动了一格。
算法2 :void RE_POISON_FRUIT(int pfoodx[],int pfoody[],int *foodx,int *foody,int *length,int snakex[],int snakey[])
{
while(1)
{
srand((unsigned)time(NULL)*(unsigned)time(NULL));
int poison_x=2*(rand()%28+2);
int poison_y=rand()%37;
if(poison_x>=4&&poison_y>=2) {
for(q=0;q<*length;q++)
{
if((snakex[q]==poison_x)&&(snakey[q]==poison_y))
break;
}
if(q==*length)
{
for(i=0;i<100;i++)
{
if(pfoodx[i]==*foodx&&pfoody[i]==*foody)
break;
}
if(i==100)
{
pfoodx[num]=poison_x;
pfoody[num]=poison_y;
num++;
break;
}
}
}
}
}
随机产生毒果的函数。产生两个随机数,分别是毒刺的横纵坐标,若该坐标不与蛇的身体的任一节重合,也不与当前显示的果子重合,则该坐标符合要求。
算法3: int DEAD(int snakex[],int snakey[],int len,int pfoodx[],int pfoody[],HANDLE handle)
{
int temp;
for(int i=2;i<=len;i++)
{
if((snakex[0]<4||snakex[0]>60||snakey[0]<2||snakey[0]>40)||(snakex[0]==snakex[i]&&snakey[0]==snakey[i]))
{
temp=1;
break;
}
}
for( int b=0;b<=num;b++)
{
if((snakex[0]==pfoodx[b])&&(snakey[0]==pfoody[b]))
{
temp=1;
break;
}
}
if(temp==1)
return 1;
else
return 0;
}
判断游戏是否结束的函数,以单人游戏为例。首先用for循环判断蛇头的横纵坐标是否在规定的范围内以及蛇头是否碰到蛇身,接着再用一个for循环判断是否碰到毒刺,以上条件符合一个,该函数就返回整数1,若两者都不返回,则返回0。在主函数中,用一个if语句,用此函数返回的值作为判断条件,若值为1,则执行退出游戏,显示结束画面的语句。
算法4:int stop(char dir)
{r=!r;
s=!s;
if(!s)
{so=dir;
return 98;}
else
{ int r=int(so);
return r;}
}
暂停函数,以单人游戏为例。在主函数所在的源文件设置一个bool类型的全局变量s,初始化为真。玩家在游戏中按下回车键时,执行暂停函数,执行语句s=!s,最后函数返回一个值。将暂停函数的返回值赋值给蛇的运动方向变量,使蛇不再运动。
在双人游戏中,设置了两个暂停函数,分别控制两条蛇的停止与运动。
算法五:
DIR=_getch();
switch (DIR)
{
case 97:
if(snake.direct[0]!=100)
snake.direct[0]=97;
break;
case 100:
if(snake.direct[0]!=97)
snake.direct[0]=100;
break;
case 119:
if(snake.direct[0]!=115)
snake.direct[0]=119;
break;
case 115:
if(snake.direct[0]!=119)
snake.direct[0]=115;
break;
case 13:
snake.direct[0]=stop(snake.direct[0]);
break;
default:
break;
换取按键信息,以单人游戏为例。用swith判断语句对不同的按键信息,另设做出上移、下移、左移、右移、暂停/开始等动作。
算法六: c=(score/10)%5; b=(score/10)/5;
if(speed!=75)
{if(c==0&&b==m+1)
{speed-=25;
level++;
m++;}
}
char bu[10];
itoa(level,bu,10);
textout(handle,72,8,wColors,1,bu);
增加蛇的移动速度及显示等级的算法,以单人游戏为例。类似毒刺产生的原理,设置两个全局变量,当这两个变量满足一定条件时,游戏等级增加一个单位,相应地蛇的速度增加。等级的最高限度为五级,防止贪吃蛇运动过快。
2.3 程序设计的难点和关键点
一、在游戏运行的同时,播放背景音乐,运用到多线程的知识。
二、在双人游戏中,分别编写两条蛇的运动算法,但两条蛇需要同时获取按键信息,这样两者才能同时运动。
三、设置蛇移动的算法时,让后一个点跟随前一个点的运动轨迹,原来最后一个点的位置就显示背景颜色,使得蛇在整体上看上去一偶那个了一格。
四、蛇的得分增加一定程度
2.4 调试的方法
[过程中出现的问题和解决方法]
在编该游戏的过程中,出现了各种问题和错误,后来都一一解决了。
一、开始设置游戏等级时,当蛇满足一个升级的条件时,蛇却停止了运动,而不是像预想的那样加快运动速度。后来发现原来是因为我用的if判断语句的判断条件出现问题,导致蛇的速度会一直减小直到零。我借鉴产生毒刺的算法,设置判断条件,使得蛇每次增加速度都都能继续稳定运行。
二、开始加入背景音乐时,贪吃蛇死亡后,不显示游戏结束的画面,而是停留在原画面中继续播放音乐。后来我设置了一个bool类型的全局变量r,把原来背景音乐函数中的无限循环的判断条件“1”换成r,当游戏结束时r变为假,使音乐不再播放。
三、刚设置双人游戏时,几乎需要把单人游戏中的每一个功能对应的函数再设置一个新的,以适应双人游戏的要求。若不仔细检查,其中微小的错误在程序编译时无法发现,但运行游戏却会出现问题。所以将单人游戏改成双人也需要极大的耐心和细致。
四、在增加毒刺和游戏等级的算法中,采用了类似的程序,都是设置两个变量,当这两个变量分别满足整除和取余的相应条件时,才执行添加毒刺或增加等级的算法。
2.5 程序性能评价
从运行效果上进行分析:
优点:添加背景颜色和背景音乐,使游戏画面更细腻绚丽。同时增加了规避毒刺、等级上升、双人游戏等功能,更具娱乐性。
不足:背景音乐单一,双人游戏的蛇头部分闪烁不同步。
从程序设计上进行分析:
优点:运用多个功能函数,且单人游戏与双人游戏分别对应,条例清晰。
不足:程序的设计没有运用到类的知识。
三.心得体会
经过这次小学期的编程,使我很好地复习回顾了一遍C++的相关知识,同时对编程有了更为深刻的了解。
编译一个好的游戏,首先要考虑游戏的界面和操作是否友好方便,其次要尽量精简程序,增加程序的可读性,提高运算速度。好的创意和想法是十分关键的,这不是书本能交给我们的,需要我们独立思考,交流取经。
小学期的编程很好地锻炼了我的全局统筹以及逻辑思维的能力,也培养了严谨、细心、耐心、专注等优良素质。这次编写游戏的经历,使我认识了一个深刻的道理——任何事情要做好,都需要付出极大的努力。即使是一个简单得不想玩的小游戏,编译起来也如此费神,可以想象编写一个软件、一个操作系统需要的惊人的人力物力。
这次小学期里我的收获很大。在以后的学习中我一定要继续努力,端正态度,取得更好的成绩。
第二篇:贪食蛇游戏实习报告
贪吃蛇游戏开发
一、需求分析
1、该贪吃蛇设计主要具有吃食、暂停、开始以及重新开始游戏。
2、主要目标:本游戏实现贪吃蛇在制定空间去吃随机出现的方块。游戏可以暂停,如不满意这一次游戏的成果还能重新开始游戏。
二、项目设计
1、流程设计
程序流程图
2、游戏具体设计:
(1) 蛇移动的实现
蛇的移动方向主要受头结点控制,所以每次画蛇时只要把前一结点的值赋给后 一节点即可。 当蛇头的移动方向向右时,y 值不变 x 的值不断增加。 当蛇头的移动方向向左时,y 值不变 x 的值不断减少。 当蛇头的移动方向向下时,y 值增加 x 的值不变。 当蛇头的移动方向向上时,y 值减小 x 的值不变。
(2) 蛇死亡的判断 当蛇头的值与蛇身或与墙的值相等时,蛇即算死亡.
(3)蛇到达边框时从另一端出现和传送的实现 当蛇头的 x 或 y 值达到边框的最大或最小位置时相应的给 x 或 y 赋以边框的最 小或最大值以实现当蛇到达边框时能从另一端出来,同理当蛇头的 x,y 值等于传 送点位置的值时将蛇头 x,y 的值赋以被传送到位置的值,这样将实现蛇的传送功 能。
三、项目实现
①蛇身颜色、最大长度及速度
int speed;//设置蛇的速度
int maxLen;//设置蛇的最大长度
②随机生成食物
产生随机种子:
qsrand(QTime().currentTime().msec());
定义食物的坐标、形状及颜色:
int mx = (qrand()%(w/20))*20;
int my = (qrand()%(h/20))*20;
food = new QLabel(this);
food->move(mx,my);
food->resize(20,20);//食物大小
food->setAutoFillBackground(true);//设置食物自动填充背景颜色
food->setFrameShape(QFrame::Box);//设置形状
food->setFrameShadow(QFrame::Sunken);
food->setPalette(QPalette(
QColor(qrand()%255,qrand()%110,qrand()%255)));//改变调色板的颜色
return food;
③方向控制及按键事件处理
方向控制:
switch(d)
{
case d_up:
nheady-=speed;break;
case d_down:
nheady+=speed;break;
case d_left:
nheadx-=speed;break;
case d_right:
nheadx+=speed;break;
}
按键事件处理:
void snake::keyPressEvent(QKeyEvent *f)
{
if(f->key()==Qt::Key_Up)
{
d=d_up;
}
else if(f->key()==Qt::Key_Down)
{
d=d_down;
}
else if(f->key()==Qt::Key_Left)
{
d=d_left;
}
else if(f->key()==Qt::Key_Right)
{
d=d_right;
}
}
④食物的显示与蛇吃食物
食物显示:
data.push_back(getFood());//将吃到的食物添加在蛇身之后
timer = new QTimer();//初始化时钟
timer->setInterval(400);//设置时钟周期
timer->start();
connect(timer,SIGNAL(timeout()),this,SLOT(move()));//传递信号
food = getFood();
food->show();
蛇吃食物:
if((nheadx==foodx)&&(nheady==foody))
{
data.push_back(food);
food = getFood();
food->show();
}
游戏截图1
游戏截图2
游戏截图3
游戏截图4
⑤游戏结束条件
if(data.size()>=10)
{
QMessageBox msg(this);
msg.setText("game over!");
msg.setStandardButtons(QMessageBox::Yes|QMessageBox::No);
msg.show();
if(msg.exec()==QMessageBox::Yes)
{
this->close();
}
}
四、总结
经过10天的培训,让我进一步熟悉了Linux系统下命令的使用、文件的编写与调试,更熟悉了以前所学的C++语言,让我对我所学习的计算机更加了解喜欢,也更加希望自己能学好计算机方面的设计,去设计更多有意思的游戏。
也非常感谢培训老师这些天对我们的悉心教导,让我们学到了很多东西。
五、附录
1、主函数代码
#include <QtGui/QApplication>
#include "snake.h"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
snake w;
w.show();
return a.exec();
}
2、头文件代码
#ifndef SNAKE_H
#define SNAKE_H
#include <QDialog>
#include <QLabel>
#include <QList>
//枚举四个方向
enum Direction{d_up,d_down,d_left,d_right};
namespace Ui {
class snake;
}
class snake : public QDialog {
Q_OBJECT
private:
QLabel* food;//食物
QList<QLabel*> data;//列表
int speed;//设置蛇的速度
int maxLen;//设置蛇的最大长度
Direction d;//方向
QTimer* timer;//定时器计时
public:
snake(QWidget *parent = 0);
~snake();//虚构函数
QLabel* getFood();
public slots:
void move();
protected:
void changeEvent(QEvent *e);
void keyPressEvent(QKeyEvent *);//按键事件处理
void TimerEvent(QTimerEvent *);//定时器事件处理
private:
Ui::snake *ui;
};
#endif // SNAKE_H
3、功能实现代码
#include "snake.h"
#include "ui_snake.h"
#include <QMessageBox>
#include <QTimer>
#include <QTime>
#include <QKeyEvent>
#include <time.h>
#include <QColor>
#include <QPalette>
#include <QApplication>
#include <QFrame>
snake::snake(QWidget *parent) :
QDialog(parent),
ui(new Ui::snake)
{
ui->setupUi(this);
speed =20;//速度
qsrand(QTime().currentTime().msec());//产生随机种子
this->resize(400,400);//窗口大小
d = d_right;//默认方向
data.push_back(getFood());//将吃到的食物添加在蛇身之后
timer = new QTimer();//初始化时钟
timer->setInterval(400);//设置时钟周期
timer->start();
connect(timer,SIGNAL(timeout()),this,SLOT(move()));//传递信号
food = getFood();
food->show();
maxLen = 10;//蛇身最大长度
}
snake::~snake()
{
delete ui;
//从集合中移除位于指定索引位置的对象
while(data.size()>0)
{
delete data[0];
data.removeAt(0);
}
}
QLabel* snake::getFood()
{
//定义窗口的高度和宽度
int w = this->width();
int h = this->height();
//定义食物的坐标
int mx = (qrand()%(w/20))*20;
int my = (qrand()%(h/20))*20;
food = new QLabel(this);
food->move(mx,my);
food->resize(20,20);//食物大小
food->setAutoFillBackground(true);//设置食物自动填充背景颜色
food->setFrameShape(QFrame::Box);//设置形状
food->setFrameShadow(QFrame::Sunken);
food->setPalette(QPalette(
QColor(qrand()%255,qrand()%110,qrand()%255)));//改变调色板的颜色
return food;
}
void snake::move(){
//获取蛇头坐标
int nheadx = data[0]->x();
int nheady = data[0]->y();
//获取食物坐标
int foodx = food->x();
int foody = food->y();
//吃食物
if((nheadx==foodx)&&(nheady==foody))
{
data.push_back(food);
food = getFood();
food->show();
}
//设置结束条件
if(data.size()>=10)
{
QMessageBox msg(this);
msg.setText("game over!");
msg.setStandardButtons(QMessageBox::Yes|QMessageBox::No);
msg.show();
if(msg.exec()==QMessageBox::Yes)
{
this->close();
}
}
//按键事件
switch(d)
{
case d_up:
nheady-=speed;break;
case d_down:
nheady+=speed;break;
case d_left:
nheadx-=speed;break;
case d_right:
nheadx+=speed;break;
}
for(int i=data.size()-1;i>0;i--)
{
data[i]->move(data[i-1]->x(),data[i-1]->y());
}
data[0]->move(nheadx,nheady);
}
//按键事件处理
void snake::keyPressEvent(QKeyEvent *f)
{
if(f->key()==Qt::Key_Up)
{
d=d_up;
}
else if(f->key()==Qt::Key_Down)
{
d=d_down;
}
else if(f->key()==Qt::Key_Left)
{
d=d_left;
}
else if(f->key()==Qt::Key_Right)
{
d=d_right;
}
}
//时钟事件处理
void snake::TimerEvent(QTimerEvent *)
{
move();
}
void snake::changeEvent(QEvent *e)
{
QDialog::changeEvent(e);
switch (e->type())
{
case QEvent::LanguageChange:
ui->retranslateUi(this);
break;
default:
break;
}
}