贪吃蛇头部和尾部为自定义照片,感觉代码没问题,但是为什么贪吃蛇头部碰到食物没有反应,不增加长度,食物也没消失并重新生成
#ifndef SNAKE_H
#define SNAKE_H
#include <QWidget>
#include<QEvent>
#include<QPoint>
#include<QTimer>
#include"setting.h"
//0. 蛇的表示
//1. 按键处理
//游戏循环:定时器提供了一个周期性的信号,用于触发游戏的主循环。
//在每次定时器触发时,游戏会检查蛇的位置、食物的位置、是否碰撞到边界或自身等,并更新游戏状态。
//2. 使用定时器
// 2.1 关联信号槽
// 2.2 启动定时器
// 2.3 实现对应的超时处理函数
//3. 渲染
namespace Ui {
class Snake;
class Setting;
}
class Snake : public QWidget
{
Q_OBJECT
public:
explicit Snake(QWidget *parent = nullptr);
~Snake();
protected:
void paintEvent(QPaintEvent *event) override;
void keyPressEvent(QKeyEvent *event) override;
private slots:
void moveSnake();
//按下ESC跳转到设置界面
void pressesc();
private:
Ui::Snake *ui;
// 蛇的各个部分的列表,每个部分都是一个QPoint对象
QList<QPoint> snakeParts;
QPoint foodPosition;
QPixmap snakeHeadPixmap;
QPixmap snakeBodyPixmap;
QPixmap foodPixmap;
// 蛇的移动方向
Qt::Key snakeDirection;
bool gameOver;
QTimer *timer;
// 初始化界面
void initGame();
//食物位置
void placeFood();
// 吃到食物
bool snakeEatsFood();
//是否碰到蛇身
bool snakeHitsWallOrSelf();
Ui::Setting *setting;
};
#endif // SNAKE_H
#include "snake.h"
#include "ui_snake.h"
#include<QPointF>
#include<QKeyEvent>
#include<QTimer>
#include<QPainter>
#include <QRandomGenerator>
#include<QFontMetrics>
Snake::Snake(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Snake)
{
ui->setupUi(this);
this->setFixedSize(700,500);
// 设置焦点策略为Qt::StrongFocus
// 这意味着窗口部件将尽可能地获取键盘输入焦点
// 当用户与窗口部件交互时,它可以接收键盘事件
setFocusPolicy(Qt::StrongFocus);
// 初始化snakeDirection为向右(Qt::Key_Right)
snakeDirection=Qt::Key_Right;
// 用于初始化游戏状态
initGame();
// 创建一个新的QTimer对象,并将其赋值给timer成员变量
// QTimer用于在指定的时间间隔后发出timeout信号
timer=new QTimer(this);
// 连接timer的timeout信号到Snake类的moveSnake槽函数
// 当timer发出timeout信号时,moveSnake函数将被调用
// 实现蛇的移动动画
connect(timer, &QTimer::timeout, this, &Snake::moveSnake);
// 启动定时器,设置其更新频率为100毫秒
// 这意味着每100毫秒,timer将发出一个timeout信号,导致moveSnake函数被调用
timer->start(200);
}
Snake::~Snake()
{
delete ui;
}
void Snake::initGame()
{
// 清除蛇的部件列表,确保游戏开始时蛇的初始状态
snakeParts.clear();
// 加载图片并设置大小
snakeHeadPixmap.load(":/image/tuzi.png");
snakeHeadPixmap = snakeHeadPixmap.scaled(40, 40, Qt::KeepAspectRatio, Qt::SmoothTransformation);
snakeBodyPixmap.load(":/image/tuzi_shen.png");
snakeBodyPixmap = snakeBodyPixmap.scaled(40, 40, Qt::KeepAspectRatio, Qt::SmoothTransformation);
// 初始化蛇的部件列表,包含一个蛇头和两个蛇身
snakeParts.append(QPoint(100, 100)); // 假设蛇头初始位置在(100, 100)
snakeParts.append(QPoint(100, 130)); // 第一个蛇身部件在蛇头的右侧
snakeParts.append(QPoint(100, 160)); // 第二个蛇身部件在第一个蛇身部件的右侧
foodPixmap.load(":/image/xfood.png");
foodPixmap = foodPixmap.scaled(40, 40, Qt::KeepAspectRatio, Qt::SmoothTransformation);
// 随机在游戏界面上放置食物
placeFood();
// 设置游戏状态为未结束
gameOver = false;
}
void Snake::placeFood()
{
// 使用Qt的全局随机数生成器QRandomGenerator来获取随机位置
// 确保食物的位置不会超出窗口的宽度和高度(减去食物的宽度和高度)
// 这可以避免食物被绘制在窗口的边界之外
// 获取一个介于0到(窗口宽度 - 食物宽度)之间的随机x坐标
int x = QRandomGenerator::global()->bounded(width() - foodPixmap.width());
// 获取一个介于0到(窗口高度 - 食物高度)之间的随机y坐标
int y = QRandomGenerator::global()->bounded(height() - foodPixmap.height());
// 设置食物的位置为随机生成的坐标
foodPosition = QPoint(x, y);
}
void Snake::paintEvent(QPaintEvent *event)
{
QPainter painter(this);
// 绘制背景图片
QPixmap background;
background.load(":/image/xia.jpg");
painter.drawPixmap(0, 0, this->width(), this->height(), background);
// 绘制蛇头和蛇身...
for (const QPoint &part : snakeParts)
{
if (part == snakeParts.first())
{
painter.drawPixmap(part, snakeHeadPixmap);
}
else
{
painter.drawPixmap(part, snakeBodyPixmap);
}
}
// 在食物当前的位置绘制食物图片
painter.drawPixmap(foodPosition, foodPixmap);
// 如果游戏结束,绘制游戏结束提示
if (gameOver)
{
QFont font("方正舒体", 60, QFont::ExtraLight, false);
painter.setFont(font);
QString text = "GAME OVER!";
QFontMetrics fm(font);
int textWidth = fm.horizontalAdvance(text);
int textHeight = fm.height();
int x = (this->width() - textWidth) / 2;
int y = (this->height() - textHeight+100) / 2;
painter.drawText(x, y, text);
}
}
void Snake::keyPressEvent(QKeyEvent *event)
{
// 这是为了防止蛇在相反方向上立即转身,这在实际的游戏逻辑中通常是不被允许的
if (event->key() == Qt::Key_W && snakeDirection != Qt::Key_S)
{
snakeDirection = Qt::Key_W;
}
else if (event->key() == Qt::Key_S && snakeDirection != Qt::Key_W)
{
snakeDirection = Qt::Key_S;
}
else if (event->key() == Qt::Key_A && snakeDirection != Qt::Key_D)
{
snakeDirection = Qt::Key_A;
}
else if (event->key() == Qt::Key_D && snakeDirection != Qt::Key_A)
{
snakeDirection = Qt::Key_D;
}
else if (event->key() == Qt::Key_Escape)
{
snakeDirection = Qt::Key_Escape;
}
}
void Snake::moveSnake()
{
// 获取当前蛇头的位置
QPoint newHead = snakeParts.first();
switch (snakeDirection)
{
case Qt::Key_W:
newHead.setY(newHead.y() - 30); // 假设每个格子高度为10
break;
case Qt::Key_S:
newHead.setY(newHead.y() + 30);
break;
case Qt::Key_A:
newHead.setX(newHead.x() - 30);
break;
case Qt::Key_D:
newHead.setX(newHead.x() + 30);
break;
case Qt::Key_Escape:
setting->show();
break;
default:
return; // 无效的方向,不移动
}
// 检查蛇是否吃到食物
if (snakeEatsFood())
{
placeFood(); // 生成新的食物
}
else
{
// 如果没有吃到食物,移除蛇尾部分
snakeParts.removeLast();
}
// 将新的蛇头添加到蛇的开头
snakeParts.push_front(newHead);
// 检查蛇是否碰到墙壁或自己
if (snakeHitsWallOrSelf())
{
gameOver = true;
timer->stop(); // 停止移动
}
// 更新窗口
update();
}
void Snake::pressesc()
{
//setting->show();
}
bool Snake::snakeEatsFood()
{
// 如果蛇头碰到食物
if (snakeParts.first() == foodPosition)
{
// 在蛇的末尾添加一个新的蛇身部件
// 创建一个新的点,通常是蛇头前一个点的位置(假设蛇的移动是逐个格子的)
QPoint newBodyPart = snakeParts.at(snakeParts.size() - 2); // 假设蛇至少有两个部分
// 如果蛇只有一个部分(初始状态),可能需要特殊处理
if (snakeParts.size() == 1)
{
// 这里可以根据需要选择一个方向来扩展蛇身
newBodyPart = QPoint(snakeParts.first().x() + 30, snakeParts.first().y()); // 假设向右扩展
}
snakeParts.push_back(newBodyPart); // 将新的蛇身部件添加到末尾
// 生成新的食物位置
placeFood();
return true;
}
return false;
}
bool Snake::snakeHitsWallOrSelf()
{
QPoint head = snakeParts.first();
// 检查是否碰到墙壁
if (head.x() < 0 || head.y() < 0 || head.x() >= width() || head.y() >= height())
return true;
// 检查是否碰到自己(除了头部)
for (int i = 1; i < snakeParts.size(); ++i)
{
if (head == snakeParts.at(i))
return true;
}
return false;
}