【鸣潮,原神PC端启动器】仿二次元手游PC端游戏启动器,以鸣潮为例。

二游GAMELauncher启动器

1.前言

  • 许多二次元手游(原神,鸣潮,少女前线)的PC端启动器都是使用Qt做的,正好最近正在玩鸣潮,心血来潮,便仿鸣潮启动器,从头写一个。先下载一个官方版的PC启动器,找到图标,背景图等素材,然后对着界面写代码就行。

  • 效果如下

    在这里插入图片描述

    在这里插入图片描述

2. 划分模块

  • 游戏启动器大致可以分为六部分

    • 主体窗口

    • 顶部标题栏

    • 公告栏

    • 轮播图

    • 游戏下载模块

    • 设置对话框

  • 模块划分后,要做的事就很清晰了,对每一个模块,都新建一个带ui(方便布局)的类,然后根据各模块功能分别实现,最后组装在一起就行。

3. 主体窗口

  • 主体窗口是一个无边框窗口,然后有动态的背景图,有logo,版本号,版本标题。

    • Qt中设置无边框窗口很简单,只要一行代码即可

        this->setWindowFlags(Qt::FramelessWindowHint | windowFlags());
      

      这会导致窗口原本的移动事件和缩放事件无效,移动事件我们留在标题栏部分实现,缩放事件我们则不需要。

    • 动态背景图,其实是通过定时切换图片实现,这时我们很容易想到使用定时器实现,到时间就就加载下一张图片。

      这样做会有一个问题,加载图片是需要时间的,这样做界面会有卡顿感。我们可以先把所有图片加载到内存,然后就不需要加载了,可以解决卡顿感。

      但是这又会导致另一个问题,图片很多,全部加载会占用很多内存空间,不够优雅。这里就需要用到线程,我们可以使用线程加载图片,然后通过信号把加载好的图片发送给主窗口就行绘制,这样既不卡顿也不占用很多内存。

    • 新建一个加载图片的类(LoadImage),继承QThread,实现run方法。

         while (!stop){if (!imgNameList.empty()){QPixmap pix = QPixmap(imgNameList[curIndex]);curIndex = (curIndex + 1) % imgNameList.size();emit sendPixmap(pix);}QThread::msleep(fps);}
      

      我们可以设定帧数,让背景图实现指定帧率刷新。

    • 这样我们就实现了背景图的切换

      在这里插入图片描述

    • 然后我们把那些logo,版本号,标题,根据对应位置绘制上去就行了。logo 和 slogan都是图片来的。

       //绘制logo 和 sloganp.drawImage(0,this->height()-slogan.height(),this->slogan);p.drawImage(50,120,this->logo//绘制版本号QPen pen;pen.setWidth(1);pen.setColor(Qt::white);p.setPen(pen);QFont font("Arial", 12, QFont::Bold);p.setFont(font);p.drawText(10,this->height()-10,versionNumber);
      
    • 这样就得到了主体视觉图

      在这里插入图片描述

  • 主体窗口还需要接收来自标题栏的移动,最小化,关闭的信号。

        connect(ui->topBar,&TopBar::miniumWindow,[this](){this->showMinimized();});connect(ui->topBar,&TopBar::closeWindow,[this](){this->close();});connect(ui->topBar,&TopBar::moveWindow,[this](QPoint pos){this->move(pos+this->pos());});
    

4. 顶部标题栏

  • 标题栏用一个QWidget,然后把背景颜色设置成rgba(0,0,0,80) 就可以实现透明的样式。其它的控件就是很常规的,里面有些按钮是有渐变的背景色和底部有白线,我们可以用一个QWidget加一个QPushButton作为一个组件实现,使用QWidget控件方便绘制白线。还有鼠标悬浮时显示的类似气泡的对话框,这个对话框需要自己实现。

    • 气泡框可以通过把QWidget设置为无边框和透明窗口,然后里面绘制一个圆角矩形,然后再画一个三角形箭头即可。

      setWindowFlags(Qt::FramelessWindowHint);
      setAttribute(Qt::WA_TranslucentBackground);void BubbleWidget::paintEvent(QPaintEvent*event)
      {QPainter painter(this);painter.setRenderHint(QPainter::Antialiasing);// 设置背景颜色和边框painter.setBrush(Qt::white);painter.setPen(QPen(Qt::gray, 1));// 创建圆角矩形路径QPainterPath path;QRectF rect = this->rect().adjusted(1, 10, -1,1); // 为箭头留出空间path.addRoundedRect(rect, 6, 6);painter.drawPath(path);path.clear();// 添加三角形箭头int arrowWidth = 15;int arrowHeight = 6;QVector<QPointF>points ={QPointF(rect.center().x() - arrowWidth / 2, rect.top()),QPointF(rect.center().x() + arrowWidth / 2, rect.top()),QPointF(rect.center().x(), rect.top() - arrowHeight)};path.addPolygon(QPolygonF(points));// 绘制路径painter.drawPath(path);
      
    • 然后我们可以根据需要往这个气泡框设置不同的Layout,就可以实现不同的布局效果了。

      在这里插入图片描述

    • 把其他控件放上就可以得到下面的标题栏,我们在这个类里面把移动,关闭,最小化的信号发给父窗口即可。
      在这里插入图片描述

    • 然后得到一个带标题栏的窗口。
      在这里插入图片描述

5. 公告栏

  • 公告栏可以用QFrame和QStackedWidget组合实现,每条公告需要自定义一个QWiget来表示,处理好气泡框提示以及绘制左则的竖线。剩下就是对样式的设置,需要慢慢调一下。

    在这里插入图片描述

6. 轮播图

  • 轮播图使用QWiget和两个QPushButton实现,按钮固定在中间的左右两则,鼠标进入轮播图时显示。QWiget负责绘制轮播图片,图片切换是带一定动画效果的,不能直接切换图片。

    • 轮播图上使用缓出动画效果,使得切换图片时更平滑。我们可以使用Qt的属性动画QPropertyAnimation,让图片位置属性offset 按缓和曲线进行变动,然后根据属性变化绘制当前图片和下一张图片即可。

        animation = new QPropertyAnimation(this, "offset");animation->setStartValue(0.0);animation->setEndValue(1.0);animation->setDuration(400);animation->setEasingCurve(QEasingCurve::OutCubic);   //缓出效果void Carousel::paintEvent(QPaintEvent*e){QPainter p(this);if(!left){p.drawImage(QRect(-width() * offset, 0, width(), height()), imgArr.at(curIndex).scaled(width(),height(),Qt::KeepAspectRatio,Qt::SmoothTransformation));// 绘制下一张图片p.drawImage(QRect(width() * (1 - offset), 0, width(), height()), imgArr.at(nextIndex).scaled(width(),height(),Qt::KeepAspectRatio,Qt::SmoothTransformation));}else{p.drawImage(QRect(width() * offset, 0, width(), height()), imgArr.at(curIndex).scaled(width(),height(),Qt::KeepAspectRatio,Qt::SmoothTransformation));// 绘制下一张图片p.drawImage(QRect(-width() * (1 - offset), 0, width(), height()), imgArr.at(nextIndex).scaled(width(),height(),Qt::KeepAspectRatio,Qt::SmoothTransformation));}}
      
    • 效果如下

      在这里插入图片描述

  • 图片进行缩放时要使用Qt::SmoothTransformation ,不然图片会很模糊。

7. 下载模块

  • 这个模块就比较简单,使用QWidget+QStackedWidget,实现下载界面和进入游戏界面的切换。

    • 有一些细节注意,QLineEdit要实现有个图标在最右侧可以使用addAction函数,添加一个图标。当QLineEdit的文字内容过长时,要让光标位于最开始位置,可以设置

      setCursorPosition(0) 。还需要把QLineEdit自动获取鼠标焦点功能禁用,设置setFocusPolicy(Qt::NoFocus)

    • 在这里插入图片描述

8. 设置

  • 设置界面比较麻烦,里面的QCheckBox和QRadioButton的效果无法通过QSS实现,需要重写,里面的漏斗形图形需要比较多步骤去绘制。

    • 重写QCheckBox

       void paintEvent(QPaintEvent *event) override{QCheckBox::paintEvent(event);QPainter painter(this);// 绘制复选框QStyleOptionButton opt;initStyleOption(&opt);QRect checkBoxRect = style()->subElementRect(QStyle::SE_CheckBoxIndicator, &opt, this);painter.setRenderHint(QPainter::Antialiasing);if (isChecked()){// 绘制选中时的圆角背景painter.setBrush(QColor("#BB9F5E"));  // 设置选中时的背景颜色painter.setPen(Qt::NoPen);  // 去除边框}else{// 绘制未选中时的圆角边框painter.setBrush(Qt::NoBrush);  // 不填充背景painter.setPen(QPen(QColor("#8C8C8C"), 2));  // 使用灰色边框,线宽为2}// 绘制圆角矩形,圆角半径为3painter.drawRoundedRect(checkBoxRect.adjusted(1, 1, -1, -1), 3, 3);// 如果复选框被选中,绘制白色的勾if (isChecked()){painter.setPen(QPen(Qt::white, 2));  // 设置勾的颜色为白色,线宽为2// 使用 QPainterPath 绘制勾的形状QPainterPath checkMarkPath;checkMarkPath.moveTo(checkBoxRect.left() + checkBoxRect.width() * 0.3, checkBoxRect.center().y());checkMarkPath.lineTo(checkBoxRect.center().x()-2, checkBoxRect.bottom() - checkBoxRect.height() * 0.3);checkMarkPath.lineTo(checkBoxRect.right() - checkBoxRect.width() * 0.3, checkBoxRect.top() + checkBoxRect.height() * 0.35);painter.drawPath(checkMarkPath);  // 绘制勾}}
      
  • 重写QRadioButton

      void paintEvent(QPaintEvent* event) override{QPainter painter(this);painter.setRenderHint(QPainter::Antialiasing);QStyleOptionButton option;initStyleOption(&option);painter.save();// 获取单选框的矩形区域QRect radioButtonRect = style()->subElementRect(QStyle::SE_RadioButtonIndicator, &option, this);// 增大单选框的尺寸int enlargedSize = 24;  // 自定义单选框的大小(增大后的大小)radioButtonRect.setWidth(enlargedSize);radioButtonRect.setHeight(enlargedSize);painter.setBrush(Qt::NoBrush);  // 不填充背景painter.setPen(QPen(QColor("#8C8C8C"), 2));  // 使用灰色边框,线宽为2// 绘制增大的圆形的单选框QRect circleRect = radioButtonRect.adjusted(2, 2, -2, -2); // 调整绘制圆形的位置painter.drawEllipse(circleRect);// 如果当前单选框被选中,则填充中心if (isChecked()) {   painter.setPen(QPen(QColor("#BB9F5E"), 2));painter.drawEllipse(circleRect);painter.setBrush(QColor("#BB9F5E"));   // 设置选中时的填充颜色为白色painter.drawEllipse(circleRect.adjusted(5,5, -5, -5));  // 绘制小圆圈,表示选中}// 绘制文本,确保文本位置对齐QRect textRect = option.rect;// 将文本左移,使其与增大的单选框右边对齐textRect.setLeft(radioButtonRect.right() + 5);  // 将文本移到单选框右侧// 使文本垂直居中textRect.moveTop(radioButtonRect.top() + (radioButtonRect.height() - textRect.height()) / 2);painter.restore();// 使用默认的文本颜色(由样式表和控件状态决定)style()->drawItemText(&painter, textRect, Qt::AlignVCenter, option.palette, isEnabled(), option.text);}
    
  • 绘制设置框的线条和图形。

    void Setting::paintEvent(QPaintEvent*event)
    {   QPainter p(this);QPen pen;QPainterPath path;pen.setColor(QColor("#CFCFCF"));//CFCFCFpen.setWidth(2);p.setPen(pen);//画顶部线条int x = ui->labelSetting->pos().x();int y = ui->labelSetting->pos().y()+ui->labelSetting->height()+10;p.drawLine(x,y,x+this->width()-40,y);//画圆弧int aw = 20;int endx = x+this->width()-35;int endy = y;int cx = endx-aw;int cy = endy-aw;int tx = cx-aw;int ty = cy-aw;//int bx = cx+aw;int by = cy+aw;int startAngle = 270;int spanAngle = 80;double rr = aw;int cx1 = tx+aw;int cy1 = ty+aw;double ex1 = cx1 + rr * cos((startAngle + spanAngle) * 3.14 / 180);double ey1 = cy1 - rr * sin((startAngle + spanAngle) * 3.14 / 180);startAngle = 90;spanAngle  = -80;int cx2 = tx+aw;int cy2 = by+aw;double ex2 = cx2 + rr * cos((startAngle + spanAngle) * 3.14 / 180);double ey2 = cy2 - rr * sin((startAngle + spanAngle) * 3.14 / 180);p.setBrush(QColor("#333333"));p.setPen(Qt::white);path.moveTo(cx,by);path.lineTo(ex1,ey1);path.lineTo(ex2,ey2);path.lineTo(cx,by);p.drawPath(path);path.clear();path.moveTo(cx,by);QRect r (tx,ty,aw*2,aw*2);  //x,y,width,heightQRect r2(tx,by,aw*2,aw*2);pen.setWidth(1);p.setPen(pen);p.setRenderHint(QPainter::Antialiasing);path.arcTo(r,270,80);path.moveTo(cx,by);path.arcTo(r2,90,-80);p.fillPath(path,Qt::white);//画顶部线条pen.setColor(QColor("#464646"));pen.setWidth(2);p.setPen(pen);p.drawLine(x+this->width()-35,0,x+this->width()-35,this->height());//画右边线条int s1x = ui->btnCancel->pos().x();int s1y = ui->btnCancel->pos().y()-20;int s2x = ui->btnOk->pos().x()+ui->btnOk->width();pen.setColor(QColor("#CBCBCB"));pen.setWidthF(1.5);p.setPen(pen);p.drawLine(s1x,s1y,s2x,s1y);//画右下角三角形QPoint p1(x+this->width()-35,this->height()-10);QPoint p2(x+this->width()-35,this->height());QPoint p3(x+this->width()-45,this->height());QPolygon cons;cons<<p1<<p2<<p3;p.setPen(Qt::black);p.drawPolygon(cons);}
  • 最终效果图就是这样。

    在这里插入图片描述

9. 其它

  • 除了界面之外,我们编写一下各控件对应事件就可以了,比如打开链接,跳转到网站。
  • 完整的源码在放在github里面了:GameLauncher
  • 有玩鸣潮的可以加个好友:ID:100073367

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/16498.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

STM32单片机CAN总线汽车线路通断检测

目录 目录 前言 一、本设计主要实现哪些很“开门”功能&#xff1f; 二、电路设计原理图 1.电路图采用Altium Designer进行设计&#xff1a; 2.实物展示图片 三、程序源代码设计 四、获取资料内容 前言 随着汽车电子技术的不断发展&#xff0c;车辆通信接口在汽车电子控…

H.265流媒体播放器EasyPlayer.js播放器出现加载视频等待画面时长过长的原因排查

在数字媒体时代&#xff0c;用户体验是衡量播放器性能的关键指标之一。EasyPlayer.js网页web无插件播放器作为一款流行的Web视频播放器&#xff0c;其加载速度和响应时间直接影响着用户的观看体验。 1、问题描述 加载视频等待画面时长过长。 2、可能的原因&#xff1a; 检查下…

联想“喜新厌旧”

科技新知 原创作者丨萧维 编辑丨蕨影 十月份&#xff0c;联想很忙。 先是2024联想科技创新大会15日在美国华盛顿州西雅图举行&#xff0c;联想大秀了一下自己在人工智能领域的创新产品、技术和解决方案&#xff0c;英特尔、AMD、英伟达三巨头更同时为其站台&#xff1b;后是与…

fpga 同步fifo

FIFO 基础知识 FIFO&#xff08;First In First Out&#xff0c;即先入先出&#xff09;&#xff0c;是一种数据缓存器&#xff0c;用来实现数据先入先出 的读写方式。在 FPGA 或者 ASIC 中使用到的 FIFO 一般指的是对数据的存储具有先入先出 特性的缓存器&#xff0c;常被用于…

Spark:大数据处理的强大引擎

一、Spark 简介 Apache Spark 是一个专为大规模数据处理而设计的快速、通用、可扩展的大数据分析计算引擎。它诞生于 2009 年&#xff0c;由美国加州伯克利大学的 AMP 实验室开发&#xff0c;2013 年被纳入 Apache 开源项目&#xff0c;并迅速成为顶级项目。 Spark 被认为是 …

常用在汽车PKE无钥匙进入系统的高度集成SOC芯片:CSM2433

CSM2433是一款集成2.4GHz频段发射器、125KHz接收器和8位RISC&#xff08;精简指令集&#xff09;MCU的SOC芯片&#xff0c;用在汽车PKE无钥匙进入系统里。 什么是汽车PKE无钥匙进入系统&#xff1f; 无钥匙进入系统具有无钥匙进入并且启动的功能&#xff0c;英文名称是PKE&…

hive 统计各项目下排名前5的问题种类

实现指定某项目下的数据效果图如下所示&#xff1a; 其中 ABCDE 为前5名的问题种类&#xff0c;其中A问题有124个&#xff08;出现了124次&#xff09; 数据说明&#xff1a; 整个数据集 包含很多项目一个项目 包含很多问题一个问题 选项 可认为是 类别值&#xff0c;所有出…

在 Windows 上搭建 FTP 服务器

&#xff08;1&#xff09;搭建 FTP 服务器 &#xff0c;为 windows 开启 ftp功能 &#xff1a;控制面板 -- 》 程序和功能 -- 》 打开或关闭 windows 功能 &#xff0c;勾选 web 管理工具下所有选项 &#xff0c;如下图所示&#xff1a; &#xff08;2&#xff09;添加 FTP 站…

AI开发-计算机视觉库-OpenCV

1 需求 官网&#xff1a;OpenCV - Open Computer Vision Library 2 接口 3 示例 import cv2image cv2.imread("./data/train/1_1.jpg") print(type(image)) 4 参考资料

STM32 ADC --- DMA采样

STM32 ADC — DMA采样 文章目录 STM32 ADC --- DMA采样cubeMX配置重要 &#xff1a;cubeMX配置使用DMA时的一个问题 代码编写一维数组接收数据二维数组接收数据 使用cubeMX生成HAL工程 cubeMX配置 上面红框中选择配置为连续转换模式&#xff0c;即通过HAL_ADC_Start_DMA()函数…

【WPF】Prism学习(二)

Prism Commands 1.命令&#xff08;Commanding&#xff09; 1.1. ViewModel的作用&#xff1a; ViewModel不仅提供在视图中显示或编辑的数据&#xff0c;还可能定义一个或多个用户可以执行的动作或操作。这些用户可以通过用户界面&#xff08;UI&#xff09;执行的动作或操作…

两部手机的IP地址:是否会相同?全面探讨

在数字化时代&#xff0c;手机已成为我们生活中不可或缺的一部分&#xff0c;而IP地址作为手机连接互联网的重要标识&#xff0c;扮演着举足轻重的角色。许多用户可能会好奇&#xff0c;两部手机在连接网络时&#xff0c;它们的IP地址是否会相同&#xff1f;这个问题看似简单&a…

【代码随想录day31】【C++复健】56. 合并区间;738.单调递增的数字

56. 合并区间 遇到了三个问题&#xff0c;一一说来&#xff1a; 1 比较应该按左区间排序&#xff0c;我却写了右区间。由于本题是合并区间&#xff0c;判断是否连续显然是用下一个的左区间与前一个的右区间比较&#xff0c;属于没想清楚了。 2 在写for循环时写成了如下的代码…

uniapp: 微信小程序包体积超过2M的优化方法

一、问题描述 在使用uniapp进行微信小程序开发时&#xff0c;经常会遇到包体积超过2M而无法上传&#xff1a; 二、解决方案 目前关于微信小程序分包大小有以下限制&#xff1a; 整个小程序所有分包大小不超过 30M&#xff08;服务商代开发的小程序不超过 20M&#xff09; 单个…

MySQL Online DDL

文章目录 1. 在线DDL的优势2. 支持的DDL操作3. 在线DDL的原理4. Online DDL的操作流程1. 准备阶段&#xff08;Prepare phase&#xff09;2. 拷贝阶段&#xff08;Copy phase&#xff09;3. 应用阶段&#xff08;Apply phase&#xff09;4. 替换阶段&#xff08;Swap phase&…

【freertos】FreeRTOS时间管理

FreeRTOS时间管理 一、睡眠延时函数1、vTaskDelay2、vTaskDelayUntil3、相对延时与绝对延时对比 二、自定义延时函数1、微秒延时2、毫秒延时 一、睡眠延时函数 1、vTaskDelay \quad 在UCOSIII 中延时函数OSTimeDly()可以设置为三种模式:相对模式、周期模式和绝对模式。在FreeR…

栈相关算法题1|通过栈判断链表是否对称|共享栈入栈出栈|括号匹配|多种括号配对|递归求序列最大值(C)

通过栈判断链表是否对称 设单链表的表头指针为L&#xff0c;data域为字符型&#xff0c;判断该链表的全部n个字符是否中心对称 xyx&#xff0c;xyyx 算法思想 使用栈来判断链表中的数据是否中心对称&#xff0c;让链表的前一半元素依次进栈 在处理链表的后一半元素时&#x…

datawhale11月组队学习 模型压缩技术3:2:4结构稀疏化BERT模型

文章目录 一、 半结构化稀疏性简介二、 代码实践2.1 定义辅助函数2.2 加载模型、tokenizer和数据集2.3 测试baseline模型指标2.4 对BERT-base模型进行半结构稀疏化 《datawhale2411组队学习之模型压缩技术1&#xff1a;模型剪枝&#xff08;上&#xff09;》&#xff1a;介绍模…

Qt中实现旋转动画效果

使用QPropertyAnimation类绑定对应的属性后 就可以给这个属性设置对应的动画 //比如自定义了属性 Q_PROPERTY(int rotation READ rotation WRITE setRotation)//给这个属性加动画效果 //参数1&#xff1a;谁要加动画效果 //参数2&#xff1a;哪个属性加动画效果 //参数3&…

视频流媒体播放器EasyPlayer.js RTSP播放器视频颜色变灰色/渲染发绿的原因分析

EasyPlayer.js RTSP播放器属于一款高效、精炼、稳定且免费的流媒体播放器&#xff0c;可支持多种流媒体协议播放&#xff0c;无须安装任何插件&#xff0c;起播快、延迟低、兼容性强&#xff0c;使用非常便捷。 EasyPlayer.js播放器不仅支持H.264与H.265视频编码格式&#xff0…