贪吃蛇的实现
要求:
(1)实现贪吃蛇游戏基本功能,屏幕上随机出现一个“食物”,称为豆子。玩家能利用上下左右键控制“蛇”的移动,“蛇”吃到“豆子”后“蛇”身体加长一节,得分增加,“蛇”碰到边界或蛇头与蛇身相撞,“蛇”死亡,游戏结束。
(2)进行交互界面的设计,要有开始键、暂停键和停止退出的选项,能够控制游戏进程。对蛇吃到豆子进行分值计算,可以设置游戏速度,游戏音乐等拓展元素。
需要的知识:QT基础知识,基本算法知识
需要解决的问题:
1.如何存储蛇:将蛇存储为一个队列,其中放入矩形元素;
2.如何在屏幕上画出蛇:通过QT的printEvent来处理画图
3.如何让蛇移动和控制难度:设置一个定时器,定时器超时就会调用相关函数,可以通过定时器设置的时间来控制难度
void Widget::timeOut()
{
int flage = 1;
for(int i=0; i<rewardpoint.length(); i++){
for(int j=0;j<3;j++){
if(rewardpoint.at(i).contains(snake.at(j).topLeft()+QPointF(snakeNodeWidth/2,snakeNodeHeight/2))){
if(rewardpoint.at(i).width()>snakeNodeWidth){
flage += 2;
}
flage++;
rewardpoint.removeAt(i);
break;
}
}
}
while(flage--){
switch (moveFlage) {
case Up:
MOVEUP();
break;
case Down:
MOVEDOWN();
break;
case Right:
MOVERIGHT();
break;
case Left:
MOVELEFT();
break;
default:
break;
}
}
deleteLastRectF();
update();
}
4.如何生成食物?
通过一个定时器,定时器超时后进行食物的生成,但是要避免食物生成到蛇的身上,需要一个检验函数
bool Widget::check(int x,int y){
for(int i=0;i<snake.length();i++){
int x1=snake[i].topLeft().x();
int x2=snake[i].bottomRight().x();
int y1=snake[i].topLeft().y();
int y2=snake[i].bottomRight().y();
int tmp;
if(x1>x2)tmp=x1,x1=x2,x2=tmp;
if(y1>y2)tmp=y1,y1=y2,y2=tmp;
if(x1<=x&&x<=x2&&y>=y1&&y<=y2)
return false;
}
return true;
}
生成食物定时器超时:
void Widget::rewardTimeOut()
{
qsrand(QTime(0,0,0).secsTo(QTime::currentTime()));
if(rewardpoint.length() > 5){
rewardpoint.removeAt(qrand()%5);
}
int xlittle=qrand()%(this->width()/20)*20,ylittle=qrand()%(this->height()/20)*20+50;
if(check(xlittle,ylittle))
rewardpoint.append(QRectF(xlittle,ylittle,snakeNodeWidth,snakeNodeWidth));
if(qrand()%5 == 3){
int xbig=qrand()%(this->width()/20)*20-5,ybig=qrand()%(this->height()/20)*20-5+50;
if(check(xbig,ybig))
rewardpoint.append(QRectF(xbig,ybig,snakeNodeWidth*2,snakeNodeWidth*2));
}
}
5.如何判断游戏结束?
枚举蛇的每一部分,检查是否碰撞
bool Widget::snakeStrike()
{
for(int i=0; i<snake.length(); i++){
for(int j=i+1; j<snake.length(); j++){
if(snake.at(i) == snake.at(j)){
return true;
}
}
}
return false;
}
6.如何让蛇朝一个方向移动?
本质时在蛇的队列中插入新元素,然后删除队列最后的元素,这里以向上移动为例:
void Widget::MOVEUP()
{
if(snake.at(0).y()-snakeNodeHeight < 50){//超出上边界时
snake.insert(0,QRectF(QPointF(snake.at(0).x(),this->height()-snakeNodeHeight),
QPointF(snake.at(0).x()+snakeNodeWidth,this->height())));
}else{//其他情况
snake.insert(0,QRectF(snake.at(0).topLeft()+QPointF(0,-snakeNodeHeight),snake.at(0).topRight()));
}
}
删除最后的元素:
void Widget::deleteLastRectF()
{
snake.removeLast();
}
贪吃蛇运行主要界面:
完整代码:
widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <QKeyEvent>
#include <QRectF>
#include <QPainter>
#include <QPen>
#include <QBrush>
#include <QDebug>
#include <QTimer>
#include <QTime>
#include <QPushButton>
#include <QPlainTextEdit>
#include <QString>
#include <QLabel>
#include <QComboBox>
namespace Ui {
class Widget;
}
class Widget : public QWidget // 继承QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = 0);
~Widget();
protected:
void paintEvent(QPaintEvent *);
void keyPressEvent(QKeyEvent *);
private:
void MOVEUP(); // 上
void MOVEDOWN(); // 下
void MOVELEFT(); // 左
void MOVERIGHT(); // 右
void deleteLastRectF(); // 删除last数据
bool snakeStrike(); // 判断是否相撞
bool check(int x,int y);
enum Move{Left,Right,Up,Down};
protected slots:
void timeOut();
void rewardTimeOut();
void on_comboBoxChanged(const QString &arg1);
void on_btnstart_pushed();
void on_btnpause_pushed();
void on_btnexit_pushed();
private:
Ui::Widget *ui;
QPushButton* btnstart,*btnpause,*btnexit;
QList<QRectF> snake;
int snakeNodeWidth = 10;
int snakeNodeHeight = 10;
QTimer *timer;
QTimer *rewardTimer;
QLabel* labs;
QComboBox* combo;
int time = 80;
int moveFlage = Up;
bool gameOver = false;
bool gameStart = false;
QList<QRectF> rewardpoint;
};
#endif // WIDGET_H
widget.cpp
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent) :QWidget(parent),ui(new Ui::Widget)
{
ui->setupUi(this);
resize(600,500);
//setStyleSheet("QWidget{background:grey}");
labs=new QLabel(this);
labs->move(0,47);
labs->setFixedSize(600,5);
labs->setStyleSheet("QWidget{background:blue}");
combo=new QComboBox(this);
combo->move(310,11);
combo->setFixedSize(80,28);
combo->addItem("初级");
combo->addItem("中级");
combo->addItem("高级");
combo->addItem("地狱");
connect(combo,SIGNAL(currentIndexChanged(const QString &)),this,SLOT(on_comboBoxChanged(QString)));
btnstart=new QPushButton(this);
btnpause=new QPushButton(this);
btnexit=new QPushButton(this);
btnstart->move(400,10);
btnstart->setFixedSize(60,30);
btnstart->setText("开始");
btnpause->move(465,10);
btnpause->setFixedSize(60,30);
btnpause->setText("暂停");
btnexit->move(530,10);
btnexit->setFixedSize(60,30);
btnexit->setText("退出");
connect(btnstart,SIGNAL(clicked()),this,SLOT(on_btnstart_pushed()));
connect(btnpause,SIGNAL(clicked()),this,SLOT(on_btnpause_pushed()));
connect(btnexit,SIGNAL(clicked()),this,SLOT(on_btnexit_pushed()));
this->setFocus();
snake.append(QRectF(200,500,snakeNodeWidth,snakeNodeHeight));
MOVEUP();
MOVEUP();
rewardpoint.append(QRectF(400,200,snakeNodeWidth,snakeNodeWidth));
timer = new QTimer;
connect(timer, SIGNAL(timeout()),this,SLOT(timeOut()));
rewardTimer = new QTimer;
connect(rewardTimer,SIGNAL(timeout()),this,SLOT(rewardTimeOut()));
this->setWindowTitle("贪吃蛇");
}
void Widget::on_btnstart_pushed(){
snake.clear();
rewardpoint.clear();
moveFlage = Up;
snake.append(QRectF(200,500,snakeNodeWidth,snakeNodeHeight));
MOVEUP();
MOVEUP();
rewardpoint.append(QRectF(100,100,snakeNodeWidth,snakeNodeWidth));
timer->start(time);
rewardTimer->start(time*30);
gameOver = false;
this->setFocus();
return;
}
void Widget::on_btnpause_pushed(){
timer->stop();
rewardTimer->stop();
this->setFocus();
return;
}
void Widget::on_btnexit_pushed(){
this->close();
this->setFocus();
return;
}
void Widget::on_comboBoxChanged(const QString &dat){
if(dat=="初级"){
time=80;
}else if(dat=="中级"){
time=60;
}else if(dat=="高级"){
time=40;
}else if(dat=="地狱"){
time=20;
}
this->setFocus();
return;
}
Widget::~Widget()
{
delete ui;
}
void Widget::timeOut()
{
int flage = 1;
for(int i=0; i<rewardpoint.length(); i++){
for(int j=0;j<3;j++){
if(rewardpoint.at(i).contains(snake.at(j).topLeft()+QPointF(snakeNodeWidth/2,snakeNodeHeight/2))){
if(rewardpoint.at(i).width()>snakeNodeWidth){
flage += 2;
}
flage++;
rewardpoint.removeAt(i);
break;
}
}
}
while(flage--){
switch (moveFlage) {
case Up:
MOVEUP();
break;
case Down:
MOVEDOWN();
break;
case Right:
MOVERIGHT();
break;
case Left:
MOVELEFT();
break;
default:
break;
}
}
deleteLastRectF();
update();
}
bool Widget::check(int x,int y){
for(int i=0;i<snake.length();i++){
int x1=snake[i].topLeft().x();
int x2=snake[i].bottomRight().x();
int y1=snake[i].topLeft().y();
int y2=snake[i].bottomRight().y();
int tmp;
if(x1>x2)tmp=x1,x1=x2,x2=tmp;
if(y1>y2)tmp=y1,y1=y2,y2=tmp;
if(x1<=x&&x<=x2&&y>=y1&&y<=y2)
return false;
}
return true;
}
void Widget::rewardTimeOut()
{
qsrand(QTime(0,0,0).secsTo(QTime::currentTime()));
if(rewardpoint.length() > 5){
rewardpoint.removeAt(qrand()%5);
}
int xlittle=qrand()%(this->width()/20)*20,ylittle=qrand()%(this->height()/20)*20+50;
if(check(xlittle,ylittle))
rewardpoint.append(QRectF(xlittle,ylittle,snakeNodeWidth,snakeNodeWidth));
if(qrand()%5 == 3){
int xbig=qrand()%(this->width()/20)*20-5,ybig=qrand()%(this->height()/20)*20-5+50;
if(check(xbig,ybig))
rewardpoint.append(QRectF(xbig,ybig,snakeNodeWidth*2,snakeNodeWidth*2));
}
}
// 向上移动
void Widget::MOVEUP()
{
if(snake.at(0).y()-snakeNodeHeight < 50){
snake.insert(0,QRectF(QPointF(snake.at(0).x(),this->height()-snakeNodeHeight),
QPointF(snake.at(0).x()+snakeNodeWidth,this->height())));
}else{
snake.insert(0,QRectF(snake.at(0).topLeft()+QPointF(0,-snakeNodeHeight),snake.at(0).topRight()));
}
}
// 向下移动
void Widget::MOVEDOWN()
{
if(snake.at(0).y()+snakeNodeHeight*2 > this->height()){
snake.insert(0,QRectF(QPointF(snake.at(0).x(),snakeNodeHeight+50),
QPointF(snake.at(0).x()+snakeNodeWidth,50)));
}else{
snake.insert(0,QRectF(snake.at(0).bottomLeft(),snake.at(0).bottomRight()+QPointF(0,snakeNodeHeight)));
}
}
// 向左移动
void Widget::MOVELEFT()
{
if(snake.at(0).x()-snakeNodeWidth < 0){
snake.insert(0,QRectF(QPointF(this->width()-snakeNodeWidth,snake.at(0).y()),
QPointF(this->width(),snake.at(0).y()+snakeNodeHeight)));
}else{
snake.insert(0,QRectF(snake.at(0).topLeft()+QPointF(-snakeNodeWidth,0),snake.at(0).bottomLeft()));
}
}
void Widget::MOVERIGHT()
{
if(snake.at(0).x()+snakeNodeWidth*2 > this->width()){
snake.insert(0,QRectF(QPointF(0,snake.at(0).y()),
QPointF(snakeNodeWidth,snake.at(0).y()+snakeNodeHeight)));
}else{
snake.insert(0,QRectF(snake.at(0).topRight(),snake.at(0).bottomRight()+QPointF(snakeNodeWidth,0)));
}
}
void Widget::deleteLastRectF()
{
snake.removeLast();
}
void Widget::paintEvent(QPaintEvent *event)
{
QPainter painter(this);
QPen pen;
QBrush brush;
QFont fontg ( "Microsoft YaHei", 20, 50);
painter.setRenderHint(QPainter::Antialiasing);
pen.setColor(Qt::black);
brush.setColor(Qt::lightGray);
brush.setStyle(Qt::SolidPattern);
painter.setPen(pen);
painter.setBrush(brush);
for(int i=0; i<snake.length(); i++){
painter.drawRect(snake.at(i));
}
brush.setColor(Qt::red);
painter.setBrush(brush);
for(int i=0; i<rewardpoint.length(); i++){
painter.drawEllipse(rewardpoint.at(i));
}
pen.setColor(Qt::black);
painter.setPen(pen);
painter.setFont(fontg);
painter.drawText(20,35,QString("分数:")+QString("%1").arg(snake.length()));
if(snakeStrike()){
QFont font("方正舒体",30,QFont::ExtraLight,false);
painter.setFont(font);
painter.drawText((this->width()-300)/2,(this->height()-30)/2,QString("GAME OVER!"));
timer->stop();
rewardTimer->stop();
gameOver = true;
}
QWidget::paintEvent(event);
}
void Widget::keyPressEvent(QKeyEvent *event)
{
if(gameOver)return;
switch(event->key()){
case Qt::Key_Up:
if(moveFlage != Down){
timeOut();
moveFlage = Up;
}
break;
case Qt::Key_Down:
if(moveFlage != Up){
timeOut();
moveFlage = Down;
}
break;
case Qt::Key_Right:
if(moveFlage != Left){
timeOut();
moveFlage = Right;
}
break;
case Qt::Key_Left:
if(moveFlage != Right){
timeOut();
moveFlage = Left;
}
break;
case Qt::Key_Enter:
case Qt::Key_Return:
if(gameOver){
snake.clear();
rewardpoint.clear();
moveFlage = Up;
snake.append(QRectF(200,500,snakeNodeWidth,snakeNodeHeight));
MOVEUP();
MOVEUP();
rewardpoint.append(QRectF(100,100,snakeNodeWidth,snakeNodeWidth));
timer->start(time);
rewardTimer->start(time*30);
gameOver = false;
}
break;
case Qt::Key_S:
if(gameStart && !gameOver){
timer->start(time);
rewardTimer->start(time*30);
gameStart = false;
}else if(!gameStart && !gameOver){
timer->stop();
rewardTimer->stop();
gameStart = true;
}
break;
default:
break;
}
}
// 判断是否相撞
bool Widget::snakeStrike()
{
for(int i=0; i<snake.length(); i++){
for(int j=i+1; j<snake.length(); j++){
if(snake.at(i) == snake.at(j)){
return true;
}
}
}
return false;
}
main.cpp
#include "widget.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Widget w;
w.show();
return a.exec();
}