You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Qt射击游戏碰撞触发后退出主程序并显示计分窗口实现方案

解决方案:用Qt信号槽解耦,实现碰撞后退出并显示得分窗口

在Qt里直接在Attack类中操作主程序的QApplication会让代码耦合度太高,最好的方式是用信号槽机制来传递碰撞事件,让主程序(main.cpp)来处理退出和打开新窗口的逻辑,这样既符合Qt的设计理念,也让代码更易维护。

下面是具体的实现步骤:

1. 在Attack类中添加碰撞信号

首先修改你的attack.h,给Attack类添加一个信号,用来通知外部“碰撞到玩家了”,并传递当前得分:

#include <QGraphicsItem>
#include <QObject> // 别忘了包含QObject,因为要使用信号槽

// 注意Attack要继承QObject,所以需要添加Q_OBJECT宏
class Attack : public QObject, public QGraphicsItem {
    Q_OBJECT // 必须添加这个宏才能使用信号槽
public:
    // 你的已有构造函数、paint()、boundingRect()等代码...
    void move();

signals:
    // 定义信号:碰撞到玩家时发射,携带得分参数
    void collidedWithPlayer(int score);
};

2. 修改move()函数,碰撞时发射信号

更新attack.cpp中的move()方法,当检测到和Player的碰撞时,先发射信号传递得分,再处理当前Attack对象的删除:

void Attack::move() {
    QList<QGraphicsItem *> colliding_att = collidingItems();
    for(int i = 0; i < colliding_att.size(); ++i){
        if(typeid(*(colliding_att[i])) == typeid(Player)){
            // 先发射信号,把当前得分传出去
            emit collidedWithPlayer(SCORE);
            
            // 然后正常删除当前Attack对象
            scene()->removeItem(this);
            delete this;
            return;
        }
    }
    // 你原来的移动逻辑代码...
}

3. 创建得分窗口类

新建一个简单的得分窗口(比如ScoreWindow),用来显示最终得分。创建scorewindow.h:

#ifndef SCOREWINDOW_H
#define SCOREWINDOW_H

#include <QDialog>
#include <QLabel>
#include <QVBoxLayout>

class ScoreWindow : public QDialog {
    Q_OBJECT
public:
    explicit ScoreWindow(QWidget *parent = nullptr) : QDialog(parent) {
        setWindowTitle("Game Over!");
        setFixedSize(300, 200); // 固定窗口大小
        
        // 创建显示得分的标签
        scoreLabel = new QLabel(this);
        scoreLabel->setStyleSheet("font-size: 18px;"); // 可以美化一下字体
        
        // 布局设置,让标签居中显示
        QVBoxLayout *layout = new QVBoxLayout(this);
        layout->addWidget(scoreLabel);
        layout->setAlignment(Qt::AlignCenter);
    }
    
    // 设置得分的方法
    void setScore(int score) {
        scoreLabel->setText(QString("Your score is %1").arg(score));
    }

private:
    QLabel *scoreLabel;
};

#endif // SCOREWINDOW_H

4. 在main.cpp中连接信号并处理逻辑

在main.cpp里,把Attack的信号连接到一个处理函数,当信号触发时,显示得分窗口,然后退出应用:

#include <QApplication>
#include "attack.h"
#include "scorewindow.h"

int main(int argc, char *argv[]) {
    QApplication a(argc, argv);
    
    // 这里是你原来的游戏初始化代码:创建场景、Player、Attack对象等
    Attack *attack = new Attack();
    // ...其他游戏逻辑...
    
    // 连接Attack的碰撞信号到lambda槽函数
    QObject::connect(attack, &Attack::collidedWithPlayer, [&](int finalScore){
        // 先关闭/隐藏你的游戏主窗口(比如QGraphicsView或QMainWindow)
        // 假设你的游戏主视图是view,那么view->close();
        
        // 创建并显示得分窗口(模态窗口,阻塞直到用户关闭)
        ScoreWindow scoreWin;
        scoreWin.setScore(finalScore);
        scoreWin.exec();
        
        // 用户关闭得分窗口后,退出应用程序
        a.quit();
    });
    
    return a.exec();
}

为什么推荐这种方式?

  • 解耦:Attack类不需要知道主程序的结构,只需要负责发射碰撞信号,代码更清晰,后续修改得分窗口或退出逻辑时,不用改动Attack类。
  • 安全:避免了在已删除的对象中操作,信号发射后再删除Attack对象,不会影响槽函数的执行(因为槽函数只用到了传递的得分参数)。

如果你不想用信号槽(不推荐),也可以直接在move()里获取QApplication实例并操作,但这样耦合度很高:

// 不推荐的耦合写法
void Attack::move() {
    // ...碰撞检测代码...
    if(...) {
        scene()->removeItem(this);
        delete this;
        
        // 显示得分窗口
        ScoreWindow scoreWin;
        scoreWin.setScore(SCORE);
        scoreWin.exec();
        
        // 退出应用
        QApplication::instance()->quit();
        return;
    }
}

但这种写法会让Attack类依赖ScoreWindow和QApplication,后续维护起来很麻烦,所以还是优先用信号槽方案。

内容的提问来源于stack exchange,提问作者Ralnor

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 08:09:24