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
相关产品推荐
相关产品推荐

