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

发布于:2025-06-20 ⋅ 阅读:(17) ⋅ 点赞:(0)

设计概述

这个模块是一个基于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的状态机如下:

 


网站公告

今日签到

点亮在社区的每一天
去签到