前言

先感叹下AI大人的强大,其实AI发展到今天这个地步,以往的那些编辑器拓展特技都可以抛之脑后了,例如我年轻时的一篇文章:Unity编辑器拓展Wiki开源项目,装得下,世界都是你的,那么现在编辑器工具的开发流程是什么样的呢?我总结了以下步骤:

  1. 下载 https://github.com/Unity-Technologies/UnityCsReference 源码,并将想要复刻,或者学习的部分投喂给AI
  2. 开始和AI结对编程(说是结对,其实是单方面的拿鞭子抽AI干活

当然了,AI对编辑器工具的影响不仅仅在Unity,其实最大收益方反而是UE,鄙人不才,几年前也在UE写过一段时间的Slate技能编辑器,只能说写的想死,比较Slate的语法相较于IMGUI还是过于反人类了一些,但是在AI这边统统不是问题,你只需要提界面需求,其余的都交给AI,体验无敌
说了这么多,回到今天的正题,起因是用到节点图的地方越来越多,原本一些易用性也在不断被放大,其中比较直观影响使用体验的就是连线不美观的问题,现在长这样:

可以看到,等待节点链接回Start节点的那条绿色的线,非常的丑陋

今天就要着手解决这个问题,那么怎么解决呢,交给AI就好

1
2
3
4
5

@NodePortLineView_UnityEdgeView.cs 这是基于unity graphview edge实现的一个线段绘制类,其有一个问题是对于一个从后往前链接的连线,无法美观的展示。解决方案是:现有出端口A,入端口B
,从A开始水平往外延伸20px 添加一个顶点(A.x + 20, A.y),然后获取出端口和入端口的中点(center_x,center_y),以(A.x + 20,
center_y)作为下一个顶点,然后是(B.x, center_y),最后是(B.x - 20, B.y),请帮我实现这个方案,应该要基于edgecontrol实现,这是GraphView源码:GraphViewEditor/ ,可以参考

然后经过一段时间的激烈讨论,结果就变成了这样:
美观程度提升了几个数量级,我给满分

以下是具体的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
using System;  
using UnityEngine;
using UnityEditor.Experimental.GraphView;
using UnityEngine.UIElements;

namespace NKG.Editor
{
/// <summary>
/// 自定义EdgeControl,改进连线绘制逻辑,特别是对从后往前连接的情况
/// 参考ANGraphViewEdgeControl的实现方法
/// </summary>
public class CustomEdgeControl : EdgeControl
{
private const float k_HorizontalExtension = 50.0f; // 水平延伸距离
private const float k_ArrowSize = 16.0f; // 箭头大小
private const float k_ArrowWidth = 0.6f; // 箭头宽度比例

public CustomEdgeControl()
{ // 重写generateVisualContent来自定义绘制逻辑
this.generateVisualContent = new Action<MeshGenerationContext>(OnGenerateVisualContent);
}
private void OnGenerateVisualContent(MeshGenerationContext mgc)
{ if (this.edgeWidth <= 0)
return;

// 重要:调用UpdateRenderPoints更新交互检测点
this.UpdateRenderPoints();

DrawStraightLines(mgc);
}
protected override void UpdateRenderPoints()
{ // 先调用基类方法来计算控制点和布局
base.UpdateRenderPoints();

// 重新计算我们自己的渲染点用于交互检测
var st = this.parent.ChangeCoordinatesTo((VisualElement)this, from);
var et = this.parent.ChangeCoordinatesTo((VisualElement)this, to);
var offset = k_HorizontalExtension;

// 访问基类的m_RenderPoints字段
var renderPointsField = typeof(EdgeControl).GetField("m_RenderPoints",
System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
var renderPoints = renderPointsField.GetValue(this) as System.Collections.Generic.List<Vector2>;

if (renderPoints != null)
{ renderPoints.Clear();

// 根据我们的绘制逻辑添加渲染点
if (st.x < et.x)
{ // 正向连接:简化路径
renderPoints.Add(st);
renderPoints.Add(new Vector2(st.x + offset, st.y));
renderPoints.Add(new Vector2(et.x - offset, et.y));
renderPoints.Add(et);
} else
{
// 反向连接:完整路径
var centerY = (st.y + et.y) * 0.5f;
renderPoints.Add(st);
renderPoints.Add(new Vector2(st.x + offset, st.y));
renderPoints.Add(new Vector2(st.x + offset, centerY));
renderPoints.Add(new Vector2(et.x - offset, centerY));
renderPoints.Add(new Vector2(et.x - offset, et.y));
renderPoints.Add(et);
} } }
private void DrawStraightLines(MeshGenerationContext mgc)
{ var painter = mgc.painter2D;

painter.lineWidth = edgeWidth;
painter.strokeColor = outputColor;

var offset = k_HorizontalExtension;

var st = this.parent.ChangeCoordinatesTo((VisualElement)this, from);
var et = this.parent.ChangeCoordinatesTo((VisualElement)this, to);

painter.BeginPath();
painter.MoveTo(st); // 起点A

// 优化:对于正向连接(出端口x < 入端口x),使用简化路径
if (st.x < et.x)
{ // 正向连接:A → (A.x + offset, A.y) → (B.x - offset, B.y) → B
var p1 = new Vector2(st.x + offset, st.y); // 从A往右延伸
var p2 = new Vector2(et.x - offset, et.y); // 到B往左延伸

painter.LineTo(p1); // 水平延伸到 (A.x + offset, A.y) painter.LineTo(p2); // 直线连接到 (B.x - offset, B.y) painter.LineTo(et); // 水平到终点B
}
else
{
// 反向连接:使用完整的5点路径
// 1. 从A开始水平往外延伸offset距离
var p1 = new Vector2(st.x + offset, st.y);

// 2. 获取出端口和入端口的中点Y坐标
var centerY = (st.y + et.y) * 0.5f;

// 3. 以(A.x + offset, center_y)作为下一个顶点
var p2 = new Vector2(st.x + offset, centerY);

// 4. 然后是(B.x - offset, center_y)
var p3 = new Vector2(et.x - offset, centerY);

// 5. 最后是(B.x - offset, B.y)
var p4 = new Vector2(et.x - offset, et.y);

painter.LineTo(p1); // 水平延伸到 (A.x + offset, A.y) painter.LineTo(p2); // 垂直到 (A.x + offset, center_y) painter.LineTo(p3); // 水平到 (B.x - offset, center_y) painter.LineTo(p4); // 垂直到 (B.x - offset, B.y) painter.LineTo(et); // 水平到终点B
}

painter.Stroke();

// 绘制箭头表示方向
DrawDirectionArrow(painter, st, et, offset);
}
private void DrawDirectionArrow(Painter2D painter, Vector2 st, Vector2 et, float offset)
{ Vector2 arrowPos;
Vector2 arrowDir;

// 根据连接类型计算箭头位置和方向
if (st.x < et.x)
{ // 正向连接:箭头在斜线段中点
var p1 = new Vector2(st.x + offset, st.y);
var p2 = new Vector2(et.x - offset, et.y);
arrowPos = (p1 + p2) * 0.5f;
arrowDir = (p2 - p1).normalized;
} else
{
// 反向连接:箭头在水平线段中点
var centerY = (st.y + et.y) * 0.5f;
var p2 = new Vector2(st.x + offset, centerY);
var p3 = new Vector2(et.x - offset, centerY);
arrowPos = (p2 + p3) * 0.5f;
arrowDir = (p3 - p2).normalized;
}
// 绘制箭头
DrawArrow(painter, arrowPos, arrowDir, k_ArrowSize);
}
private void DrawArrow(Painter2D painter, Vector2 position, Vector2 direction, float size)
{ // 计算箭头的三个顶点 - 增大宽度,使箭头更明显
var perpendicular = new Vector2(-direction.y, direction.x);

// 箭头尖端向前延伸更多
var tip = position + direction * size * 0.6f;
// 箭头底部向后延伸,增加宽度
var base1 = position - direction * size * 0.4f + perpendicular * size * k_ArrowWidth;
var base2 = position - direction * size * 0.4f - perpendicular * size * k_ArrowWidth;

// 绘制箭头三角形
painter.BeginPath();
painter.MoveTo(tip);
painter.LineTo(base1);
painter.LineTo(base2);
painter.LineTo(tip);
painter.fillColor = painter.strokeColor;
painter.Fill();
} }}

使用方式就在自定义的继承自Edge的类中重写CreateEdgeControl即可:

1
2
3
4
5
6
7
8
/// <summary>  
/// 重写CreateEdgeControl方法以使用自定义的EdgeControl
/// </summary>
/// <returns>自定义的CustomEdgeControl实例</returns>
protected override EdgeControl CreateEdgeControl()
{
return new CustomEdgeControl();
}

希望大家能活用AI,享受美好生活