Qt蓝图式技能编辑器状态机模块设计与实现

设计概述

这个模块是一个基于Qt的蓝图式技能编辑器状态机,主要用于游戏开发中的技能状态管理。核心功能包括:

  • 状态节点(开始、结束、普通状态)的可视化

  • 状态间连线的绘制与管理

  • 状态转换逻辑的可视化编辑

  • 动作选择与配置

核心类设计

1. 状态节点类 (QSkeBlendGraphics)

复制代码
class QSkeBlendGraphics : public QGraphicsItem {
public:
    // 状态类型枚举
    enum StateType {
        SKE_BELEN_STATUE_EMPTY,    // 空状态
        SKE_BELEN_STATUE_NORMAL,   // 普通状态
        SKE_BELEN_STATUE_BEGIN,    // 开始状态
        SKE_BELEN_STATUE_END       // 结束状态
    };
    
    // 核心方法
    void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override;
    QRectF boundingRect() const override;
    void mouseMoveEvent(QGraphicsSceneMouseEvent *event) override;
    void setSkaName(QString name); // 设置动作名称
    
private:
    StateType m_state;     // 当前状态类型
    QString m_name;        // 状态名称
    QString m_skaName;     // 关联的动作名称
    QPointF m_pos;         // 位置信息
    int m_nProgerssValue;  // 进度值(0-100)
    QAnimationDlg* m_pAniViewWidget; // 父窗口指针
};

2. 状态连线类 (QLineArray)

复制代码
class QLineArray : public QGraphicsItem {
public:
    // 核心方法
    void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override;
    QRectF boundingRect() const override;
    void setLineItem(QPointF startP, QPointF endP); // 设置连线起止点
    
private:
    QSkeBlendGraphics* m_skeStar; // 起始状态节点
    QSkeBlendGraphics* m_skeEnd;  // 结束状态节点
    QPointF m_EndP;               // 终点位置
    QPointF m_MidP;               // 中点位置(用于箭头计算)
    QPointF m_points[3];          // 箭头三角形点
};

3. 状态场景管理类 (QSkeblendScene)

复制代码
class QSkeblendScene : public QGraphicsScene {
public:
    // 场景操作
    void drawBackground(QPainter *painter, const QRectF &rect) override;
    void deleteSelect(); // 删除选中项
    void contextMenuEvent(QGraphicsSceneContextMenuEvent* event) override;
    
private:
    QAnimationDlg* m_pAniWnd; // 父窗口
    bool m_bShowFrid;          // 是否显示网格
    QSkeBlendGraphics* m_pSelCOpy; // 当前复制的状态
};

4. 状态视图类 (QSkeblendGraphicsView)

复制代码
class QSkeblendGraphicsView : public QGraphicsView {
public:
    // 视图操作
    void wheelEvent(QWheelEvent* event) override; // 滚轮缩放
    void drawBackground(QPainter *painter, const QRectF &rect) override;
    
    // 缩放功能
    void zoomIn();
    void zoomOut();
    void zoomOriginal();
    
private:
    bool m_wheelZoomEnabled; // 是否启用滚轮缩放
    qreal m_zoomFactor;      // 当前缩放因子
};

核心功能实现

状态节点绘制

复制代码
void QSkeBlendGraphics::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) {
    // 根据状态类型设置不同颜色
    switch(m_state) {
    case SKE_BELEN_STATUE_NORMAL:
        painter->setBrush(isSelected() ? QColor(100,100,100) : QColor(118,118,118));
        break;
    case SKE_BELEN_STATUE_BEGIN:
        painter->setBrush(QColor(45,206,46)); // 绿色
        break;
    case SKE_BELEN_STATUE_END:
        painter->setBrush(QColor(219,12,12)); // 红色
        break;
    case SKE_BELEN_STATUE_EMPTY:
        painter->setBrush(QColor(114,124,114)); // 灰色
        break;
    }
    
    // 绘制圆角矩形作为状态节点主体
    QRect rcRectItem(10, 10, RECT_BLEND_GRAPHICS_NE_WIGHT, RECT_BLEND_GRAPHICS_NE_HEIGHT);
    painter->drawRoundRect(rcRectItem, RECT_BLEND_GRAPHICS_NE_RECT, RECT_BLEND_GRAPHICS_NE_RECT);
    
    // 添加渐变效果
    QLinearGradient Linear(QPointF(10, 10), QPointF(10, rcRectItem.height()));
    // ... 根据状态类型和选中状态设置渐变颜色
    
    // 绘制状态名称
    painter->setPen(QColor(225,225,225));
    QFont font = painter->font();
    font.setPointSize(10);
    painter->setFont(font);
    painter->drawText(QPoint(10 + nXposFont, 25), m_name);
    
    // 绘制进度条(如果有)
    if(m_nProgerssValue < 99) {
        painter->setBrush(QColor(45,46,45));
        painter->drawRoundedRect(nXposStar, nYPosStar, width, height, 2, 2);
        
        // 绘制进度
        int progressWidth = (m_nProgerssValue * totalWidth) / 100;
        painter->setBrush(QColor(0,122,204));
        painter->drawRect(progressRect);
    }
}

状态连线绘制

复制代码
void QLineArray::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) {
    painter->setRenderHint(QPainter::Antialiasing, true);
    
    // 设置连线颜色(选中/未选中)
    if (isSelected()) {
        painter->setBrush(m_SelColor);
        painter->setPen(QPen(m_SelColor, 2, Qt::SolidLine));
    } else {
        painter->setBrush(m_Color);
        painter->setPen(QPen(m_Color, 2, Qt::SolidLine));
    }
    
    // 绘制连接线
    QLineF line(0, 0, m_EndP.x(), m_EndP.y());
    painter->drawLine(line);
    
    // 绘制箭头
    painter->drawPolygon(m_points, 3);
}

void QLineArray::CreatePointNodes() {
    // 计算箭头位置
    float angle = atan2(m_MidP.y(), m_MidP.x()) + PI;
    
    const float arrowLength = 15;
    const float arrowAngle = 0.3;
    
    m_points[0] = m_MidP;
    m_points[1].setX(m_MidP.x() + arrowLength * cos(angle - arrowAngle));
    m_points[1].setY(m_MidP.y() + arrowLength * sin(angle - arrowAngle));
    m_points[2].setX(m_MidP.x() + arrowLength * cos(angle + arrowAngle));
    m_points[2].setY(m_MidP.y() + arrowLength * sin(angle + arrowAngle));
}

场景背景绘制

复制代码
void QSkeblendScene::drawBackground(QPainter *painter, const QRectF &rect) {
    if (m_bShowFrid) {
        // 绘制网格背景
        for (int i = 0; i < 300; i++) {
            if (i % 10 == 0) {
                painter->setPen(QPen(QColor(25,25,25), 1));
            } else {
                painter->setPen(QPen(QColor(34,34,34), 1));
            }
            
            // 绘制水平线
            painter->drawLine(startX, startY + i * gridH, 
                             gridW * 1000 + startX, startY + i * gridH);
            
            // 绘制垂直线
            painter->drawLine(startX + gridW * i, startY, 
                             startX + i * gridW, startY + gridH * 1000);
        }
    } else {
        // 纯色背景
        painter->setPen(Qt::NoPen);
        painter->setBrush(QBrush(QColor(192,192,192)));
        painter->drawRect(rect);
    }
}

视图缩放功能

复制代码
void QSkeblendGraphicsView::wheelEvent(QWheelEvent* event) {
    if (m_wheelZoomEnabled && (event->modifiers() & Qt::ControlModifier)) {
        if (event->angleDelta().y() > 0) {
            zoomIn(); // 放大
        } else {
            zoomOut(); // 缩小
        }
    } else {
        QGraphicsView::wheelEvent(event);
    }
}

void QSkeblendGraphicsView::zoomIn() {
    m_zoomFactor *= 1.2;
    if (m_zoomFactor > 256) m_zoomFactor = 256;
    performZoom();
}

void QSkeblendGraphicsView::zoomOut() {
    m_zoomFactor /= 1.2;
    if (m_zoomFactor < 0.5) m_zoomFactor = 0.5;
    performZoom();
}

void QSkeblendGraphicsView::performZoom() {
    QTransform transform;
    transform.scale(m_zoomFactor, m_zoomFactor);
    setTransform(transform);
}

功能流程图

效果展示

状态节点效果

  • 开始状态:绿色圆角矩形

  • 结束状态:红色圆角矩形

  • 普通状态:灰色圆角矩形

  • 空状态:深灰色圆角矩形

  • 选中状态:蓝色边框和发光效果

  • 进度显示:节点底部蓝色进度条

连线效果

  • 状态间连线为直线

  • 连线中点处有三角形箭头指示方向

  • 选中连线时变为蓝色

场景效果

  • 可切换网格背景/纯色背景

  • 支持Ctrl+滚轮缩放

  • 支持拖拽移动节点位置

技术亮点

  1. 灵活的状态管理

    • 通过枚举清晰区分不同类型的状态

    • 每个状态节点独立维护自己的属性和外观

  2. 高效的渲染机制

    • 使用QPainter进行高效的自定义绘制

    • 通过渐变和圆角效果提升视觉体验

    • 针对选中状态提供特殊视觉效果

  3. 交互体验优化

    • 支持Ctrl+滚轮平滑缩放

    • 节点拖拽时实时更新连线位置

    • 右键菜单提供上下文相关操作

  4. 可扩展的架构

    • 通过信号槽机制实现组件间通信

    • 状态数据与UI分离,便于扩展

总结

这个蓝图式技能编辑器状态机模块为游戏开发提供了一套直观、灵活的状态管理工具。通过精心设计的可视化界面和流畅的交互体验,开发者可以高效地构建复杂的技能状态转换逻辑。模块采用Qt强大的图形框架实现,具有良好的可扩展性和稳定性,能够满足游戏开发中各种复杂的状态管理需求。

当然最终实现效果 跟unity的状态机 差不多,要看你自己怎么自绘了,比如可以给状态块添加渐变模型。内部实现结果的方式,反正就像qwidget一样进去绘制,然后把结点间的连续,做成连续播放,就可以让美术清晰的知道状态间的过渡效果

unity的状态机如下:

相关推荐
用户805533698034 天前
不止三件套:QObject 属性系统全关键字与运行时反射!
c++·qt
xcyxiner4 天前
DicomViewer (vcpkg Windows和ubuntu编译)7
qt
Quz9 天前
QML Hello World 入门示例
qt
xcyxiner12 天前
DicomViewer (dcmtk读取dcm文件)5
qt
xcyxiner13 天前
DicomViewer (后台线程处理文件)4
qt
xcyxiner13 天前
DicomViewer (添加模型类)3
qt
xcyxiner14 天前
DicomViewer (目录调整) 2
qt
xcyxiner14 天前
dcmtk vtk vtk-dicom(gdcm) 编译(debug) v2
qt
LDR00616 天前
Type-C 快充全面升级!LDR6601 赋能个人护理便携电机,重塑剃须刀 / 理发器新体验
c语言·开发语言
雪碧聊技术16 天前
Tree.js是什么?一文讲透
开发语言·javascript·ecmascript