目录
项目需求
- 实现贪吃蛇游戏基本功能,屏幕上随机出现一个“食物”,称为豆子,上下左右控制“蛇”的移动,吃到“豆子”以后“蛇”的身体加长一点;
- “蛇”碰到边界或蛇头与蛇身相撞,蛇死亡,游戏结束;
- 为游戏设计友好的交互界面;例如欢迎界面,游戏界面,游戏结束界面。要有开始键、暂停键和停止退出的选项;
- 对蛇吃到豆子进行分值计算,可以设置游戏速度,游戏音乐等拓展元素。
项目准备
- 仔细思考分析项目需求,生成项目的总体框架;
- 解决如何生成食物、蛇的移动、碰撞等问题,学习相关算法;
- 构造合适的界面UI;
- 思考拓展部分,比如双人模式,添加得分,移动速度等等。
项目框架
关键算法分析
食物的生成
主体功能是随机生成食物的行列坐标值,但不能出现在蛇身中。如果出现在蛇身中,需要重新生成。除此以外,为保证游戏的激情, 生成食物的速度会根据得分逐渐加快。
//判断是否吃到食物
if(snake[0][0]==foodx&&snake[0][1]==foody)
{
//吃到食物发出声音
sound->play();
//如果吃到食物,食物坐标更新
int i=0;
foodcount++;
score++;
str1=QString::number(score);
scoreshow->setText(str1);
QTime t;
t= QTime::currentTime();
qsrand(t.msec()+t.second()*1000);
foodx=qrand()%COL;
foody=qrand()%ROW;
while(i<=foodcount)
{
i++;
//判断食物是否出现在蛇身上,如果出现在蛇身上重新产生食物
if(snake[i][0]==foodx&&snake[i][1]==foody)
{
foodx=qrand()%COL;
foody=qrand()%ROW;
i=0;
}
}
switch(score)
{
case 1:timer->setInterval(180);level++;break;
case 10:timer->setInterval(160);level++;break;
case 20:timer->setInterval(140);level++;break;
case 30:timer->setInterval(120);level++;break;
}
}
详细设计
蛇的移动
通过不断更改坐标值来实现。
//实现蛇1的游动
for(int i=foodcount;i>=1;i--)
{
snake[i][0]=snake[i-1][0];
snake[i][1]=snake[i-1][1];
}
switch(direction)
{
case UP:snake[0][1]--;break;
case DOWN:snake[0][1]++;break;
case LEFT:snake[0][0]--;break;
case RIGHT:snake[0][0]++;break;
}
//蛇2的游动
for (int i_two=foodcount_two;i_two>=1;i_two--)
{
snake_two[i_two][0]=snake_two[i_two-1][0];
snake_two[i_two][1]=snake_two[i_two-1][1];
}
switch(direction_two)
{
case UP_TWO:snake_two[0][1]--;break;
case DOWN_TWO:snake_two[0][1]++;break;
case LEFT_TWO:snake_two[0][0]--;break;
case RIGHT_TWO:snake_two[0][0]++;break;
}
判断蛇头是否对撞
通过坐标是否重合来判断,分为了两种情况。
for (int i =1;i<=foodcount;i++){
if(snake_two[0][0]==snake[i][0]&&snake_two[0][1]==snake[i][1]){
sound1->play();
if(QMessageBox::question(this,"提示","玩家2被击杀",QMessageBox::Yes,QMessageBox::No)==QMessageBox::Yes)
{
delete this;
return ;
}
}
}
for (int i =1;i<=foodcount_two;i++){
if(snake[0][0]==snake_two[i][0]&&snake[0][1]==snake_two[i][1]){
sound1->play();
if(QMessageBox::question(this,"提示","玩家1被击杀",QMessageBox::Yes,QMessageBox::No)==QMessageBox::Yes)
{
delete this;
return ;
}
}
}
判断蛇是否撞到墙体
游戏整体设计均为无法穿越墙体,通过蛇头坐标值与墙体行列的比较来实现
if(snake[0][0]<0||snake[0][0]>=COL||snake[0][1]<0||snake[0][1]>=ROW)
{
sound1->play();
timer->stop();
if(QMessageBox::question(this,"提示","玩家1撞墙,玩家2胜利!",QMessageBox::Yes,QMessageBox::No)==QMessageBox::Yes)
{delete this;
return ;}
}
if(snake_two[0][0]<0||snake_two[0][0]>=COL||snake_two[0][1]<0||snake_two[0][1]>=ROW){
sound1->play();
timer->stop();
if(QMessageBox::question(this,"提示","玩家2撞墙,玩家1胜利!",QMessageBox::Yes,QMessageBox::No)==QMessageBox::Yes)
{delete this;
return ;}
}
成品展示
项目总结
在本实验项目中,我认为最难的部分在于设计蛇吃了一个食物之后身体的增长,主要是坐标的处理,为简化这个问题我才决定将游戏设置为碰到墙就结束游戏,个人觉得穿墙有点复杂;其次是食物的生成,以及两蛇相撞的设计,食物生成不仅要确保不生成在蛇身体上,还要确保随着玩家得分的增加,食物生成以及蛇移动速度的增加。
除此之外,我觉得这个最基本的游戏可以融入很多元素和技术,比如自动吃食物以及角色的选择和历史记录的保存,但对应都要有ui界面的设计,比较复杂。