设计概述
这个模块是一个基于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+滚轮缩放
支持拖拽移动节点位置
技术亮点
灵活的状态管理
通过枚举清晰区分不同类型的状态
每个状态节点独立维护自己的属性和外观
高效的渲染机制
使用QPainter进行高效的自定义绘制
通过渐变和圆角效果提升视觉体验
针对选中状态提供特殊视觉效果
交互体验优化
支持Ctrl+滚轮平滑缩放
节点拖拽时实时更新连线位置
右键菜单提供上下文相关操作
可扩展的架构
通过信号槽机制实现组件间通信
状态数据与UI分离,便于扩展
总结
这个蓝图式技能编辑器状态机模块为游戏开发提供了一套直观、灵活的状态管理工具。通过精心设计的可视化界面和流畅的交互体验,开发者可以高效地构建复杂的技能状态转换逻辑。模块采用Qt强大的图形框架实现,具有良好的可扩展性和稳定性,能够满足游戏开发中各种复杂的状态管理需求。
当然最终实现效果 跟unity的状态机 差不多,要看你自己怎么自绘了,比如可以给状态块添加渐变模型。内部实现结果的方式,反正就像qwidget一样进去绘制,然后把结点间的连续,做成连续播放,就可以让美术清晰的知道状态间的过渡效果
unity的状态机如下: