Qt 项目实战 | 俄罗斯方块

Qt 项目实战 | 俄罗斯方块

  • Qt 项目实战 | 俄罗斯方块
    • 游戏架构
    • 实现游戏逻辑
      • 游戏流程
      • 实现基本游戏功能
        • 设计小方块
        • 设计方块组
        • 添加游戏场景
        • 添加主函数
      • 测试
        • 踩坑点1:rotate 失效
        • 踩坑点2:items 方法报错
        • 踩坑点3:setCodecForTr 失效
        • 踩坑点4:不要在中文路径下运行 Qt 项目
        • 踩坑点5:multiple definition of `qMain(int, char**)'
        • 测试效果

官方博客:https://www.yafeilinux.com/

Qt开源社区:https://www.qter.org/

参考书:《Qt 及 Qt Quick 开发实战精解》

Qt 项目实战 | 俄罗斯方块

开发环境:Qt Creator 4.6.2 Based on Qt 5.9.6

在这里插入图片描述

游戏架构

本项目由三个类构成:

  • OneBox 类:继承自 QGraphicsObject 类。表示小正方形,可以使用信号与槽机制和属性动画。
  • BoxGroup 类:继承自 QObject 类和 QGraphicsItemGroup 类。表示游戏中的方块图形,可以使用信号与槽机制,实现了方块图形的创建、移动和碰撞检测。
  • MyView 类:实现了游戏场景。

游戏场景示意图:

在这里插入图片描述

实现游戏逻辑

游戏流程

游戏流程图:

在这里插入图片描述

七种方块图形:

在这里插入图片描述

方块组移动和旋转:

在这里插入图片描述

  • 碰撞检测:对每一个小方块都使用函数来获取与它们碰撞的图形项的数目,如果数目大于 1,说明已经发生了碰撞。
  • 游戏结束:当一个新的方块组出现时,就立即对它进行碰撞检测,如果发现了碰撞,说明游戏结束,这时由方块组发射游戏结束信号。
  • 消除满行:游戏开始后,每当出现新的方块前,都判断游戏移动区域的每一行是否已经拥有了满行的小方块。若满行,则销毁该行的所有小方块,然后让该行上面的方块都下移一格。

实现基本游戏功能

新建空的 Qt 项目,项目名 myGame。

myGame.pro 中新增代码:

QT += widgets

TARGET = myGame

这也是个踩坑点,在这里提前说了。

添加资源文件,名称为 myImages,添加图片:

在这里插入图片描述

设计小方块

新建 mybox.h,添加 OneBox 类的定义:

#ifndef MYBOX_H
#define MYBOX_H

#include <QGraphicsItemGroup>
#include <QGraphicsObject>

// 小方块类
class OneBox : public QGraphicsObject
{
private:
    QColor brushColor;

public:
    OneBox(const QColor& color = Qt::red);
    QRectF boundingRect() const;
    void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget);
    QPainterPath shape() const;
};

#endif  // MYBOX_H

新建 mybox.cpp,添加 OneBox 类的实现代码:

#include "mybox.h"

#include <QPainter>

OneBox::OneBox(const QColor& color) { brushColor = color; }

QRectF OneBox::boundingRect() const
{
    qreal penWidth = 1;
    return QRectF(-10 - penWidth / 2, -10 - penWidth / 2, 20 + penWidth, 20 + penWidth);
}

void OneBox::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget)
{
    // 为小方块使用贴图
    painter->drawPixmap(-10, -10, 20, 20, QPixmap(":/images/box.gif"));
    painter->setBrush(brushColor);

    QColor penColor = brushColor;
    // 将颜色的透明度降低
    penColor.setAlpha(20);
    painter->setPen(penColor);
    painter->drawRect(-10, -10, 20, 20);
}

QPainterPath OneBox::shape() const
{
    QPainterPath path;
    // 形状比边框矩形小 0.5 像素,这样方块组中的小方块才不会发生碰撞
    path.addRect(-9.5, -9.5, 19, 19);
    return path;
}
设计方块组

在 mybox.h 中添加头文件:

#include <QGraphicsItemGroup>

再添加 BoxGroup 类的定义:

// 方块组类
class BoxGroup : public QObject, public QGraphicsItemGroup
{
    Q_OBJECT
private:
    BoxShape currentShape;
    QTransform oldTransform;
    QTimer* timer;

protected:
    void keyPressEvent(QKeyEvent* event);

public:
    enum BoxShape
    {
        IShape,
        JShape,
        LShape,
        OShape,
        SShape,
        TShape,
        ZShape,
        RandomShape
    };

    BoxGroup();
    QRectF boundingRect() const;

    bool isColliding();
    void createBox(const QPointF& point = QPointF(0, 0), BoxShape shape = RandomShape);
    void clearBoxGroup(bool destroyBox = false);
    BoxShape getCurrentShape() { return currentShape; }

signals:
    void needNewBox();
    void gameFinished();

public slots:
    void moveOneStep();
    void startTimer(int interval);
    void stopTimer();
};

到 mybox.cpp 中添加头文件:

#include <QKeyEvent>
#include <QTimer>

添加 BoxGroup 类的实现代码:

// 方块组类

void BoxGroup::keyPressEvent(QKeyEvent* event)
{
    switch (event->key())
    {
        case Qt::Key_Down:
            moveBy(0, 20);
            if (isColliding())
            {
                moveBy(0, -20);

                // 将小方块从方块组中移除到场景中
                clearBoxGroup();

                // 需要显示新的方块
                emit needNewBox();
            }
            break;

        case Qt::Key_Left:
            moveBy(-20, 0);
            if (isColliding())
                moveBy(20, 0);
            break;

        case Qt::Key_Right:
            moveBy(20, 0);
            if (isColliding())
                moveBy(-20, 0);
            break;

        case Qt::Key_Up:
            rotate(90);
            if (isColliding())
                rotate(-90);
            break;

        // 空格键实现坠落
        case Qt::Key_Space:
            moveBy(0, 20);
            while (!isColliding())
            {
                moveBy(0, 20);
            }
            moveBy(0, -20);
            clearBoxGroup();
            emit needNewBox();
            break;
    }
}

BoxGroup::BoxGroup()
{
    setFlags(QGraphicsItem::ItemIsFocusable);

    // 保存变换矩阵,当 BoxGroup 进行旋转后,可以使用它来进行恢复
    oldTransform = transform();

    timer = new QTimer(this);
    connect(timer, SIGNAL(timeout()), this, SLOT(moveOneStep()));
    currentShape = RandomShape;
}

QRectF BoxGroup::boundingRect() const
{
    qreal penWidth = 1;
    return QRectF(-40 - penWidth / 2, -40 - penWidth / 2, 80 + penWidth, 80 + penWidth);
}

// 碰撞检测
bool BoxGroup::isColliding()
{
    QList<QGraphicsItem*> itemList = childItems();
    QGraphicsItem* item;
    // 使用方块组中的每一个小方块来进行判断
    foreach (item, itemList)
    {
        if (item->collidingItems().count() > 1)
            return true;
    }
    return false;
}

// 创建方块
void BoxGroup::createBox(const QPointF& point, BoxShape shape)
{
    static const QColor colorTable[7] =
    {
        QColor(200, 0, 0, 100),
        QColor(255, 200, 0, 100),
        QColor(0, 0, 200, 100),
        QColor(0, 200, 0, 100),
        QColor(0, 200, 255, 100),
        QColor(200, 0, 255, 100),
        QColor(150, 100, 100, 100)
    };

    int shapeID = shape;

    if (shape == RandomShape)
    {
        // 产生 0-6 之间的随机数
        shapeID = qrand() % 7;
    }

    QColor color = colorTable[shapeID];

    QList<OneBox*> list;
    //恢复方块组的变换矩阵
    setTransform(oldTransform);
    for (int i = 0; i < 4; ++i)
    {
        OneBox* temp = new OneBox(color);
        list << temp;
        addToGroup(temp);
    }

    switch (shapeID)
    {
        case IShape:
            currentShape = IShape;
            list.at(0)->setPos(-30, -10);
            list.at(1)->setPos(-10, -10);
            list.at(2)->setPos(10, -10);
            list.at(3)->setPos(30, -10);
            break;

        case JShape:
            currentShape = JShape;
            list.at(0)->setPos(10, -10);
            list.at(1)->setPos(10, 10);
            list.at(2)->setPos(-10, 30);
            list.at(3)->setPos(10, 30);
            break;

        case LShape:
            currentShape = LShape;
            list.at(0)->setPos(-10, -10);
            list.at(1)->setPos(-10, 10);
            list.at(2)->setPos(-10, 30);
            list.at(3)->setPos(10, 30);
            break;

        case OShape:
            currentShape = OShape;
            list.at(0)->setPos(-10, -10);
            list.at(1)->setPos(10, -10);
            list.at(2)->setPos(-10, 10);
            list.at(3)->setPos(10, 10);
            break;

        case SShape:
            currentShape = SShape;
            list.at(0)->setPos(10, -10);
            list.at(1)->setPos(30, -10);
            list.at(2)->setPos(-10, 10);
            list.at(3)->setPos(10, 10);
            break;

        case TShape:
            currentShape = TShape;
            list.at(0)->setPos(-10, -10);
            list.at(1)->setPos(10, -10);
            list.at(2)->setPos(30, -10);
            list.at(3)->setPos(10, 10);
            break;

        case ZShape:
            currentShape = ZShape;
            list.at(0)->setPos(-10, -10);
            list.at(1)->setPos(10, -10);
            list.at(2)->setPos(10, 10);
            list.at(3)->setPos(30, 10);
            break;

        default: break;
    }
    // 设置位置
    setPos(point);
    // 如果开始就发生碰撞,说明已经结束游戏
    if (isColliding())
    {
        stopTimer();
        emit gameFinished();
    }
}

// 删除方块组中的所有小方块
void BoxGroup::clearBoxGroup(bool destroyBox)
{
    QList<QGraphicsItem*> itemList = childItems();
    QGraphicsItem* item;
    foreach (item, itemList)
    {
        removeFromGroup(item);
        if (destroyBox)
        {
            OneBox* box = (OneBox*)item;
            box->deleteLater();
        }
    }
}

// 向下移动一步
void BoxGroup::moveOneStep()
{
    moveBy(0, 20);
    if (isColliding())
    {
        moveBy(0, -20);
        // 将小方块从方块组中移除到场景中
        clearBoxGroup();
        emit needNewBox();
    }
}

// 开启定时器
void BoxGroup::startTimer(int interval) { timer->start(interval); }

// 停止定时器
void BoxGroup::stopTimer() { timer->stop(); }
添加游戏场景

新建一个 C++ 类,类名为 MyView,基类为 GraphicsView,继承自 QWidget:

在这里插入图片描述

更改 myview.h:

#ifndef MYVIEW_H
#define MYVIEW_H

#include <QGraphicsView>
#include <QWidget>

class BoxGroup;

class MyView : public GraphicsView
{
private:
    BoxGroup* boxGroup;
    BoxGroup* nextBoxGroup;
    QGraphicsLineItem* topLine;
    QGraphicsLineItem* bottomLine;
    QGraphicsLineItem* leftLine;
    QGraphicsLineItem* rightLine;
    qreal gameSpeed;
    QList<int> rows;

    void initView();
    void initGame();
    void updateScore(const int fullRowNum = 0);

public:
    explicit MyView(QWidget* parent = 0);

public slots:
    void startGame();
    void clearFullRows();
    void moveBox();
    void gameOver();
};

#endif  // MYVIEW_H

更改 myview.cpp:

#include "myview.h"

#include <QIcon>

#include "mybox.h"

// 游戏的初始速度
static const qreal INITSPEED = 500;

// 初始化游戏界面
void MyView::initView()
{
    // 使用抗锯齿渲染
    setRenderHint(QPainter::Antialiasing);
    // 设置缓存背景,这样可以加快渲染速度
    setCacheMode(CacheBackground);
    setWindowTitle(tr("MyBox方块游戏"));
    setWindowIcon(QIcon(":/images/icon.png"));
    setMinimumSize(810, 510);
    setMaximumSize(810, 510);
    // 设置场景
    QGraphicsScene* scene = new QGraphicsScene;
    scene->setSceneRect(5, 5, 800, 500);
    scene->setBackgroundBrush(QPixmap(":/images/background.png"));
    setScene(scene);
    // 方块可移动区域的四条边界线
    topLine = scene->addLine(197, 47, 403, 47);
    bottomLine = scene->addLine(197, 453, 403, 453);
    leftLine = scene->addLine(197, 47, 197, 453);
    rightLine = scene->addLine(403, 47, 403, 453);
    // 当前方块组和提示方块组
    boxGroup = new BoxGroup;
    connect(boxGroup, SIGNAL(needNewBox()), this, SLOT(clearFullRows()));
    connect(boxGroup, SIGNAL(gameFinished()), this, SLOT(gameOver()));
    scene->addItem(boxGroup);
    nextBoxGroup = new BoxGroup;
    scene->addItem(nextBoxGroup);

    startGame();
}

// 初始化游戏
void MyView::initGame()
{
    boxGroup->createBox(QPointF(300, 70));
    boxGroup->setFocus();
    boxGroup->startTimer(INITSPEED);
    gameSpeed = INITSPEED;
    nextBoxGroup->createBox(QPointF(500, 70));
}

// 更新分数
void MyView::updateScore(const int fullRowNum) {}

MyView::MyView(QWidget* parent) : QGraphicsView(parent) { initView(); }

// 开始游戏
void MyView::startGame() { initGame(); }

// 清空满行
void MyView::clearFullRows()
{
    // 获取比一行方格较大的矩形中包含的所有小方块
    for (int y = 429; y > 50; y -= 20)
    {
        QList<QGraphicsItem*> list = scene()->items(199, y, 202, 22, Qt::ContainsItemShape);
        // 如果该行已满
        if (list.count() == 10)
        {
            foreach (QGraphicsItem* item, list)
            {
                OneBox* box = (OneBox*)item;
                box->deleteLater();
            }
            // 保存满行的位置
            rows << y;
        }
    }

    if (rows.count() > 0)
    {
        // 如果有满行,下移满行上面的各行再出现新的方块组
        moveBox();
    }
    else  // 如果没有满行,则直接出现新的方块组
    {
        boxGroup->createBox(QPointF(300, 70), nextBoxGroup->getCurrentShape());
        // 清空并销毁提示方块组中的所有小方块
        nextBoxGroup->clearBoxGroup(true);
        nextBoxGroup->createBox(QPointF(500, 70));
    }
}

// 下移满行上面的所有小方块
void MyView::moveBox()
{
    // 从位置最靠上的满行开始
    for (int i = rows.count(); i > 0; i--)
    {
        int row = rows.at(i - 1);
        foreach (QGraphicsItem* item, scene()->items(199, 49, 202, row - 47, Qt::ContainsItemShape))
        {
            item->moveBy(0, 20);
        }
    }
    // 更新分数
    updateScore(rows.count());
    // 将满行列表清空为 0
    rows.clear();
    // 等所有行下移以后再出现新的方块组
    boxGroup->createBox(QPointF(300, 70), nextBoxGroup->getCurrentShape());
    nextBoxGroup->clearBoxGroup(true);
    nextBoxGroup->createBox(QPointF(500, 70));
}

// 游戏结束
void MyView::gameOver() {}
添加主函数

新建 main.cpp,添加代码:

#include <QApplication>
#include <QTextCodec>
#include <QTime>

#include "myview.h"

int main(int argc, char* argv[])
{
    QApplication app(argc, argv);

    QTextCodec::setCodecForTr(QTextCodec::codecForLocale());

    // 设置随机数的初始值
    qsrand(QTime(0, 0, 0).secsTo(QTime::currentTime()));

    MyView view;
    view.show();

    return app.exec();
}

测试

运行程序。

果不其然的报错了。

主要是一些 Qt4 和 Qt5 的差别带来的问题。

踩坑点1:rotate 失效

函数 void BoxGroup::keyPressEvent(QKeyEvent* event) 原代码:

void BoxGroup::keyPressEvent(QKeyEvent *event)
{
    switch (event->key())
    {
    case Qt::Key_Down :
        moveBy(0, 20);
        if (isColliding()) {
            moveBy(0, -20);

            // 将小方块从方块组中移除到场景中
            clearBoxGroup();

            // 需要显示新的方块
            emit needNewBox();
        }
        break;

    case Qt::Key_Left :
        moveBy(-20, 0);
        if (isColliding())
            moveBy(20, 0);
        break;

    case Qt::Key_Right :
        moveBy(20, 0);
        if (isColliding())
            moveBy(-20, 0);
        break;

    case Qt::Key_Up :
        rotate(90);
        if(isColliding())
            rotate(-90);
        break;

    // 空格键实现坠落
    case Qt::Key_Space :
        moveBy(0, 20);
        while (!isColliding()) {
            moveBy(0, 20);
        }
        moveBy(0, -20);
        clearBoxGroup();
        emit needNewBox();
        break;
    }
}

其中的 rotate 函数失效。

在 Qt5 中,QGraphicsItem::rotate 已经不再使用,而是使用 setRotation。

修改为:

void BoxGroup::keyPressEvent(QKeyEvent* event)
{
    qreal oldRotate;

    switch (event->key())
    {
        // 下移
        case Qt::Key_Down:
            moveBy(0, 20);
            if (isColliding())
            {
                moveBy(0, -20);
                // 将小方块从方块组中移除到场景中
                clearBoxGroup();
                // 需要显示新的方块
                emit needNewBox();
            }
            break;
        // 左移
        case Qt::Key_Left:
            moveBy(-20, 0);
            if (isColliding())
                moveBy(20, 0);
            break;
        // 右移
        case Qt::Key_Right:
            moveBy(20, 0);
            if (isColliding())
                moveBy(-20, 0);
            break;
        // 旋转
        case Qt::Key_Up:
            // 在 Qt5 中,QGraphicsItem::rotate 已经不再使用,而是使用 setRotation
            /* old code */
            //    rotate(90);
            //   if (isColliding())
            //       rotate(-90);
            //    break;
            /* old code */

            oldRotate = rotation();
            if (oldRotate >= 360)
            {
                oldRotate = 0;
            }
            setRotation(oldRotate + 90);
            if (isColliding())
            {
                setRotation(oldRotate - 90);
            }
            break;

        // 空格键实现坠落
        case Qt::Key_Space:
            moveBy(0, 20);
            while (!isColliding())
            {
                moveBy(0, 20);
            }
            moveBy(0, -20);
            clearBoxGroup();
            emit needNewBox();
            break;
    }
}

参考博客:Qt及Qt Quick开发实战精解项目二俄罗斯方块 rotate失效方法报错

踩坑点2:items 方法报错

在 void MyView::clearFullRows() 函数里有这样一行代码:

QList<QGraphicsItem*> list = scene()->items(199, y, 202, 22, Qt::ContainsItemShape);

报错信息:

myview.cpp:75:47: error: no matching member function for call to 'items'
qgraphicsscene.h:158:28: note: candidate function not viable: requires at most 4 arguments, but 5 were provided
qgraphicsscene.h:159:28: note: candidate function not viable: requires at most 4 arguments, but 5 were provided
qgraphicsscene.h:160:28: note: candidate function not viable: requires at most 4 arguments, but 5 were provided
qgraphicsscene.h:161:28: note: candidate function not viable: requires at most 4 arguments, but 5 were provided
qgraphicsscene.h:175:35: note: candidate function not viable: requires at least 6 arguments, but 5 were provided
qgraphicsscene.h:156:28: note: candidate function not viable: allows at most single argument 'order', but 5 arguments were provided

大概意思是参数不匹配。

修改为:

QList<QGraphicsItem*> list = scene()->items(199, y, 202, 22, Qt::ContainsItemShape, Qt::AscendingOrder);

新增的一项 Qt::AscendingOrder 的意思是对 QList 的内容正序排序。

参考博客:Qt及Qt Quick开发实战精解项目二俄罗斯方块 items方法报错

踩坑点3:setCodecForTr 失效

在 main.cpp 中有这样一行代码:

QTextCodec::setCodecForTr(QTextCodec::codecForLocale());

这行代码主要解决 Qt 中文乱码的问题。

但是在 Qt5 中 setCodecForTr 函数已经失效了,我们改成:

QTextCodec::setCodecForLocale(QTextCodec::codecForName("utf-8"));

这个视个人电脑使用的编码决定。

踩坑点4:不要在中文路径下运行 Qt 项目

就是这样,喵~

踩坑点5:multiple definition of `qMain(int, char**)’

报错信息:

error: multiple definition of `qMain(int, char**)'

这是在 pro 文件中出的问题,频繁的添加以及移除文件,导致 HEADERS 以及 SOURCES 中会重复添加。

在这里插入图片描述

这里 main.cpp 重复了,删掉一个即可。

测试效果

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

蓝桥杯刷题

欢迎来到Cefler的博客&#x1f601; &#x1f54c;博客主页&#xff1a;那个传说中的man的主页 &#x1f3e0;个人专栏&#xff1a;题目解析 &#x1f30e;推荐文章&#xff1a;题目大解析&#xff08;3&#xff09; &#x1f449;&#x1f3fb;最大降雨量 原题链接&#xff1…

OpenCV官方教程中文版 —— 分水岭算法图像分割

OpenCV官方教程中文版 —— 分水岭算法图像分割 前言一、原理二、示例三、完整代码 前言 本节我们将要学习 • 使用分水岭算法基于掩模的图像分割 • 函数&#xff1a;cv2.watershed() 一、原理 任何一副灰度图像都可以被看成拓扑平面&#xff0c;灰度值高的区域可以被看成…

Hand Avatar: Free-Pose Hand Animation and Rendering from Monocular Video

Github&#xff1a; https://seanchenxy.github.io/HandAvatarWeb 1、结构摘要 MANO-HD模型&#xff1a;作为高分辨率网络拓扑来拟合个性化手部形状将手部几何结构分解为每个骨骼的刚性部分&#xff0c;再重新组合成对的几何编码&#xff0c;得到一个跨部分的一致占用场纹理建…

2.数据结构-链表

概述 目标 链表的存储结构和特点链表的几种分类及各自的存储结构链表和数组的差异刷题(反转链表) 概念及存储结构 先来看一下动态数组 ArrayList 存在哪些弊端 插入&#xff0c;删除时间复杂度高需要一块连续的存储空间&#xff0c;对内存要求比较高&#xff0c;比如要申请…

CentOS 安装 Hadoop Local (Standalone) Mode 单机模式

CentOS 安装 Hadoop Local (Standalone) Mode 单机模式 Hadoop Local (Standalone) Mode 单机模式 1. 修改yum源 并升级内核和软件 curl -o /etc/yum.repos.d/CentOS-Base.repo https://mirrors.aliyun.com/repo/Centos-7.repoyum clean allyum makecacheyum -y update2. 安…

如何让salesforce提交待审批后不锁定记录

在 Salesforce 中&#xff0c;默认情况下&#xff0c;当记录被提交待审批时&#xff0c;它会被锁定以防止其他用户对其进行修改。这是为了确保审批过程中数据的完整性和一致性。然而&#xff0c;有时可能希望提交待审批后不锁定记录&#xff0c;这时可以使用Apex代码来实现: Ap…

idea自动编译以及修改代码后需要执行 mvn clean install 才生效

idea自动编译以及修改代码后需要执行 mvn clean install 才生效 一. idea热部署一、开启IDEA的自动编译&#xff08;静态&#xff09;二、开启IDEA的自动编译&#xff08;动态&#xff09;三、开启IDEA的热部署策略&#xff08;非常重要&#xff09; 二. IDEA 中项目代码修改后…

Vue 插槽 组件插入不固定内容

定义好一个组件&#xff0c;如果想插入图片或视频这非常不好的控制应该显示什么&#xff0c;这个时候可以使用插槽插入自定义内容 默认插槽 <Login><template><h1>我是插入的内容</h1></template></Login >组件 <slot></slot>…

一文了解Elasticsearch

数据分类 数据按数据结构分类主要有三种&#xff1a;结构化数据、半结构化数据和非结构化数据。 结构化数据 结构化数据具有明确定义数据模型和格式的数据类型。 特点&#xff1a; 数据具有固定的结构和模式。 数据项明确定义数据类型和长度。 适合用于数据查询、过滤和分…

ZOC8 for Mac:超越期待的终端仿真器

在Mac上&#xff0c;一个优秀的终端仿真器是每位开发者和系统管理员的必备工具。ZOC8&#xff0c;作为一款广受好评的终端仿真器&#xff0c;以其强大的功能和易用性&#xff0c;已经在Mac用户中积累了良好的口碑。本文将为您详细介绍ZOC8的各项特性&#xff0c;以及为什么它会…

MSQL系列(十二) Mysql实战-为什么索引要建立在被驱动表上

Mysql实战-为什么索引要建立在被驱动表上 前面我们讲解了BTree的索引结构&#xff0c;也详细讲解下 left Join的底层驱动表 选择原理&#xff0c;那么今天我们来看看到底如何用以及如何建立索引和索引优化 开始之前我们先提一个问题&#xff0c; 为什么索引要建立在被驱动表上…

【NI-DAQmx入门】传感器基础知识

1.什么是传感器&#xff1f; 传感器可将真实的现象&#xff08;例如温度或压力&#xff09;转换为可测量的电流和电压&#xff0c;因而对于数据采集应用必不可少。接下来我们将介绍您所需的测量类型及其对应的传感器类型。在开始之前&#xff0c;您还可以先了解一些传感器术语&…

uniapp 开发微信小程序 v-bind给子组件传递函数,该函数中的this不是父组件的二是子组件的this

解决办法&#xff1a;子组件通过缓存子组件this然后&#xff0c;用bind改写this 这个方法因为定义了全局变量that 那么该变量就只能用一次&#xff0c;不然会有赋值覆盖的情况。 要么就弃用v-bind传入函数,改为emit传入自定义事件 [uniapp] uview(1.x) 二次封装u-navbar 导致…

[MySQL]——SQL预编译、动态sql

键盘敲烂&#xff0c;年薪30万&#x1f308; 目录 一、SQL的预编译 &#x1f4d5;一条SQL语句的执行过程 &#x1f4d5;弊端 &#x1f4d5;预编译SQL的优势 &#x1f4d5;两种参数占位符 &#x1f4d5;小结 二、动态SQL &#x1f4d5;概念介绍&#xff1a; &#x1f4…

ROS自学笔记二十: Gazebo里面仿真环境搭建

Gazebo 中创建仿真实现方式有两种:1直接添加内置组件创建仿真环境2: 手动绘制仿真环境 1.添加内置组件创建仿真环境 1.1启动 Gazebo 并添加组件 1.2保存仿真环境 添加完毕后&#xff0c;选择 file ---> Save World as 选择保存路径(功能包下: worlds 目录)&#xff0c;文…

Docker:命令

Docker&#xff1a;命令 1. 创建MySQL的命令解读2. 基础命令3. 案例 查看DockerHub&#xff0c;拉取Nginx镜像&#xff0c;创建并运行Nginx容器4. 命令别名附录 1. 创建MySQL的命令解读 docker run :创建并运行一个容器&#xff0c;-d 是让容器在后台运行--name:给容器起一个名…

Java日志组件介绍之一

一、前言 前段时间爆出Log4j安全漏洞的事情&#xff0c;XX云因未及时报告漏洞被工信部暂停网络安全威胁和漏洞信息共享平台合作单位&#xff08;https://www.cstis.cn/&#xff09;&#xff0c;话说Java的日志组件真是多而且也比较乱&#xff0c;后续几篇文章就聊一下各日志组…

【嵌入式】【GIT】如何迁移老的GIF到新的仓库时使用LFS功能并保持LOG不变

一、正常迁移流程 假设有仓库 ssh://old/buildroot-201902 需要迁移到新的仓库 ssh://old/buildroot-201902时,我们可以使用以下命令来完成: # 下载老的仓库 git clone ssh://old/buildroot-201902 # 向新的仓库上传所有的tags git push ssh://new/buildroot-201902 --tag…

【Linux】:Linux开发工具之Linux编辑器vim的使用

&#x1f52b;1.Linux编辑器-vim使用 &#x1f4e4; vi/vim的区别简单点来说&#xff0c;它们都是多模式编辑器&#xff0c;不同的是vim是vi的升级版本&#xff0c;它不仅兼容vi的所有指令&#xff0c;而且还有一些新的特性在里面。例如语法加亮&#xff0c;可视化操作不仅可以…

【ARMv8 SIMD和浮点指令编程】NEON 存储指令——如何将数据从寄存器存储到内存?

和加载指令一样,NEON 有一系列的存储指令。比如 ST1、ST2、ST3、ST4。 1 ST1 (multiple structures) 从一个、两个、三个或四个寄存器存储多个单元素结构。该指令将元素从一个、两个、三个或四个 SIMD&FP 寄存器存储到内存,无需交错。每个寄存器的每个元素都被存储。 …