小鸟游菜花 2024-06-02 18:46 采纳率: 86.5%
浏览 4

贪吃蛇QT c++设计

贪吃蛇头部和尾部为自定义照片,感觉代码没问题,但是为什么贪吃蛇头部碰到食物没有反应,不增加长度,食物也没消失并重新生成


#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;
}


img

  • 写回答

3条回答 默认 最新

  • 关注

    晚上好🌙🌙🌙
    本答案参考ChatGPT-3.5

    问题分析:

    根据提供的代码,鉴于图片并没有问题,那么问题出在代码部分,从代码中可以看出,代码中食物的位置是通过随机生成的方式在游戏界面上放置食物的,而他与蛇的距离也在代码中进行了判断,因此不太可能出现食物在蛇身这种情况。那么问题可能出在判定条件函数中。

    解决方案:

    步骤一: 从代码运行的角度出发,需要确保判定碰撞条件函数中对蛇头部分和食物部分进行条件判定。出现食物消失的现象,也是对碰撞判定条件函数是否精准的体现。

    步骤二: 检查碰撞判定函数逻辑的正确性, 部分问题出在函数没有考虑到蛇的头部是否和食物部分重叠并检测是否在蛇身内部。

    步骤三: 经过代码检测,发现当头部碰到食物的时候没有加入新的部分,没有进一步延长贪吃蛇身体的长度。

    步骤四: 所以,这里需要增加补充的逻辑,就是在碰撞条件成立之后,对蛇的身体进行长度的增加。

    代码应该修改如下(仅列出重点部分):

    bool Snake::snakeHitsWallOrSelf() { // 碰撞检测函数,如果返回true,代表发生碰撞

    // 第一项是检查是否撞墙
    // 第二项是检查蛇头是否被包含在蛇身内部
    if (snakeParts.first().rx() < 0 || snakeParts.first().rx() > width() - snakeHeadPixmap.width() ||
        snakeParts.first().ry() < 0 || snakeParts.first().ry() > height() - snakeHeadPixmap.height() ||
        std::any_of(snakeParts.begin() + 1, snakeParts.end(), [&](const QPoint &part) {
            return part == snakeParts.first();
        }))
    {
        // 游戏结束,设置gameOver为true
        gameOver = true;
        return true;
    }
    
    // 碰撞检测,检查蛇头部分是否和食物部分重叠
    if (snakeParts.first() == foodPosition)
    {
        //如果蛇头与食物重叠
        snakeParts.prepend(snakeParts.first()+(snakeDirection-Qt::Key_Right?snakeDirection-Qt::Key_Left:snakeDirection-Qt::Key_Right)?QPoint(snakeBodyPixmap.width(), 0):QPoint(0, snakeBodyPixmap.height()));
        placeFood();
    }
    return false;
    

    }

    肉眼检测无误之后重新运行程序即可看到修改结果。

    评论 编辑记录

报告相同问题?

问题事件

  • 创建了问题 6月2日

悬赏问题

  • ¥15 pyqt怎么把滑块和输入框相互绑定,求解决!
  • ¥20 wpf datagrid单元闪烁效果失灵
  • ¥15 券商软件上市公司信息获取问题
  • ¥100 ensp启动设备蓝屏,代码clock_watchdog_timeout
  • ¥15 Android studio AVD启动不了
  • ¥15 陆空双模式无人机怎么做
  • ¥15 想咨询点问题,与算法转换,负荷预测,数字孪生有关
  • ¥15 C#中的编译平台的区别影响
  • ¥15 软件供应链安全是跟可靠性有关还是跟安全性有关?
  • ¥15 电脑蓝屏logfilessrtsrttrail问题