计算机软件实习项目二

贪吃蛇的实现

要求:

(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();
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值