【Unity】为GraphView中的Edge添加数据流效果
Unity的Shader Graph和Visual Effect Graph的连图效果,都是通过 GraphView 实现的,但GraphView中默认的Edge并不带有数据流效果,这里提供了一个简单的带数据流效果的Edge实现。
主要实现过程就是在Edge元素下增加了一个Image元素,使Image在Edge的各个顶点之间游走,并根据游走的进度对Image颜色进行插值。
最初尝试实现这个效果的时候,发现EdgeControl类中有材质属性,所以想通过Shader来实现Edge的动态样式,但看了部分源码后发现EdgeControl中使用了大量的Internal接口,无法在自定义EdgeControl中调用,所以放弃了Shader方案。如果有大佬知道怎么使用Shader实现此效果,还请指教一下!
FlowingEdge的源代码:
using UnityEditor;
using UnityEditor.Experimental.GraphView;
using UnityEngine;
using UnityEngine.UIElements;
public class FlowingEdge : Edge
{
public bool enableFlow
{
get => _isEnableFlow;
set
{
if (_isEnableFlow == value)
{
return;
}
_isEnableFlow = value;
if (_isEnableFlow)
{
Add(_flowImg);
}
else
{
Remove(_flowImg);
}
}
}
private bool _isEnableFlow;
public float flowSize
{
get => _flowSize;
set
{
_flowSize = value;
_flowImg.style.width = new Length(_flowSize, LengthUnit.Pixel);
_flowImg.style.height = new Length(_flowSize, LengthUnit.Pixel);
}
}
private float _flowSize = 6f;
public float flowSpeed {
get; set; } = 150f;
private readonly Image _flowImg;
public FlowingEdge()
{
_flowImg = new Image
{
name = "flow-image",
style =
{
width = new Length(flowSize, LengthUnit.Pixel),
height = new Length(flowSize, LengthUnit.Pixel),
borderTopLeftRadius = new Length(flowSize / 2, LengthUnit.Pixel),
borderTopRightRadius = new Length(flowSize / 2, LengthUnit.Pixel),
borderBottomLeftRadius = new Length(flowSize / 2, LengthUnit.Pixel),
borderBottomRightRadius = new Length(flowSize / 2, LengthUnit.Pixel),
}
};
// Add(_flowImg);
edgeControl.RegisterCallback<GeometryChangedEvent>(OnEdgeControlGeometryChanged);
enableFlow = true;
}
public override bool UpdateEdgeControl()
{
if (!base.UpdateEdgeControl())
{
return false;
}
UpdateFlow();
return true;
}
#region Flow
private float _totalEdgeLength;
private float _passedEdgeLength;
private int _flowPhaseIndex;
private double _flowPhaseStartTime;
private double _flowPhaseDuration;
private float _currentPhaseLength;
public void UpdateFlow()
{
if (!enableFlow)
{
return;
}
// Position
var posProgress = (EditorApplication.timeSinceStartup - _flowPhaseStartTime) / _flowPhaseDuration;
var flowStartPoint = edgeControl.controlPoints[_flowPhaseIndex];
var flowEndPoint = edgeControl.controlPoints[_flowPhaseIndex + 1];
var flowPos = Vector2.Lerp(flowStartPoint, flowEndPoint, (float)posProgress);
_flowImg.transform.position = flowPos - Vector2.one * flowSize / 2;
// Color
var colorProgress = (_passedEdgeLength + _currentPhaseLength * posProgress) / _totalEdgeLength;
var startColor = edgeControl.outputColor;
var endColor = edgeControl.inputColor;
var flowColor = Color.Lerp(startColor, endColor, (float)colorProgress);
_flowImg.style.backgroundColor = flowColor;
// Enter next phase
if (posProgress >= 0.99999f)
{
_passedEdgeLength += _currentPhaseLength;
_flowPhaseIndex++;
if (_flowPhaseIndex >= edgeControl.controlPoints.Length - 1)
{
// Restart flow
_flowPhaseIndex = 0;
_passedEdgeLength = 0;
}
_flowPhaseStartTime = EditorApplication.timeSinceStartup;
_currentPhaseLength = Vector2.Distance(edgeControl.controlPoints[_flowPhaseIndex],
edgeControl.controlPoints[_flowPhaseIndex + 1]);
_flowPhaseDuration = _currentPhaseLength / flowSpeed;
}
}
private void OnEdgeControlGeometryChanged(GeometryChangedEvent evt)
{
// Restart flow
_flowPhaseIndex = 0;
_passedEdgeLength = 0;
_flowPhaseStartTime = EditorApplication.timeSinceStartup;
_currentPhaseLength = Vector2.Distance(edgeControl.controlPoints[_flowPhaseIndex],
edgeControl.controlPoints[_flowPhaseIndex + 1]);
_flowPhaseDuration = _currentPhaseLength / flowSpeed;
// Calculate edge path length
_totalEdgeLength = 0;
for (int i = 0; i < edgeControl.controlPoints.Length - 1; i++)
{
var p = edgeControl.controlPoints[i];
var pNext = edgeControl.controlPoints[i + 1];
var phaseLen = Vector2.Distance(p, pNext);
_totalEdgeLength += phaseLen;
}
}
#endregion
}
在可以连接FlowingEdge的Node里,还要重写 InstantiatePort
方法,告知Port使用FlowingEdge:
public override Port InstantiatePort(Orientation orientation, Direction direction, Port.Capacity capacity, Type type)
{
return Port.Create<FlowingEdge>(orientation, direction, capacity, type);
}