首先说一下实现思路:
四个类
蛇 、食物、块 、Widget(主类)
可以将蛇看成一个一个的块的组成 。
块类 功能
记录当前坐标 、上一个块、下一个块的
蛇类 功能
记录蛇的位置(蛇头和蛇身)、 移动、吃、 换方向
食物类 功能
记录食物出现位置。以及要出现的位置
Widget主类
Widget主类相当于上帝,在这个类里面需要实现 创建一条蛇,创建一个食物,在蛇移动的时候观察是否碰到墙上、是否吃到了食物、还有是否玩家对蛇进行了操作(换方向)
block.h
#ifndef BLOCK_H
#define BLOCK_H
class block
{
public:
block(int x,int y);
int x ; // x y 记录这个块的坐标
int y ;
block *next; // 记录下一个块
block * prev; // 记录上一个块
};
#endif // BLOCK_H
block.cpp
#include "block.h"
block::block(int x,int y )
{
this->x=x;
this->y=y;
}
snake.h
#ifndef SNAKE_H
#define SNAKE_H
#include "block.h"
#include <QPen>
#include <QPainter>
typedef struct {
int x ;
int y ;
}dir;
class snake
{
public:
snake();
block * snakeHead; // 记录蛇头位置
block * snakeBody; // 记录蛇身位置 也可以看成是蛇的尾(yi)巴
bool isMove; // 是否已经移动过
void init();
void move();
void eat();
void setDir(char s);
char getS();
int getNextX();
int getNextY();
private:
char s;
dir direction;
};
#endif // SNAKE_H
snake.cpp
#include "snake.h"
#include <QDebug>
using namespace std;
snake::snake()
{
}
void snake::init()
{
//初始化蛇头
snakeHead = new block(1,0);
//初始化蛇尾
snakeBody = new block(0,0);
// 初始化方向
isMove=true; // 先设置可以移动用来进行初始化 第一步永远向右走一步
this->setDir('r');
snakeHead->next=this->snakeBody;
snakeHead->prev=nullptr;
snakeBody->prev=this->snakeHead;
snakeBody->next=nullptr;
}
int snake::getNextX(){
return snakeHead->x+this->direction.x*1;
};
int snake::getNextY(){
return snakeHead->y+(this->direction.y*1);
};
void snake::setDir(char s )
{
// 动一次之后才可以继续设置下一次的方向,不可以在动一次的过程中多次设置
if( isMove==false){ return ;} // 没有移动 不可以设置
isMove=false;
this->s=s;
switch (s) {
case 'u':
this->direction.x=0;
this->direction.y=-1;
break;
case 'd':
this->direction.x=0;
this->direction.y=1;
break;
case 'l':
this->direction.x=-1;
this->direction.y=0;
break;
case 'r':
this->direction.x=1;
this->direction.y=0;
break;
}
}
char snake::getS(){
return this->s;
}
void snake::move()
{
//擦去蛇尾
// 移动
int x = getNextX(); // 下一个节点的位置 x
int y = getNextY();// ;下一个节点的位置y
block *temp= snakeBody->prev; // 保存最后一个节点
temp->next=nullptr; // 设置最后一个节点的下一个为空
snakeBody->x=x;
snakeBody->y=y;
snakeBody->next= snakeHead;
snakeHead->prev=snakeBody;
snakeBody->prev=nullptr;//
snakeHead= snakeBody; //头节点替换
snakeBody = temp;
isMove=true;// 移动之后才可以设置方向
};
void snake::eat(){
int x = getNextX(); // 下一个节点的位置 x
int y = getNextY();// ;下一个节点的位置y
block *temp = new block(x,y);
temp->next=snakeHead;
temp->prev= nullptr;
snakeHead->prev=temp;
snakeHead=temp;
}
food.h
#ifndef FOOD_H
#define FOOD_H
#include<iostream>
class food
{
public:
food();
int x ;
int y ;
void init (int x, int y);
};
#endif // FOOD_H
food.cpp
#include "food.h"
#include <time.h>
using namespace std;
food::food( )
{
}
void food::init(int x, int y)
{
this->x=x;
this->y=y;
}
widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <QLabel>
#include <QTimer>
#include <QPainter>
#include "snake.h"
#include "food.h"
#include <QPen>
#include <block.h>
#include <QKeyEvent>
#include <QDebug>
#include <QColor>
#include <QRectF>
#include <QTime>
namespace Ui {
class Widget;
}
class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = nullptr);
~Widget();
void init();// 初始化函数
void draw(QPainter &p );// 画函数
void initFood();// 初始化食物函数
private:
Ui::Widget *ui;
QTimer* mytimer; // 定时器
snake * mySnake;
food * f;
protected:
// QPainter* mPainter=nullptr; //画家对象
QPen * mPen=nullptr;// 画笔对象
void paintEvent(QPaintEvent *event);//画图事件更新函数
void keyPressEvent(QKeyEvent *);//键盘判定
public slots:
void doProcessTimeOut();//计时结束后触发的槽
};
#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);
this->setMinimumSize(600,600);
this->setMaximumSize(600,600);
this->init();
}
void Widget::initFood(){
QTime randtime;
randtime = QTime::currentTime();
qsrand(randtime.msec()+randtime.second()*1000); //以当前时间ms为随机种子
int x = qrand() % 60;
int y = qrand() % 60;
f->init(x,y);
block *temp = mySnake->snakeHead;
while (temp!=nullptr) {
if(temp->x==x&&temp->y==f->y)
{
x = qrand() % 60;
y = qrand() % 60;
temp= mySnake->snakeHead; // 从头开始
}else {
temp= temp->next;
}
}
}
void Widget::init( ){
mySnake = new snake();
mySnake->init();
mytimer = new QTimer(this);
//
this->f = new food();
this->initFood();// 创建食物
connect(mytimer,&QTimer::timeout, this, [&]() mutable{
// 获取下一步 的 x y
int x= this->mySnake->getNextX(); // 获取下一步的x y
int y= this->mySnake->getNextY();
if(x<0||x>59||y<0||y>59)
{
//碰到墙了 游戏结束
this->mytimer->stop();
}
else
{
if(x==f->x&&y==f->y)
{
// 吃
this->mySnake->eat();
//重新初始化食物
this->initFood();//
}
else
{
this->mySnake->move(); // x y bool
}
this->update();
}
});
mytimer->start(100);
}
void Widget::draw(QPainter &p ){
// 开始画
block *temp = mySnake->snakeHead;
p.brush();
this->mPen->setColor(QColor(255, 2,2) ); // 头的颜色 255, 2,2 身体的颜色 35, 146, 1
p.setPen( *( this->mPen));
while (temp!=nullptr) {
double x = static_cast<double>(temp->x) ;
double y = static_cast<double>(temp->y) ;
temp= temp->next;
p.drawRect(QRectF(x*10,y*10,10,10));
this->mPen->setColor(QColor(35, 146,1) );
p.setPen( *( this->mPen));
}
// 画食物
this->mPen->setColor(QColor(0, 115,225) );
p.setPen( *( this->mPen));
p.drawRect(QRectF(this->f->x*10,this->f->y*10,10,10));
}
void Widget::doProcessTimeOut(){
}
void Widget::paintEvent(QPaintEvent *event)
{
QPainter p (this);
this->mPen=new QPen();
this->draw(p);
}
void Widget::keyPressEvent(QKeyEvent *event)
{
switch(event->key()){
case Qt::Key_W :
//上
if( mySnake->getS()=='u' ||mySnake->getS()=='d'){
return ;
}
this->mySnake->setDir('u');
break;
case Qt::Key_Up :
//上
if( mySnake->getS()=='u' ||mySnake->getS()=='d'){
return ;
}
this->mySnake->setDir('u');
break;
case Qt::Key_S :
//下
if( mySnake->getS()=='u' ||mySnake->getS()=='d'){
return ;
}
this->mySnake->setDir('d');
break;
case Qt::Key_Down :
//下
if( mySnake->getS()=='u' ||mySnake->getS()=='d'){
return ;
}
this->mySnake->setDir('d');
break;
case Qt::Key_A:
//左
if( mySnake->getS()=='l' ||mySnake->getS()=='r'){
return ;
}
this->mySnake->setDir('l');
break;
case Qt::Key_Left:
//左
if( mySnake->getS()=='l' ||mySnake->getS()=='r'){
return ;
}
this->mySnake->setDir('l');
break;
case Qt::Key_D:
//右
if( mySnake->getS()=='l' ||mySnake->getS()=='r'){
return ;
}
this->mySnake->setDir('r');
break;
case Qt::Key_Right:
//右
if( mySnake->getS()=='l' ||mySnake->getS()=='r'){
return ;
}
this->mySnake->setDir('r');
break;
}
}
Widget::~Widget()
{
delete ui;
}
main.cpp
#include "widget.h"
#include <QApplication>
int main(int argc, char *argv[])
{
//初始化窗口
QApplication a(argc, argv);
Widget w;
w.show();
return a.exec();
}
完成效果: