跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客我的书AI学习GitHub 精选镜像AI 生图工具UI配色美学关于
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
C++大前端算法

基于 Qt 与 C++ 的经典游戏小球打砖块完整项目实战

如何使用 Qt 框架和 C++ 语言开发一款经典的小球打砖块游戏。文章涵盖了从架构设计(三层模式)、视觉系统(QGraphicsView 与 Scene-View-Item 模型)、游戏主循环驱动、图形元素封装(QGraphicsItem 继承)、图像资源加载与优化、动态场景更新技巧、键盘事件处理(防抖与状态管理)、小球运动与反射算法、碰撞检测实战以及游戏状态机设计等核心内容。通过完整的代码示例和流程图,帮助开发者掌握从界面绘制到逻辑封装的完整流程,构建高性能、可扩展的 2D 游戏引擎。

beaabea发布于 2026/3/25更新于 2026/9/1064 浏览

Qt 框架下小球打砖块游戏的设计与实现:从零构建一个高性能、可扩展的 2D 游戏引擎

'小球打砖块'是一款使用 C++ 语言和 Qt 框架开发的经典休闲游戏,支持跨平台运行,涵盖图形界面构建、事件处理、碰撞检测与游戏逻辑实现等核心技术。通过控制挡板反弹小球以消除砖块,玩家需完成关卡目标。本项目全面整合 GUI 设计、动画控制、状态管理与资源处理,适用于学习 Qt 应用开发与游戏编程基础。

架构先行:为什么你的游戏需要三层楼?

如果把所有逻辑都塞进一个类里,比如 MainWindow,那很快就会变成一团乱麻:绘图、移动、碰撞、音效全都混在一起。所以,我们采用经典的三层架构模式:

  • 界面层(View):负责看,也就是画面展示;
  • 逻辑层(Controller/Model):负责想,控制规则和状态流转;
  • 资源管理层(Resource Manager):负责存,统一管理图片、声音等资产。

这三者之间通过信号槽机制解耦通信。Qt 的 QObject 派生类天然支持信号槽,这是实现松耦合的最佳武器。

视觉系统的基石:为何选择 QGraphicsView 而非 QWidget 直接绘图?

QGraphicsView 就像是为复杂交互式应用量身定做的舞台导演。它与 QWidget + QPainter 的对比如下:

特性QWidget + QPainterQGraphicsView
图元管理手动维护列表内置场景自动管理
局部刷新全局重绘或手动裁剪自动脏区域检测
动画支持需配合 QTimer 自行实现支持 QPropertyAnimation 等高级动画
坐标系统设备坐标为主场景 + 视图 + 项目三级坐标体系
事件分发手动判断点击位置自动映射并转发给对应 item

双缓冲机制默认开启,彻底告别闪烁问题。

Scene-View-Item 模型解析

简单说就是三个角色:

  • QGraphicsScene:后台大管家,管着所有的游戏角色(items),但它自己不上台;
  • QGraphicsView:摄像机镜头,把你指定的场景拍出来显示在屏幕上;
  • QGraphicsItem:演员本人,每个砖块、小球、挡板都是它的子类。
graph TD
    A[QWidget 主窗口] --> B[QVBoxLayout 布局]
    B --> C[QGraphicsView 视图]
    C --> D[QGraphicsScene 场景]
    D --> E[QGraphicsItem 砖块]
    D --> F[QGraphicsItem 小球]
    D --> G[QGraphicsItem 挡板]

从主窗口到最底层的图形项,层层嵌套,职责分明。这种结构不仅清晰,还特别适合后期扩展。

游戏的心跳:主循环是如何驱动一切的?

在 Qt 中,我们靠 QTimer 来模拟这颗心脏。设置一个固定时间步长(通常是 16ms,对应 60FPS),让它不断触发更新:

connect(m_timer, &QTimer::timeout, this, &GameScene::gameLoop);
m_timer->start(16); // 每 16 毫秒跳一次

每一帧典型的闭环流程如下:输入处理 → 小球位移计算 → 碰撞检测 → 状态更新 → 场景重绘。最关键的一点是逻辑更新与渲染分离。即使某帧卡了一下,也不影响物理逻辑的准确性。

让画面动起来:基于 QGraphicsItem 的可视化元素封装

现在进入实战环节。我们要把挡板、小球、砖块这些元素一个个做出来,并且要做得优雅、可复用。

继承 QGraphicsItem:定义可动的基本单元

所有图形元素都应继承自 QGraphicsItem。你需要重写三个核心函数:

class Ball : public QGraphicsItem {
public:
    QRectF boundingRect() const override { return QRectF(-5, -5, 10, 10); }
    void paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *) override {
        painter->setRenderHint(QPainter::Antialiasing);
        painter->setBrush(Qt::yellow);
        painter->setPen(Qt::darkGray);
        painter->drawEllipse(-5, -5, 10, 10);
    }
    QPainterPath shape() const override {
        QPainterPath path;
        path.addEllipse(boundingRect());
        return path;
    }
};

为什么要返回 QPainterPath?因为默认的 collidesWithItem() 只用 boundingRect() 判断,那是矩形框。如果你的小球是圆的,就会出现明明没碰到却判定碰撞的情况。而一旦你重写了 shape(),Qt 会使用路径进行更精细的检测。

挡板、小球、砖块的具体实现对比

Paddle(挡板)
class Paddle : public QGraphicsItem {
    QRectF boundingRect() const override {
        return QRectF(-40, -8, 80, 16);
    }
    void paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *) override {
        painter->setRenderHint(QPainter::Antialiasing);
        painter->setBrush(QColor(70, 130, 180));
        painter->setPen(Qt::NoPen);
        painter->drawRoundedRect(boundingRect(), 8, 8);
    }
};
Brick(砖块)
class Brick : public QGraphicsItem {
    Q_OBJECT
public:
    Brick(qreal x, qreal y, const QColor &color) : m_color(color) {
        setPos(x, y);
    }
    QRectF boundingRect() const override {
        return QRectF(-35, -10, 70, 20);
    }
    void paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *) override {
        painter->setBrush(m_color);
        painter->setPen(QPen(Qt::white, 1));
        painter->drawRoundedRect(boundingRect(), 4, 4);
    }
private:
    QColor m_color;
};
属性Ball(小球)Paddle(挡板)Brick(砖块)
形状圆形圆角矩形圆角矩形
尺寸直径 10px宽 80×高 16px宽 70×高 20px
是否可动是是否(销毁前固定)
是否参与碰撞是是是
是否可销毁否否是
Z 值(层级)中高低

Z 值决定了绘制顺序,数值越大越靠前。比如挡板一定要比砖块高,否则会被盖住。

抗锯齿与渐变填充

纯色填充太单调?试试加个渐变效果吧!

void Ball::paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *) {
    painter->setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform);
    QRadialGradient gradient(0, 0, 5, -2, -2);
    gradient.setColorAt(0, Qt::white);
    gradient.setColorAt(1, Qt::yellow);
    painter->setBrush(gradient);
    painter->setPen(QPen(Qt::darkGray, 0.8));
    painter->drawEllipse(boundingRect());
}

性能提醒:抗锯齿虽然好看,但耗性能。建议只在关键元素上启用,或者让用户自行开关。

砖块布局算法:整齐排列的艺术

砖块通常以矩阵形式出现在屏幕上方。为了居中对齐,我们需要动态计算起始 X 坐标:

const int cols = 10;
const int brickWidth = 70;
const int hSpacing = 10;
const int totalWidth = cols * brickWidth + (cols - 1) * hSpacing;
const int startX = (800 - totalWidth) / 2; // 居中对齐

然后双重循环生成:

QColor colors[] = {Qt::red, Qt::magenta, Qt::blue, Qt::cyan, Qt::green};
for (int row = 0; row < 5; ++row) {
    for (int col = 0; col < cols; ++col) {
        qreal x = startX + col * (brickWidth + hSpacing);
        qreal y = 50 + row * 30;
        Brick *brick = new Brick(x, y, colors[row % 5]);
        scene->addItem(brick);
        m_bricks.append(brick);
    }
}

这套算法灵活通用,改行列数也能完美适配。

图像资源加载与优化策略

静态绘图虽好,但真实游戏中常需加载背景图、精灵图等外部资源。

使用 QPixmap 加载并缓存图像

QPixmap background = QPixmap(":/images/bg_stars.png");
if (background.isNull()) {
    qWarning() << "Failed to load background image!";
} else {
    background = background.scaled(800, 600, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
    scene->setBackgroundBrush(background);
}

几点注意:

  • 资源路径使用 : 开头,表示从 Qt 资源系统(qrc)读取,打包后无需额外文件;
  • scaled() 支持高质量插值算法;
  • setBackgroundBrush() 会自动拉伸或平铺。

缓存复杂图形减少重复绘制

有些内容每次重绘代价很高,比如带阴影的文字提示。可以预先渲染成 QPixmap 缓存:

QPixmap cache(200, 50);
cache.fill(Qt::transparent);
QPainter p(&cache);
p.setRenderHint(QPainter::Antialiasing);
p.setFont(QFont("Arial", 16));
p.setPen(Qt::white);
p.drawText(cache.rect(), Qt::AlignCenter, "Press SPACE to Start");
p.end();
// 在 paint() 中直接绘制缓存图
painter->drawPixmap(-100, -25, cache);

这样就把多次矢量操作变成了单次位图拷贝,效率翻倍。

高 DPI 适配不能少

现在高清屏满地走,必须做好适配:

QApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::PassThrough);

并在加载图像时设置像素比:

pixmap.setDevicePixelRatio(devicePixelRatioF());

否则在 Retina 屏上看起来会模糊不清。

动态场景更新技巧

游戏运行时,场景时刻在变:小球飞、挡板滑、砖块炸。如何高效更新?

销毁砖块的标准姿势

当小球撞到砖块后,记得按顺序来:

void GameScene::destroyBrick(Brick *brick) {
    removeItem(brick); // 先从场景移除
    m_bricks.removeOne(brick); // 从容器删除引用
    delete brick; // 最后再释放内存
    emit brickDestroyed(); // 发信号通知得分更新
}

千万不要反过来!否则 delete 后再调 removeItem 会导致访问非法内存,程序崩溃分分钟的事。

paint() 函数里的性能雷区

避免在 paint() 中做字符串拼接、动态分配这类事:

// ❌ 错误示范:每帧新建 QString
void BadItem::paint(...) {
    QString text = QString("Score: %1").arg(score);
    painter->drawText(..., text);
}

// ✅ 正确做法:仅在 score 变化时更新缓存文本
void ScoreItem::updateScore(int newScore) {
    if (newScore != m_score) {
        m_score = newScore;
        m_cachedText = QString("Score: %1").arg(m_score);
        update(); // 触发重绘
    }
}

视口刷新策略优化

设置合适的刷新模式:

view->setViewportUpdateMode(QGraphicsView::MinimalViewportUpdate);

这个模式只会重绘最小必要的区域,大幅降低 GPU 负载。结合 update() 的局部刷新能力,性能起飞。

键盘事件处理:让玩家的操作如丝般顺滑

再好的游戏逻辑,如果响应迟钝也白搭。我们来看看如何打造一套灵敏、稳定、防抖的输入系统。

输入状态管理:告别 auto-repeat 的坑

操作系统自带的键盘连打机制(auto-repeat)延迟不定,不同平台还不一样。所以我们不能依赖 keyPressEvent 的频率来控制移动速度,而应该把它当作按下/释放的状态标志。

class GameView : public QGraphicsView {
    QSet<int> m_pressedKeys;
protected:
    void keyPressEvent(QKeyEvent *event) override {
        if (!event->isAutoRepeat()) {
            m_pressedKeys.insert(event->key());
        }
        QGraphicsView::keyPressEvent(event);
    }
    void keyReleaseEvent(QKeyEvent *event) override {
        if (!event->isAutoRepeat()) {
            m_pressedKeys.remove(event->key());
        }
        QGraphicsView::keyReleaseEvent(event);
    }
};

然后在定时器里查询当前状态:

void GameScene::processInput() {
    bool leftPressed = m_view->isKeyPressed(Qt::Key_Left);
    bool rightPressed = m_view->isKeyPressed(Qt::Key_Right);
    if (leftPressed && !rightPressed) {
        m_paddle->moveLeft();
    } else if (rightPressed && !leftPressed) {
        m_paddle->moveRight();
    }
}

这才是真正的连续移动体验。

信号槽解耦:把按左键变成向左移动

Qt 的信号槽机制简直是模块解耦神器。我们可以定义语义化的信号:

class GameControl : public QObject {
    Q_OBJECT
signals:
    void moveLeft();
    void moveRight();
    void pauseGame();
    void startGame();
};

在主窗口捕获按键并发射信号:

void MainWindow::keyPressEvent(QKeyEvent *event) {
    switch (event->key()) {
    case Qt::Key_Left:
        emit m_control->moveLeft();
        break;
    case Qt::Key_Right:
        emit m_control->moveRight();
        break;
    case Qt::Key_Space:
        emit m_control->pauseGame();
        break;
    default:
        QMainWindow::keyPressEvent(event);
    }
}

再绑定到具体行为:

connect(m_control, &GameControl::moveLeft, m_paddle, &Paddle::moveLeft);
connect(m_control, &GameControl::moveRight, m_paddle, &Paddle::moveRight);
connect(m_control, &GameControl::pauseGame, this, &GameScene::togglePause);

好处是什么?将来换成手柄、触控甚至语音控制,只要发射同样的信号就行,完全不用改其他代码。

下面是各组件的角色分工:

classDiagram
    class MainWindow {
        +keyPressEvent()
        -emit moveLeft()
        -emit pauseGame()
    }
    class GameControl {
        <<signal>>
        +moveLeft()
        +moveRight()
        +pauseGame()
    }
    class GameScene {
        +togglePause()
        +startGame()
    }
    class Paddle {
        +moveLeft()
        +moveRight()
    }
    MainWindow --> GameControl : emits signals
    GameControl --> GameScene : connected to
    GameControl --> Paddle : connected to

是不是有种事件总线的感觉?

实时反馈与异常处理:专业级输入控制系统

高手之间的差距往往体现在细节上。下面我们来加几个高级特性。

挡板加速曲线:从匀速到变速

你想不想让挡板有加速度?按久一点越来越快那种?

class Paddle : public QGraphicsItem {
    qreal m_velocity = 0;
    qreal m_acceleration = 20;
    qreal m_maxSpeed = 300;
private slots:
    void updateMovement() {
        if (m_targetDir != 0) {
            m_velocity += m_targetDir * m_acceleration / 60;
            m_velocity = qBound(-m_maxSpeed, m_velocity, m_maxSpeed);
        } else {
            m_velocity *= 0.85; // 摩擦减速
        }
        setX(x() + m_velocity / 60);
        checkBounds();
    }
};

配上一个 16ms 的定时器,就能做出非常真实的物理感。

去抖动:防止暂停键连点抽风

连续按 Space 很容易造成暂停→恢复→暂停的抖动。加个最小间隔即可:

void GameScene::togglePause() {
    static QElapsedTimer lastToggle;
    if (lastToggle.isValid() && lastToggle.elapsed() < 300) {
        return; // 忽略 300ms 内的重复点击
    }
    lastToggle.restart();
    m_paused = !m_paused;
    if (m_paused) {
        m_gameTimer->stop();
    } else {
        m_gameTimer->start();
    }
}

输入屏蔽:暂停时不响应方向键

游戏暂停时,当然不该让挡板还能动:

void GameScene::keyPressEvent(QKeyEvent *event) {
    if (m_state == GameState::Paused || m_state == GameState::GameOver) {
        if (event->key() == Qt::Key_Escape) {
            emit exitRequested();
        }
        event->ignore(); // 其他键全部忽略
        return;
    }
    // 正常处理...
}

小球运动与反射算法:数学建模的艺术

终于到了最烧脑的部分——小球的运动轨迹和反弹逻辑。

位置、速度、加速度的 C++ 封装

我们为小球建个模型:

class Ball : public QGraphicsItem {
    QPointF m_position;
    QPointF m_velocity;
    QPointF m_acceleration;
public:
    void advance(int phase) override {
        if (!phase) return;
        m_position += m_velocity; // 简单欧拉积分
        update();
    }
    void setVelocity(qreal vx, qreal vy) {
        m_velocity = QPointF(vx, vy);
    }
};

每帧调用 advance() 推进状态,简洁明了。

边界反弹的矢量公式

传统做法是判断碰哪边墙就反哪个分量。但这无法应对斜面或动态角度表面。真正的解法是使用矢量反射公式:

$$ \vec{v}' = \vec{v} - 2(\vec{v} \cdot \hat{n})\hat{n} $$

代码实现:

QPointF reflectVector(const QPointF &v, const QPointF &n) {
    qreal dot = v.x()*n.x() + v.y()*n.y();
    return v - 2 * dot * n.normalized();
}

只要传入单位法线向量,就能算出正确的新速度。无论是上下左右墙,还是任意倾斜面,通吃。

增强玩法:挡板不同区域击球角度不同

经典设定来了:打中挡板中间,球垂直向上;靠近边缘,则反弹角度更斜。

怎么做?构造一个伪法线!

QPointF calculatePaddleReflection(const Ball* ball, const Paddle* paddle) {
    qreal relPos = (ball->pos().x() - paddle->centerX()) / (paddle->width() / 2);
    QPointF fakeNormal(-relPos * 0.5, -1.0);
    return reflectVector(ball->velocity(), fakeNormal.normalized());
}

relPos ∈ [-1, 1],越靠边,法线越倾斜,反射角也就越大。玩家瞬间就有我能控方向的错觉,游戏深度立马提升。

碰撞检测实战:圆形 vs 矩形

砖块是矩形,小球是圆形,怎么判断是否相撞?

数学原理:找最近点距离

思路是这样的:

  1. 找到矩形上离圆心最近的那个点;
  2. 看这个点到圆心的距离是否小于半径。
bool checkBallBrickCollision(const QPointF& center, qreal radius, const QRectF& rect) {
    qreal clampedX = qBound(rect.left(), center.x(), rect.right());
    qreal clampedY = qBound(rect.top(), center.y(), rect.bottom());
    qreal dx = center.x() - clampedX;
    qreal dy = center.y() - clampedY;
    return (dx*dx + dy*dy) < (radius*radius);
}

为了避免开方运算,我们比较的是距离平方,性能更高。

预检优化:先做 AABB 粗筛

如果两个包围盒都不相交,就没必要算精确碰撞了:

QRectF ballRect = ball->sceneBoundingRect();
QRectF brickRect = brick->sceneBoundingRect();
if (!ballRect.intersects(brickRect)) continue;

这一招能过滤掉大部分无效检测,CPU 占用直降一半不止。

游戏状态机:掌控全局生命周期

最后一步,把所有模块整合成一个完整的游戏。

我们定义四种状态:

enum GameState { Start, Playing, Paused, GameOver };

转换关系如下:

stateDiagram-v2
    [*] --> Start
    Start --> Playing: 用户点击开始
    Playing --> Paused: 按下 P 键或菜单暂停
    Paused --> Playing: 再次按下 P 键
    Playing --> GameOver: 小球掉落底部三次
    GameOver --> Start: 点击重新开始
    state Playing {
        [*] --> MovePaddle
        [*] --> UpdateBall
        [*] --> CheckCollisions
    }

每个状态有不同的行为组合:

状态定时器输入响应UI 显示
Start停接受开始显示 Press Start
Playing开挡板控制隐藏按钮,更新分数
Paused停仅恢复显示 PAUSED 浮层
GameOver停重试显示最终得分

切换函数示例:

void GameWidget::setState(GameState newState) {
    currentState = newState;
    switch (newState) {
    case Start:
        timer->stop();
        startButton->show();
        ball->resetPosition();
        break;
    case Playing:
        timer->start(16);
        startButton->hide();
        break;
    // ...
    }
    emit gameStateChanged(newState);
}

主循环集成:形成闭环控制系统

最终的主循环就像一台精密仪器:

connect(timer, &QTimer::timeout, [&]() {
    if (currentState != Playing) return;
    ball->move();
    checkWallCollisions();
    checkPaddleCollision();
    handleCollisions();
    checkWinCondition();
    checkLoseCondition();
});

同时绑定 UI 响应状态变化:

connect(this, &GameWidget::gameStateChanged, [this](GameState s){ updateUiForState(s); });

整个系统形成了输入 → 状态 → 物理模拟 → 渲染 → 反馈的完美闭环。

结语:你已经拥有了构建任何 2D 游戏的基础能力

我们从架构设计讲到图形绘制,从键盘响应讲到物理模拟,再到状态管理,一步步搭建出了一个结构清晰、性能优良、易于扩展的 2D 游戏骨架。

这不仅仅是一个小球打砖块游戏,它是你通往更复杂项目的跳板。未来你可以轻松加入:

  • 多关卡系统
  • 道具系统(加速球、多球、火焰弹)
  • 音效与背景音乐
  • 存档与排行榜
  • 甚至网络对战模式

记住一句话:优秀的设计让扩展变得简单,糟糕的设计让修改变得痛苦。

而现在,你手里握着的就是那份优秀的设计。继续加油,下一个爆款可能就是你做的。

编程不是写代码,而是创造世界。

目录

  1. Qt 框架下小球打砖块游戏的设计与实现:从零构建一个高性能、可扩展的 2D 游戏引擎
  2. 架构先行:为什么你的游戏需要三层楼?
  3. 视觉系统的基石:为何选择 QGraphicsView 而非 QWidget 直接绘图?
  4. Scene-View-Item 模型解析
  5. 游戏的心跳:主循环是如何驱动一切的?
  6. 让画面动起来:基于 QGraphicsItem 的可视化元素封装
  7. 继承 QGraphicsItem:定义可动的基本单元
  8. 挡板、小球、砖块的具体实现对比
  9. Paddle(挡板)
  10. Brick(砖块)
  11. 抗锯齿与渐变填充
  12. 砖块布局算法:整齐排列的艺术
  13. 图像资源加载与优化策略
  14. 使用 QPixmap 加载并缓存图像
  15. 缓存复杂图形减少重复绘制
  16. 高 DPI 适配不能少
  17. 动态场景更新技巧
  18. 销毁砖块的标准姿势
  19. paint() 函数里的性能雷区
  20. 视口刷新策略优化
  21. 键盘事件处理:让玩家的操作如丝般顺滑
  22. 输入状态管理:告别 auto-repeat 的坑
  23. 信号槽解耦:把按左键变成向左移动
  24. 实时反馈与异常处理:专业级输入控制系统
  25. 挡板加速曲线:从匀速到变速
  26. 去抖动:防止暂停键连点抽风
  27. 输入屏蔽:暂停时不响应方向键
  28. 小球运动与反射算法:数学建模的艺术
  29. 位置、速度、加速度的 C++ 封装
  30. 边界反弹的矢量公式
  31. 增强玩法:挡板不同区域击球角度不同
  32. 碰撞检测实战:圆形 vs 矩形
  33. 数学原理:找最近点距离
  34. 预检优化:先做 AABB 粗筛
  35. 游戏状态机:掌控全局生命周期
  36. 主循环集成:形成闭环控制系统
  37. 结语:你已经拥有了构建任何 2D 游戏的基础能力

更多推荐文章

查看全部
  • Streamlit AI 对话界面实现:CSS :has() 选择器与 Flex 布局反转
  • 使用 Trae IDE 结合 Figma MCP 实现设计稿转前端代码
  • 基于强化学习的无人机端到端飞行控制算法开发
  • YApi 代码生成功能使用指南:自动生成前端请求代码
  • 前端状态管理:Redux、Zustand 与 Jotai 方案对比
  • 二叉树算法实战:美国血统与公共祖先求解
  • Linux 互斥锁原理与 C++ RAII 封装实战
  • Ollama Windows 安装与使用:本地运行 Llama、DeepSeek 等大模型
  • LeetCode 202 快乐数:快慢指针判环解法
  • 3ds Max VR 渲染器局部渲染设置指南
  • 基于 Rokid 灵珠平台搭建旅游 AR 智能体指南
  • RoboMME:机器人通用策略的记忆基准测试与理解
  • 别瞎改了!直接抄DeepSeek这5大降AIGC指令,搭配3款超有效工具,亲测98%暴降至5%!
  • Qlib 前端可视化平台助力量化投资
  • MiniOneRec 论文解读:生成式推荐框架与代码分析
  • AMD 显卡部署 llama.cpp 兼容性解决方案与部署指南
  • 大模型 LLM 合成训练样本的数据分布问题
  • 基于 Python 解析 HAR 文件生成页面性能测试报告
  • Qwen3.5 大模型单 GPU 高效部署与股票筛选应用
  • F12 网络调试实战:前后端交互原理与 Flask 路由定位

相关免费在线工具

  • 加密/解密文本

    使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online

  • Gemini 图片去水印

    基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online

  • Markdown转HTML

    将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online

  • HTML转Markdown

    将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online