附代码:链接:https://pan.baidu.com/s/1zItNTLnB4QkLuSlVgsd-gQ?pwd=rhy2
提取码:rhy2
一、菜单界面
1.菜单类
class menu: public QWidget
{
Q_OBJECT
public:
menu();
int map_id; //当前地图编号
int start; //游戏启动信号
int his_flag; //历史记录显示信号
int difficulty; //难度系数
QPushButton *left; //地图选择左按键
QPushButton *right; //地图选择右按键
QPushButton *start_game; //游戏开始按键
QPushButton *history; //查看历史记录按键
QComboBox *cmb_difficulty;//难度系数选择
QLabel *l_pix; //标签——显示当前地图/历史记录
map m_map; //地图对象
//菜单界面初始化
void menu_init();
//绘图事件
void paintEvent(QPaintEvent *event);
public slots://按键、下拉框槽函数
void bt_left();
void bt_right();
void bt_start();
void bt_history();
void cmb_num(int index);
};
2.菜单部分各代码段
//地图选择代码段
void menu::bt_left()
{
if(map_id==0)
map_id=4;
else
map_id--;
l_pix->setPixmap(QPixmap(m_map.m_map[map_id]));
update();
}
void menu::bt_right()
{
if(map_id==4)
map_id=0;
else
map_id++;
l_pix->setPixmap(QPixmap(m_map.m_map[map_id]));
update();
}
//难度选择代码段
void menu::cmb_num(int index)
{
difficulty = index;
qDebug()<<difficulty;
}
//游戏开始代码段
void menu::bt_start()
{
start=1;
}
//历史记录查询代码段
void menu::bt_history()
{
his_flag=!his_flag;
if(his_flag)
{
Widget::query_sql();
l_pix->clear();
l_pix->setText(Widget::query_str);
QFont font("宋体",15,QFont::Thin,false);
l_pix->setFont(font);
l_pix->setStyleSheet("color:white;");
Widget::query_str.clear();
}
else
{
l_pix->clear();
l_pix->setPixmap(QPixmap(m_map.m_map[map_id]));//数据里查询历史成绩
}
}
QString Widget::query_str = {""};
void Widget::query_sql()
{
//去数据库中查询一条信息
int str_size=0;
QString str = QString("select * from score;");
QString n_str[10]={"0"};
QString s_str[10]={"0"};
QSqlQuery query;
query.exec(str);//查询命令输入
while(query.next())
{
if(str_size < 10)
str_size++;
int i;
for(i=0;i < str_size;i++)
{
if(query.value(1).toInt() > s_str[i].toInt())//选取历史记录中最高的10条记录
{
for(int j=str_size-1;j > i;j--)//从大到小排序
{
n_str[j] = n_str[j-1];
s_str[j] = s_str[j-1];
}
break;
}
}
n_str[i] = query.value(0).toString();
s_str[i] = query.value(1).toString();
}
for(int i=0;i<str_size;i++)
{
query_str = query_str + n_str[i];
query_str = query_str + " " +s_str[i]+ "\n";
}
}
二、游戏主界面
1.主界面类
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
int tm_flag; //暂停信号
int start_flag; //游戏启动信号
int m_recorder; //敌机出场间隔记录
static QString query_str;//记录存放字符串
QSqlDatabase db; //数据库对象
map m_map; //地图对象
menu m_menu; //创建菜单对象
heroplane m_hero; //创建飞机对象
QTimer m_Timer; //刷新坐标定时器对象
QTimer s_Timer; //游戏启动查询定时器对象
bomb m_bombs[BOMB_NUM]; //爆炸数组
enemyplane m_enemys[ENEMY_NUM];//敌机数组
//初始化场景
void initScene(); //主界面初始化
void initpopup(); //弹窗初始化
void paintEvent(QPaintEvent *event);//绘图事件
void enemyToScene();//敌机出场
void enemyescape(); //敌机逃逸
void startGame(); //启动游戏
void playGame(); //游戏进行
void updatePosition(); //坐标更新
void collisionDetection();//碰撞检测
void mouseMoveEvent(QMouseEvent *);//重写鼠标移动事件
void mousePressEvent(QMouseEvent *);//重写鼠标点击事件
void creat_sql(); //数据库创建
void Insert_sql(); //数据库插入
static void query_sql();//数据库查询
//弹窗部分成员
QWidget over_popup;//游戏结束弹窗
QWidget stop_popup;//游戏暂停弹窗
QPushButton *bt_con;//继续按键
QPushButton *bt_aff;//确认按键
QPushButton *bt_ret;//返回菜单按键
QLineEdit *le; //图片标签
QLineEdit *s_le; //文本标签
};
2.主界面定时器检测代码段
s_Timer定时器检测游戏开始按键被按下,则启动m_Timer定时器
m_Timer定时器驱动游戏引擎,刷新界面
//定时器代码段
//设置定时器间隔
s_Timer.setInterval(GAME_RATE);
m_Timer.setInterval(GAME_RATE);
s_Timer.start();//s_Timer定时器开启
connect(&s_Timer,&QTimer::timeout,[=](){
if(m_menu.start == 1)
{
m_menu.hide();
this->show();
//玩游戏 启动定时器
m_Timer.start();
//敌机出场纪录变量 初始化
m_recorder = 0;
m_menu.start=0;
start_flag = 0;
}
if(m_hero.plane_hp == 0)
{
//游戏结束 定时器停止
m_Timer.stop();
over_popup.show();
s_le->setText((QString::number(m_hero.score)));
start_flag=1;
}
});
//监听定时器的信号
connect(&m_Timer , &QTimer::timeout,[=](){
//调用启动游戏接口
playGame();
});
3.游戏启动代码段
敌机设置空闲标志位,依次出动空闲敌机,横坐标为随机生成,敌机被击中或逃逸成功, 转变为空闲敌机,等待下一次出动。
子弹同样设置空闲标志位,依次间隔一定时间发射,发射初始坐标为发射时主机坐标,通过检测非空闲敌机和非空闲子弹的边界是否交叉得到敌机是否被击中,边界交叉的敌机和子弹转变为空闲状态,主机得分+10,敌机穿过游戏界面最下延,视为成功逃逸,主机hp-1;
//游戏驱动引擎
void Widget::playGame()
{
//敌机出场
enemyToScene();
//更新游戏中所有元素的坐标
updatePosition();
//游戏中的元素 绘制到屏幕中
update(); //再调用paintEvent函数
//碰撞检测
collisionDetection();
//敌机逃逸检测
enemyescape();
}
//敌机出场
void Widget::enemyToScene()
{
//累加出场间隔
m_recorder++;
if(m_recorder < ENEMY_INTERVAL-7*m_menu.difficulty)
{
return;
}
m_recorder = 0;
for(int i = 0 ; i< ENEMY_NUM;i++)
{
if(m_enemys[i].m_Free)
{
//敌机空闲状态改为false
m_enemys[i].m_Free = false;
//设置坐标
m_enemys[i].m_X = rand() % (GAME_WIDTH - m_enemys[i].m_Rect.width());
m_enemys[i].m_Y = -m_enemys[i].m_Rect.height();
break;
}
}
}
//更新各元素坐标
void Widget::updatePosition()
{
//更新地图坐标
m_map.mapPosition();
//发射子弹
m_hero.shoot();
//计算子弹坐标
for(int i = 0 ;i < BULLET_NUM;i++)
{
//如果子弹状态为非空闲,计算发射位置
if(m_hero.m_bullets[i].m_Free == false)
{
m_hero.m_bullets[i].updatePosition();
}
}
//敌机坐标计算
for(int i = 0 ; i< ENEMY_NUM;i++)
{
//非空闲敌机 更新坐标
if(m_enemys[i].m_Free == false)
{
m_enemys[i].updatePosition();
}
}
//计算爆炸播放的图片
for(int i = 0 ; i < BOMB_NUM;i++)
{
if(m_bombs[i].m_Free == false)
{
m_bombs[i].updateInfo();
}
}
}
//碰撞检测
void Widget::collisionDetection()
{
//遍历所有非空闲的敌机
for(int i = 0 ;i < ENEMY_NUM;i++)
{
if(m_enemys[i].m_Free)
{
//空闲飞机 跳转下一次循环
continue;
}
//遍历所有 非空闲的子弹
for(int j = 0 ; j < BULLET_NUM;j++)
{
if(m_hero.m_bullets[j].m_Free)
{
//空闲子弹 跳转下一次循环
continue;
}
//如果子弹矩形框和敌机矩形框相交,发生碰撞,同时变为空闲状态即可
if(m_enemys[i].m_Rect.intersects(m_hero.m_bullets[j].m_Rect))
{
m_enemys[i].m_Free = true;
m_hero.m_bullets[j].m_Free = true;
m_hero.score = m_hero.score+10;
//调用爆炸特效
//播放爆炸效果
for(int k = 0 ; k < BOMB_NUM;k++)
{
if(m_bombs[k].m_Free)
{
//爆炸状态设置为非空闲
m_bombs[k].m_Free = false;
//更新坐标
m_bombs[k].m_X = m_enemys[i].m_X;
m_bombs[k].m_Y = m_enemys[i].m_Y;
break;
}
}
}
}
}
}
//敌机逃逸检测
void Widget::enemyescape()
{
//遍历所有非空闲的敌机
for(int i = 0 ;i < ENEMY_NUM;i++)
{
if(m_enemys[i].m_Free)
{
//空闲飞机 跳转下一次循环
continue;
}
if(m_enemys[i].m_Y >= GAME_HEIGHT)
{
m_enemys[i].m_Free = true;
m_hero.plane_hp--;
}
}
}
鼠标点击代码:左键点击可移动主机位置,右键点击游戏暂停,暂停弹窗生成,再次右键点击,游戏继续。
鼠标移动代码:鼠标左键点击时拖动可带动主机移动。
void Widget::mousePressEvent(QMouseEvent *event)
{
if(Qt::RightButton == event->button()) //右键
{
if(!start_flag)
{
tm_flag=!tm_flag;
if(tm_flag)
{
m_Timer.stop();
stop_popup.show();
}
else
{
m_Timer.start();
stop_popup.hide();
}
}
}
else
if(Qt::LeftButton == event->button()) //左键
{
int x = event->x() -m_hero.m_Rect.width() * 0.5;
int y = event->y() -m_hero.m_Rect.height() *0.5;
m_hero.setPosition(x,y);
}
}
void Widget::mouseMoveEvent(QMouseEvent *event)
{
int x = event->x() -m_hero.m_Rect.width() * 0.5;
int y = event->y() -m_hero.m_Rect.height() *0.5;
//边界检测
if(x <= 0 )
{
x = 0;
}
if(x >= GAME_WIDTH - m_hero.m_Rect.width())
{
x = GAME_WIDTH - m_hero.m_Rect.width();
}
if(y <= 0)
{
y = 0;
}
if(y >= GAME_HEIGHT - m_hero.m_Rect.height())
{
y = GAME_HEIGHT - m_hero.m_Rect.height();
}
m_hero.setPosition(x,y);
}
数据库代码:创建数据库,并生成score表,包含用户+成绩表,游戏结束时插入该次游戏的成绩,无用户名时默认为"默认用户"。
//数据库部分代码
void Widget::creat_sql()
{
//创建数据库
db = QSqlDatabase::addDatabase("QSQLITE");
db.setDatabaseName("scoredb");
bool ok = db.open();
//创建用户名+成绩
QString str("create table score(name, score);");
QSqlQuery query;
query.exec(str);
}
void Widget::Insert_sql()
{
//向入据库中加入一条信息
QString n_str;
if(le->text() == "")
n_str = "默认用户";
else
n_str = le->text();
QString str = QString("insert into score values('%0', '%1');").arg(n_str).arg(m_hero.score);
QSqlQuery query;
query.exec(str);
}
//数据库查询部分代码在菜单界面历史记录查询
三、弹窗部分代码
暂停弹窗在右键单击时弹出,点击继续游戏则恢复游戏,结束游戏则暂停弹窗隐藏,结束弹窗弹出。
结束弹窗在点击结束游戏或者hp为0时弹出,键入用户名,并点击确认,记录将保存在数据库中,无键入用户名时,用户名为"默认用户",保存成功后返回主菜单,可再次进行游戏。
//弹窗部分按键槽函数
//按键信号与槽函数
connect(bt_con,&QPushButton::clicked,[=](){
m_Timer.start();
stop_popup.hide();
});
connect(bt_ret,&QPushButton::clicked,[=](){
over_popup.show();
s_le->setText((QString::number(m_hero.score)));
stop_popup.hide();
start_flag =1;
});
connect(bt_aff,&QPushButton::clicked,[=](){
over_popup.hide();
m_menu.show();
this->hide();
Insert_sql();
startGame();
});