简易贪吃蛇练习:使用单链表存储数据
写在开头
本练习主要使用QPainter操作,单链表,自定义鼠标事件,定时事件来实现一个简易的贪吃蛇练习。
代码构思
参考常见的贪吃蛇游戏,分析总结应包含以下功能:
- 页面背景:绘制网格线背景
- 绘制蛇:使用QPainter绘制连续的小方块组成一条蛇
- 绘制果实:使用QPainter绘制不同颜色的小方块代表果实。
- 蛇的移动:根据需要移动的方向改变蛇的头部位置,其他的身体的方块移动到上一个身体方块的位置上,删除最后一个身体方块,实现移动效果。
- 蛇吃果实:判断蛇头是否和果实的位置参数相等,相等就执行蛇的身体长度+1
- 用户点击事件:用户点击改变蛇当前的运动方向。
- 游戏结束闪烁蛇身提示:判断蛇头是否达到边缘,闪烁的效果可以改变蛇的身体颜色+定时事件来实现。
本练习代码思路
- 页面背景:绘制网格线背景
- 绘制蛇:蛇的身体数据使用单链表**(snake_Node)来存储,链表数据域site存储坐标,对应网格线中网格的左上角顶点。使用QPainter->drawRect()绘制出蛇链表的每一个结点,从而实现绘制整条蛇。
- 绘制果实:果实数据使用结构体(FruitPoint)来存储,结构体成员FruitSite存储果实的坐标,isAddGoal存储代表该果实是否被蛇吃掉。绘制原理同绘制蛇。
- 蛇的移动:蛇的移动方向根据蛇的头结点的变换来实现,函数名为moveSnake(),每次进次函数的时候时候先判断当前需要移动的方向MoveDirection,根据方向去控制头结点的变换。
蛇的移动逻辑为:蛇头根据移动方向变换相邻位置,接下来的每个身体结点都继承上一个数据结点的位置,蛇尾删除。
使用定时事件timerEvent每秒钟执行一次。
每次移动前先判断蛇头是否到达边缘,到达边缘就重新开始。 - 蛇吃果实:这里做了一个蛇自动向果实移动的函数snakeSeekFruitPoint()。根据判断果实的位置和蛇头的位置差去改变当前蛇的移动方向,直到蛇头的位置与果实的位置重合。重合后刷新果实位置,蛇的下一次移动不删除蛇尾从而实现蛇的身体增加1。
- 用户点击事件:用户点击改变蛇当前的运动方向。
- 游戏结束闪烁蛇身提示:通过控制draw_snake(snake_Node* &p)绘制蛇身体时候的QPainter->setBrush()画刷颜色,实现闪烁。
代码详细设计
代码文件
UI界面
diyform.ui
diyform.ui为空,本练习全部使用QPainter绘制。
代码部分
1.diyform.h
#ifndef DIYFORM_H
#define DIYFORM_H
#include <QMainWindow>
#include <QLabel>
#include <QString>
#include <QPoint>
#include <QPalette>
#include <QFont>
#include <QPainter>
#include <QPen>
#include <QLineF>
#include <QDebug>
#include <QTimerEvent>
#include <QTime>
#include <QMutex>
QT_BEGIN_NAMESPACE
namespace Ui {
class diyForm; }
QT_END_NAMESPACE
#define PLAY_WIDGET_LENGTH 870
#define PLAY_WIDGET_WEIGHT 480
//链表结点
typedef struct SnakeNode
{
QPoint site; //数据域:存储当前方块的原点
struct SnakeNode *next; //指针域:存储下一个方块结点的地址
}snake_Node;
//结构体
struct FruitPoint
{
QPoint FruitSite; //果实方块的原点
bool isAddGoal; //标志位
};
//枚举:移动方向
enum MoveDirection
{
UP_MOVE,
DOWN_MOVE,
LEFT_MOVE,
RIGHT_MOVE
};
class diyForm : public QMainWindow
{
Q_OBJECT
public:
diyForm(QWidget *parent = nullptr);
~diyForm();
void paintEvent(QPaintEvent *);
void timerEvent(QTimerEvent *e);
void init_SnakeList(snake_Node* &p); //初始化链表
void add_SnakeSite(snake_Node* &p); //增加链表结点
void DestroyList(snake_Node * &L); //销毁链表
void draw_background(); //绘制背景
void draw_snake(snake_Node* &p); //绘制蛇
void show_snake_info(snake_Node* &p); //打印链表信息
void moveSnake(snake_Node* &p); //实现蛇移动
void flash_Snake(); //实现闪烁提示
void refreshFruitPoint(); //刷新果实
void painterFruitPoint(); //绘制果实
void snakeSeekFruitPoint(); //自动寻找果实
private:
Ui::diyForm *ui;
snake_Node *snake_list;
int moveTime;
int flashTime;
int flashStopTime;
int customPlayTime;
bool flashState;
QColor snake_color;
MoveDirection moveDirection;
FruitPoint fruitPoint;
QMutex mutex;
protected:
virtual void mouseReleaseEvent(QMouseEvent * ev);
};
#endif // DIYFORM_H
2.diyform.cpp
#include "diyform.h"
#include "ui_diyform.h"
#define X_Max 1024
#define Y_Max 600
#define Grid_Size 20 //网格线大小
#define X_Grid_Max