小鸟游菜花 2024-06-02 19:02 采纳率: 86.5%
浏览 7

贪吃蛇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

    问题分析: 看起来你的贪吃蛇程序在头部碰到食物时没有增加长度或者食物没有消失的问题可能出现在以下几个部分:

    1. 蛇吃到食物的逻辑snakeEatsFood() 函数可能没有正确处理蛇吃到食物的情况。
    2. 碰撞检测snakeHitsWallOrSelf() 函数可能没有正确检查蛇头是否碰到了自身或边界。
    3. 游戏循环moveSnake() 函数可能没有更新蛇的长度和食物的位置。

    解决方案:

    1. 更新蛇长度:snakeEatsFood() 函数中,检查蛇头的位置是否与食物相同,如果相同,你需要将蛇头移到下一个点,同时将蛇尾添加到蛇的部件列表中。
    bool Snake::snakeEatsFood()
    {
        if (snakeParts.first() == foodPosition) {
            snakeParts.prepend(snakeParts.last()); // 将蛇尾添加到头部,然后移动头部
            snakeParts.removeLast(); // 移除旧的蛇尾
            return true; // 表示蛇吃了食物
        } else {
            return false;
        }
    }
    
    1. 食物消失和重新生成:snakeEatsFood() 函数之后,调用 placeFood() 以重新随机生成食物的位置。同时,当蛇头碰到边界或自身时,也需要调用 placeFood() 生成新的食物。
    void Snake::moveSnake()
    {
        if (snakeEatsFood()) {
            placeFood();
        }
    
        // ...其他移动逻辑...
    
        if (snakeHitsWallOrSelf()) {
            gameOver = true;
            placeFood(); // 重新生成食物
        }
    }
    
    1. 碰撞检测:snakeHitsWallOrSelf() 函数中,需要检查蛇头的边界条件和蛇身的碰撞。
    bool Snake::snakeHitsWallOrSelf()
    {
        // 检查蛇头是否碰到边界或自身
        if (snakeParts.first().x() < 0 || snakeParts.first().x() >= width() ||
            snakeParts.first().y() < 0 || snakeParts.first().y() >= height()) {
            return true;
        }
    
        for (const QPoint &part : snakeParts) {
            if (part == snakeParts.first()) {
                return true; // 蛇头碰到了自己
            }
        }
    
        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问题