一、游戏项目具体功能及演示
1.1具体功能
1.蛇的初始化;
2.控制蛇上下左右移动;
3.随机生成食物;
4.蛇吃食物,蛇身变长;
5.蛇头撞蛇尾即游戏结束;
6.计分功能;
7.简单QPushButton使用
1.2 游戏演示
1.2.1 游戏主界面
游戏界面由三个按键分别实现:开始游戏、暂停游戏和重新开始,如图1所示。
图1 游戏主界面
1.2.2 游戏进行时
游戏进行时会随机刷新食物的位置,当蛇头与食物相交蛇身变长并分数板进行加分,如图2所示。
图2 游戏进行时
1.2.3 游戏结束
当蛇头撞到蛇身即游戏结束,点击重新开始游戏重开,分数板归零,如图3所示。
图3 游戏结束
二、涉及到QT相关内容
1.QT定时器的使用;
2.键盘事件类;
3.画家、画笔、画刷等绘图相关类;
4.信号与槽机制。
三、代码实现
代码主要由widget.h头文件和widget.cpp主要功能实现组成。
3.1 首先引入头文件,涉及到按键事件、定时器、绘画事件等;
#include <QKeyEvent>
#include <QWidget>
#include <QTimer>
#include <QPaintEvent>
#include <QPainter>
#include <iostream>
#include <QDateTime>
#include <QLabel>
#include <QElapsedTimer>
3.2头文件widget类中添加 绘画事件、按键操作、蛇的一系列操作声明;这类操作函数均放置在protected权限中
protected:
//渲染、绘图 事件
void paintEvent(QPaintEvent *event);
//1.按键处理
//
void keyPressEvent(QKeyEvent *event);
// 贪吃蛇吃食物,变长
void addTop();//蛇顶部加长
void addDown();//蛇底部加长
void addRight();//蛇右部加长
void addLeft();//蛇左部加长
void deleteLast();//蛇尾部删除
void reStarGame();//重新开始游戏
3..3枚举蛇的移动方向
typedef enum Direct{
DIR_LEFT,
DIR_RIGHT,
DIR_UP,
DIR_DOWN
}dir_t;
3.4widget.h 头文件完整代码
#ifndef WIDGET_H
#define WIDGET_H
#include <QKeyEvent>
#include <QWidget>
#include <QTimer>
#include <QPaintEvent>
#include <QPainter>
#include <iostream>
#include <QDateTime>
#include <QLabel>
#include <QElapsedTimer>
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE
//0、蛇的表示 可以用vector、list
//
//1.按键处理
//2.使用定时器
//2.1 关联信号槽
//2.2 启动定时器
//2.3 实现对应的超时处理函数
//3.渲染
typedef enum Direct{
DIR_LEFT,
DIR_RIGHT,
DIR_UP,
DIR_DOWN
}dir_t;
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
protected:
//渲染、绘图 事件
void paintEvent(QPaintEvent *event);
//1.按键处理
//
void keyPressEvent(QKeyEvent *event);
// 贪吃蛇吃食物,变长
void addTop();
void addDown();
void addRight();
void addLeft();
void deleteLast();
void updateScore(int timeAliveSeconds, int snakeLength);
private:
Ui::Widget *ui;
dir_t moveFlag=DIR_UP;//方向标志
bool gameStart = false;//游戏开始标志
//定时器
QTimer *timer;
QTimer *timer2;
int time = 100;//ms
int time1 = 1000;
//蛇
QList<QRectF>snake; //<QRectF>表示区域
//小方块的大小 宽高
int nodeWidth = 20;
int nodeHeight = 20;
//蛇的食物
QRectF rewardNode;
void addNewReword();
bool checkContact();
//记录开始的时间
QDateTime startTime; // 记录游戏开始的时间
QLabel *scoreLabel;
int score;
int initialLength ; // 蛇的初始长度
QElapsedTimer elapsedTimer; // 计时器,用于跟踪游戏运行时间
protected slots:
void timeout();
void timeout2();
};
#endif // WIDGET_H
3.6 widget.cpp 完整代码
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
//使用定时器
timer=new QTimer();
//定时器有一个超时信号timer函数
//信号槽(信号和对应的函数关联)
connect(timer,SIGNAL(timeout()),this,SLOT(timeout()));
// resize(600,300);
//定时器2
timer2 =new QTimer();
connect(timer2,SIGNAL(timeout2()),this,SLOT(timeout2()));
setFixedSize(600, 300); // 设置窗口大小为宽400像素,高300像素
//初始化蛇身
QRectF rect(300,150,nodeWidth,nodeHeight);
snake.append(rect);
addTop();
addTop();
//初始化蛇的食物
addNewReword();
score = 0;
// elapsedTimer.start();//游戏时间开始计时
// //在构造函数中初始化 QLabel
// scoreLabel = new QLabel(this);//()中需要添加this指针,指向对象本身
// scoreLabel->setText("Score:0");
// scoreLabel->setFont(QFont("Arial",12));
// scoreLabel->setGeometry(50, 50, 150, 30); // 设置位置和大小
// scoreLabel->show(); // 显示 QLabel
}
Widget::~Widget()
{
delete ui;
}
//控制方向
void Widget::keyPressEvent(QKeyEvent *event)
{
//需要添加#include <QKeyEvent>头文件 否则报错
switch((event ->key())) {
case Qt::Key_Up:
if(moveFlag!=DIR_DOWN) //贪吃蛇上下行动时,需要判断,不然会撞死
{
moveFlag=DIR_UP;
}
break;
case Qt::Key_Down:
if(moveFlag!=DIR_DOWN){
moveFlag=DIR_DOWN;
}
break;
case Qt::Key_Left:
if(moveFlag!=DIR_RIGHT)
{
moveFlag=DIR_LEFT;
}
break;
case Qt::Key_Right:
if(moveFlag!=DIR_LEFT){
moveFlag=DIR_RIGHT;
}
break;
case Qt::Key_Space:
if(gameStart == false)
{
gameStart = true;
//启动定时器
timer->start(time);
timer2->start(time1);
}
else
{
gameStart = false;
timer->stop();//停止定时器1
timer2->stop();//停止定时器1
}
break;
default:
break;
}
}
void Widget::timeout()
{
int count=1;
//判断蛇是否吃到食物
if(snake[0].intersects(rewardNode))
{
count++;
addNewReword();
score++;
}
while(count--){
switch (moveFlag) {
case DIR_UP:
//在顶部在方块
addTop();
break;
case DIR_DOWN:
addDown();
break;
case DIR_LEFT:
addLeft();
break;
case DIR_RIGHT:
addRight();
break;
default:
break;
}
}
deleteLast();
update();
}
void Widget::timeout2()//计分板定时器
{
int currentLength = snake.size();
// 计算经过的时间(秒)
qint64 elapsedTime = elapsedTimer.elapsed();
int seconds = elapsedTime / 1000;
// 根据公式计算分数
score = seconds * 1 + (currentLength - initialLength) * 10;
// 其他游戏逻辑(移动蛇身、检查碰撞等)
// 更新显示
update();
}
void Widget::addTop()
{//QPointF 点类, 有关点的操作
QPointF leftTop;
QPointF rightBotom;
if(snake[0].y()-nodeHeight<0)
{
leftTop = QPoint(snake[0].x(),this->height()-nodeHeight);//this->height窗口的高度,与
rightBotom = QPointF(snake[0].x()+nodeWidth,this->height());
}
else{
leftTop =QPointF(snake[0].x(),snake[0].y()-nodeHeight);//
rightBotom = snake[0].topRight();
}
snake.insert(0,QRectF(leftTop,rightBotom));//左上角与右下角可确定位置
}
void Widget::addDown()
{
QPointF leftTop;
QPointF rightBotom;
if(snake[0].y()+nodeHeight*2 > this->height()) //蛇到底部
{
leftTop = QPointF(snake[0].x(),0);
rightBotom = QPointF(snake[0].x()+nodeWidth,nodeHeight);
}else{
leftTop = snake[0].bottomLeft();
rightBotom = snake[0].bottomRight()+QPointF(0,nodeHeight);
}
snake.insert(0,QRectF(leftTop,rightBotom));
}
void Widget::addLeft()
{
QPointF leftTop;
QPointF rightBotom;
if(snake[0].x() - nodeWidth <0)
{
leftTop = QPointF(this->width() - nodeWidth,snake[0].y());
}else{
leftTop = snake[0].topLeft()-QPointF(nodeWidth,0);
}
rightBotom = leftTop + QPointF(nodeWidth,nodeHeight);
snake.insert(0,QRectF(leftTop,rightBotom));
}
void Widget::addRight()
{
QPointF leftTop;
QPointF rightBotom;
if(snake[0].x()+ nodeWidth*2 > this->width())
{
leftTop = QPointF(0,snake[0].y());
}else{
leftTop = snake[0].topRight();
}
rightBotom = leftTop + QPointF(nodeWidth,nodeHeight);
snake.insert(0,QRectF(leftTop,rightBotom));
}
void Widget::paintEvent(QPaintEvent *event)
{
//画家类 画笔
QPainter painter(this);
//背景图片
QPixmap pix;
pix.load("C:/Users/ASUS/Documents/snakes0718/snake.jpeg");
painter.drawPixmap(0,0,600,300,pix);
//画蛇 画笔
QPen pen;
pen.setColor(Qt::black);
//画刷
QBrush brush;
brush.setColor(Qt::darkMagenta);
brush.setStyle(Qt::SolidPattern);
painter.setPen(pen);
painter.setBrush(brush);
for(int i=0;i<snake.length();i++)
{
painter.drawRect(snake[i]);
}
//画蛇的奖品
pen.setColor(Qt::red);
brush.setColor(Qt::red);
brush.setStyle(Qt::SolidPattern);
brush.setColor(Qt::red);
brush.setStyle(Qt::SolidPattern);
painter.setPen(pen);
painter.setBrush(brush);
// painter.drawRect(rewardNode); 画方形
painter.drawEllipse(rewardNode);
// 绘制分数
QFont font("方正舒体", 16, QFont::ExtraLight, false); // 设置字体
painter.setFont(font);
painter.drawText(10, 20, QString("分数: %1").arg(score)); // 绘制分数文本
if(checkContact())
{
QFont font("方正舒体",30,QFont::ExtraLight,false);
painter.setFont(font);
painter.drawText((this->width()-300)/2,
(this->height()-30)/2,
QString ("游戏结束"));
timer->stop();
}
QWidget::paintEvent(event);
}
void Widget::deleteLast()
{
snake.removeLast();
}
void Widget::addNewReword()
{
rewardNode =QRectF(
qrand()%(this->width()/20)*20,qrand()%(this->height()/20)*20,
nodeWidth,
nodeHeight);
}
bool Widget::checkContact()
{
for(int i=0;i<snake.length();i++)
{
for(int j=i+1;j<snake.length();j++)
{
if(snake[i] == snake[j])
{
return true;
}
}
}
return false;
}