update 0.6.1
This commit is contained in:
-33
@@ -1,33 +0,0 @@
|
||||
# Build and Release Folders
|
||||
bin-debug/
|
||||
bin-release/
|
||||
[Oo]bj/
|
||||
[Bb]in/
|
||||
/.vs/
|
||||
.vs/
|
||||
.idea/
|
||||
|
||||
# Other files and folders
|
||||
.settings/
|
||||
|
||||
# Executables
|
||||
*.swf
|
||||
*.air
|
||||
*.ipa
|
||||
*.apk
|
||||
|
||||
# dll
|
||||
!*.dll
|
||||
|
||||
|
||||
# Project files, i.e. `.project`, `.actionScriptProperties` and `.flexProperties`
|
||||
# should NOT be excluded as they contain compiler settings and other important
|
||||
# information for Eclipse / Flash Builder.
|
||||
/Developer/DLLDev/.idea
|
||||
/Developer/DLLDev/CentralizeLog/.vs
|
||||
/Developer/DLLDev/Deconstruction/.vs
|
||||
/Developer/DLLDev/EditorLevel/.vs
|
||||
/Developer/DLLDev/MainSolution/.vs
|
||||
/Developer/DLLDev/ManagerStyle/.vs
|
||||
/Developer/DLLDev/RescissionRework/.vs
|
||||
/Developer/DLLDev/XericLibrary/.vs
|
||||
+16
-4
@@ -2,7 +2,21 @@
|
||||
|
||||
## [Unrealse]
|
||||
|
||||
* 添加运行时蓝图框架
|
||||
|
||||
## [0.6.1] 2026-07-09
|
||||
|
||||
添加:
|
||||
* 添加支持跨平台的剪贴板功能。(webgl除外)
|
||||
* 增加webgl的模板管理面板,可以管理并添加预设的js功能。
|
||||
* 添加MacroPrefs库。
|
||||
* 添加密码对话框。
|
||||
* 嵌入了quickgraph
|
||||
* 添加了更多高级绘制组件
|
||||
* 添加了文本过滤器库
|
||||
|
||||
删除:
|
||||
* 删除了nav导航接口,现在可以通过quickgraph实现导航。
|
||||
|
||||
## [0.6.0] 2026-06-26
|
||||
|
||||
@@ -27,10 +41,8 @@
|
||||
* 添加了unity上预处理命令管理器
|
||||
|
||||
修复:
|
||||
* SingleMonoBase.EditorInstance 重写:移除 Obsolete,父节点+子节点统一 HideAndDontSave,编辑器内不再因 GlobalInstance 报错。
|
||||
* XericLifeCycleCore.GlobalInstance:编辑器模式自动走 EditorInstance(场景世界),不再走 DontDestroyOnLoad。
|
||||
* 修复 PID 控制器导数公式(除以 Δt² 改为 Δt),解决无穷大溢出问题。
|
||||
* 删除 PID 中无效的 NaN 安全检查(只能拦截 NaN,无法拦截 Infinity)。
|
||||
* SingleMonoBase.EditorInstance 重写:修复编辑器报错的问题
|
||||
* 修复 PID 控制器导数公式中无穷大溢出问题。
|
||||
|
||||
优化:
|
||||
* 优化按键宏逻辑,添加了按键趋势统计。
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 43995da0c5deada479d25647709b2854
|
||||
guid: 02e804ccbe95d97408762108b8b929c7
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
@@ -0,0 +1,250 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using XericLibrary.Runtime.UIGraph;
|
||||
|
||||
namespace XericLibraryEditor.XericComponent
|
||||
{
|
||||
[CustomEditor(typeof(UICurveRenderer))]
|
||||
public class UICurveRendererEditor : Editor
|
||||
{
|
||||
private UICurveRenderer script;
|
||||
|
||||
// MaskableGraphic 基础属性
|
||||
private SerializedProperty m_RaycastTarget;
|
||||
private SerializedProperty m_Maskable;
|
||||
private SerializedProperty m_Material;
|
||||
private SerializedProperty m_Color;
|
||||
|
||||
// 曲线属性
|
||||
private SerializedProperty controlPointsProp;
|
||||
private SerializedProperty startColorProp;
|
||||
private SerializedProperty endColorProp;
|
||||
private SerializedProperty tessellationSegmentsProp;
|
||||
private SerializedProperty arrowsProp;
|
||||
private SerializedProperty autoRebuildProp;
|
||||
|
||||
// 折叠状态
|
||||
private bool m_ControlPointsFoldout = true;
|
||||
private bool m_ArrowsFoldout = true;
|
||||
|
||||
protected void OnEnable()
|
||||
{
|
||||
script = (UICurveRenderer)target;
|
||||
FetchProperties();
|
||||
}
|
||||
|
||||
private void FetchProperties()
|
||||
{
|
||||
m_RaycastTarget = serializedObject.FindProperty("m_RaycastTarget");
|
||||
m_Maskable = serializedObject.FindProperty("m_Maskable");
|
||||
m_Material = serializedObject.FindProperty("m_Material");
|
||||
m_Color = serializedObject.FindProperty("m_Color");
|
||||
|
||||
controlPointsProp = serializedObject.FindProperty("controlPoints");
|
||||
startColorProp = serializedObject.FindProperty("startColor");
|
||||
endColorProp = serializedObject.FindProperty("endColor");
|
||||
tessellationSegmentsProp = serializedObject.FindProperty("tessellationSegments");
|
||||
arrowsProp = serializedObject.FindProperty("arrows");
|
||||
autoRebuildProp = serializedObject.FindProperty("autoRebuild");
|
||||
}
|
||||
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
// 确保 SerializedProperty 已初始化
|
||||
if (arrowsProp == null)
|
||||
FetchProperties();
|
||||
|
||||
serializedObject.Update();
|
||||
|
||||
// MaskableGraphic 基础属性
|
||||
EditorGUILayout.PropertyField(m_RaycastTarget);
|
||||
EditorGUILayout.PropertyField(m_Maskable);
|
||||
EditorGUILayout.PropertyField(m_Material, new GUIContent("材质"));
|
||||
EditorGUILayout.PropertyField(m_Color, new GUIContent("顶点颜色"));
|
||||
|
||||
EditorGUILayout.Space();
|
||||
EditorGUILayout.LabelField("颜色", EditorStyles.boldLabel);
|
||||
EditorGUILayout.PropertyField(startColorProp, new GUIContent("起始颜色"));
|
||||
EditorGUILayout.PropertyField(endColorProp, new GUIContent("结束颜色"));
|
||||
|
||||
EditorGUILayout.Space();
|
||||
EditorGUILayout.LabelField("曲线参数", EditorStyles.boldLabel);
|
||||
EditorGUILayout.PropertyField(tessellationSegmentsProp, new GUIContent("细分段数"));
|
||||
|
||||
// 控制点折叠列表
|
||||
EditorGUILayout.Space();
|
||||
m_ControlPointsFoldout = EditorGUILayout.Foldout(m_ControlPointsFoldout,
|
||||
new GUIContent($"控制点 ({GetArrayLength(controlPointsProp)})"), true);
|
||||
if (m_ControlPointsFoldout)
|
||||
{
|
||||
EditorGUI.indentLevel++;
|
||||
DrawControlPointsList();
|
||||
EditorGUI.indentLevel--;
|
||||
}
|
||||
|
||||
// 箭头折叠列表
|
||||
EditorGUILayout.Space();
|
||||
m_ArrowsFoldout = EditorGUILayout.Foldout(m_ArrowsFoldout,
|
||||
new GUIContent($"箭头装饰 ({arrowsProp.arraySize})"), true);
|
||||
if (m_ArrowsFoldout)
|
||||
{
|
||||
EditorGUI.indentLevel++;
|
||||
DrawArrowsList();
|
||||
EditorGUI.indentLevel--;
|
||||
}
|
||||
|
||||
EditorGUILayout.Space();
|
||||
EditorGUILayout.PropertyField(autoRebuildProp, new GUIContent("自动刷新"));
|
||||
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
|
||||
if (GUI.changed)
|
||||
{
|
||||
if (script.autoRebuild && script.isActiveAndEnabled)
|
||||
{
|
||||
script.RebuildCurve();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private int GetArrayLength(SerializedProperty prop)
|
||||
{
|
||||
if (prop == null) return 0;
|
||||
return prop.arraySize;
|
||||
}
|
||||
|
||||
private void DrawControlPointsList()
|
||||
{
|
||||
if (controlPointsProp == null) return;
|
||||
|
||||
int count = controlPointsProp.arraySize;
|
||||
int newCount = EditorGUILayout.IntField("数量", count);
|
||||
if (newCount < 2) newCount = 2;
|
||||
if (newCount != count)
|
||||
{
|
||||
controlPointsProp.arraySize = newCount;
|
||||
}
|
||||
|
||||
for (int i = 0; i < controlPointsProp.arraySize; i++)
|
||||
{
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
|
||||
var cp = controlPointsProp.GetArrayElementAtIndex(i);
|
||||
// Vector3: (x,y)=位置, z=宽度
|
||||
Vector3 val = cp.vector3Value;
|
||||
EditorGUI.BeginChangeCheck();
|
||||
val = EditorGUILayout.Vector3Field($"CP {i}", val);
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
cp.vector3Value = val;
|
||||
|
||||
// 删除按钮(至少保留 2 个控制点)
|
||||
GUI.enabled = controlPointsProp.arraySize > 2;
|
||||
if (GUILayout.Button("×", GUILayout.Width(25)))
|
||||
{
|
||||
RemoveControlPointAt(i);
|
||||
}
|
||||
GUI.enabled = true;
|
||||
|
||||
EditorGUILayout.EndHorizontal();
|
||||
}
|
||||
|
||||
// 添加按钮
|
||||
if (GUILayout.Button("+ 添加控制点"))
|
||||
{
|
||||
int idx = controlPointsProp.arraySize;
|
||||
controlPointsProp.arraySize++;
|
||||
|
||||
if (idx > 0)
|
||||
{
|
||||
var prev = controlPointsProp.GetArrayElementAtIndex(idx - 1);
|
||||
var prev2 = idx > 1 ? controlPointsProp.GetArrayElementAtIndex(idx - 2) : prev;
|
||||
Vector3 prevVal = prev.vector3Value;
|
||||
Vector3 prev2Val = prev2.vector3Value;
|
||||
Vector3 dir = new Vector3(
|
||||
(prevVal.x - prev2Val.x) * 0.5f,
|
||||
(prevVal.y - prev2Val.y) * 0.5f,
|
||||
10f);
|
||||
dir = dir.normalized * 50f;
|
||||
|
||||
var newCp = controlPointsProp.GetArrayElementAtIndex(idx);
|
||||
newCp.vector3Value = new Vector3(
|
||||
prevVal.x + dir.x,
|
||||
prevVal.y + dir.y,
|
||||
prevVal.z);
|
||||
}
|
||||
else
|
||||
{
|
||||
var newCp = controlPointsProp.GetArrayElementAtIndex(0);
|
||||
newCp.vector3Value = Vector3.zero;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void RemoveControlPointAt(int index)
|
||||
{
|
||||
controlPointsProp.DeleteArrayElementAtIndex(index);
|
||||
}
|
||||
|
||||
private void DrawArrowsList()
|
||||
{
|
||||
if (arrowsProp == null) return;
|
||||
|
||||
for (int i = 0; i < arrowsProp.arraySize; i++)
|
||||
{
|
||||
var arrow = arrowsProp.GetArrayElementAtIndex(i);
|
||||
|
||||
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
|
||||
|
||||
var shapeProp = arrow.FindPropertyRelative("shape");
|
||||
var reversedProp = arrow.FindPropertyRelative("reversed");
|
||||
var progressProp = arrow.FindPropertyRelative("progress");
|
||||
var widthProp = arrow.FindPropertyRelative("width");
|
||||
var heightProp = arrow.FindPropertyRelative("height");
|
||||
var depthProp = arrow.FindPropertyRelative("depthCompensation");
|
||||
var colorProp = arrow.FindPropertyRelative("color");
|
||||
|
||||
EditorGUILayout.PropertyField(shapeProp, new GUIContent("形状"));
|
||||
EditorGUILayout.PropertyField(reversedProp, new GUIContent("反向"));
|
||||
EditorGUILayout.Slider(progressProp, 0f, 1f, new GUIContent("进度"));
|
||||
EditorGUILayout.PropertyField(widthProp, new GUIContent("宽度"));
|
||||
EditorGUILayout.PropertyField(heightProp, new GUIContent("高度"));
|
||||
EditorGUILayout.Slider(depthProp, -1f, 1f, new GUIContent("深度补偿"));
|
||||
EditorGUILayout.PropertyField(colorProp, new GUIContent("颜色"));
|
||||
|
||||
if (GUILayout.Button("× 删除此箭头"))
|
||||
{
|
||||
arrowsProp.DeleteArrayElementAtIndex(i);
|
||||
break;
|
||||
}
|
||||
|
||||
EditorGUILayout.EndVertical();
|
||||
EditorGUILayout.Space(2);
|
||||
}
|
||||
|
||||
if (GUILayout.Button("+ 添加箭头"))
|
||||
{
|
||||
int idx = arrowsProp.arraySize;
|
||||
arrowsProp.arraySize++;
|
||||
|
||||
var newArrow = arrowsProp.GetArrayElementAtIndex(idx);
|
||||
var shapeProp = newArrow.FindPropertyRelative("shape");
|
||||
var reversedProp = newArrow.FindPropertyRelative("reversed");
|
||||
var progressProp = newArrow.FindPropertyRelative("progress");
|
||||
var widthProp = newArrow.FindPropertyRelative("width");
|
||||
var heightProp = newArrow.FindPropertyRelative("height");
|
||||
var depthProp = newArrow.FindPropertyRelative("depthCompensation");
|
||||
var colorProp = newArrow.FindPropertyRelative("color");
|
||||
|
||||
shapeProp.enumValueIndex = 0;
|
||||
reversedProp.boolValue = false;
|
||||
progressProp.floatValue = 0.5f;
|
||||
widthProp.floatValue = 20f;
|
||||
heightProp.floatValue = 20f;
|
||||
depthProp.floatValue = 0f;
|
||||
colorProp.colorValue = Color.white;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: de176d833dcb3f84b9a7da6addacc5da
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,51 @@
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using XericLibrary.Runtime.UIGraph;
|
||||
|
||||
namespace XericLibraryEditor.XericComponent
|
||||
{
|
||||
[CustomEditor(typeof(UILineRendererV2))]
|
||||
public class UILineRendererV2Editor : Editor
|
||||
{
|
||||
private UILineRendererV2 script;
|
||||
|
||||
private SerializedProperty m_RaycastTarget;
|
||||
private SerializedProperty m_Maskable;
|
||||
private SerializedProperty m_Material;
|
||||
private SerializedProperty m_Color;
|
||||
private SerializedProperty defaultThicknessProp;
|
||||
private SerializedProperty defaultColorProp;
|
||||
private SerializedProperty defaultUVModeProp;
|
||||
|
||||
protected virtual void OnEnable()
|
||||
{
|
||||
script = (UILineRendererV2)target;
|
||||
|
||||
m_RaycastTarget = serializedObject.FindProperty("m_RaycastTarget");
|
||||
m_Maskable = serializedObject.FindProperty("m_Maskable");
|
||||
m_Material = serializedObject.FindProperty("m_Material");
|
||||
m_Color = serializedObject.FindProperty("m_Color");
|
||||
defaultThicknessProp = serializedObject.FindProperty("defaultThickness");
|
||||
defaultColorProp = serializedObject.FindProperty("defaultColor");
|
||||
defaultUVModeProp = serializedObject.FindProperty("defaultUVMode");
|
||||
}
|
||||
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
serializedObject.Update();
|
||||
|
||||
// MaskableGraphic 基础属性
|
||||
EditorGUILayout.PropertyField(m_RaycastTarget);
|
||||
EditorGUILayout.PropertyField(m_Maskable);
|
||||
EditorGUILayout.PropertyField(m_Material, new GUIContent("材质"));
|
||||
|
||||
EditorGUILayout.Space();
|
||||
EditorGUILayout.LabelField("全局默认设置", EditorStyles.boldLabel);
|
||||
EditorGUILayout.PropertyField(defaultThicknessProp);
|
||||
EditorGUILayout.PropertyField(defaultColorProp);
|
||||
EditorGUILayout.PropertyField(defaultUVModeProp);
|
||||
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: baeb976551b20704e9120f9ca2b5ba3a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,109 @@
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using XericLibrary.Runtime.UIGraph;
|
||||
|
||||
namespace XericLibraryEditor.XericComponent
|
||||
{
|
||||
[CustomEditor(typeof(UIPrimitiveRenderer))]
|
||||
public class UIPrimitiveRendererEditor : Editor
|
||||
{
|
||||
private UIPrimitiveRenderer script;
|
||||
|
||||
private SerializedProperty m_RaycastTarget;
|
||||
private SerializedProperty m_Maskable;
|
||||
private SerializedProperty m_Material;
|
||||
private SerializedProperty m_Color;
|
||||
private SerializedProperty primitiveTypeProp;
|
||||
private SerializedProperty sizeModeProp;
|
||||
private SerializedProperty sizeProp;
|
||||
private SerializedProperty centerOffsetProp;
|
||||
private SerializedProperty sideCountProp;
|
||||
private SerializedProperty chamferSizeProp;
|
||||
private SerializedProperty chamferSegmentsProp;
|
||||
private SerializedProperty bgColorProp;
|
||||
private SerializedProperty centerColorProp;
|
||||
private SerializedProperty borderColorProp;
|
||||
private SerializedProperty borderThicknessProp;
|
||||
private SerializedProperty autoRebuildProp;
|
||||
|
||||
protected virtual void OnEnable()
|
||||
{
|
||||
script = (UIPrimitiveRenderer)target;
|
||||
|
||||
m_RaycastTarget = serializedObject.FindProperty("m_RaycastTarget");
|
||||
m_Maskable = serializedObject.FindProperty("m_Maskable");
|
||||
m_Material = serializedObject.FindProperty("m_Material");
|
||||
m_Color = serializedObject.FindProperty("m_Color");
|
||||
primitiveTypeProp = serializedObject.FindProperty("primitiveType");
|
||||
sizeModeProp = serializedObject.FindProperty("sizeMode");
|
||||
sizeProp = serializedObject.FindProperty("size");
|
||||
centerOffsetProp = serializedObject.FindProperty("centerOffset");
|
||||
sideCountProp = serializedObject.FindProperty("sideCount");
|
||||
chamferSizeProp = serializedObject.FindProperty("chamferSize");
|
||||
chamferSegmentsProp = serializedObject.FindProperty("chamferSegments");
|
||||
bgColorProp = serializedObject.FindProperty("bgColor");
|
||||
centerColorProp = serializedObject.FindProperty("centerColor");
|
||||
borderColorProp = serializedObject.FindProperty("borderColor");
|
||||
borderThicknessProp = serializedObject.FindProperty("borderThickness");
|
||||
autoRebuildProp = serializedObject.FindProperty("autoRebuild");
|
||||
}
|
||||
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
serializedObject.Update();
|
||||
|
||||
// MaskableGraphic 基础属性
|
||||
EditorGUILayout.PropertyField(m_RaycastTarget);
|
||||
EditorGUILayout.PropertyField(m_Maskable);
|
||||
EditorGUILayout.PropertyField(m_Material, new GUIContent("材质"));
|
||||
EditorGUILayout.PropertyField(m_Color, new GUIContent("顶点颜色"));
|
||||
|
||||
EditorGUILayout.Space();
|
||||
EditorGUILayout.LabelField("图元类型", EditorStyles.boldLabel);
|
||||
EditorGUILayout.PropertyField(primitiveTypeProp);
|
||||
EditorGUILayout.PropertyField(sizeModeProp);
|
||||
|
||||
bool isPolygon = script.primitiveType == XericLibrary.Runtime.UIGraph.PrimitiveType.Polygon;
|
||||
|
||||
EditorGUILayout.Space();
|
||||
EditorGUILayout.LabelField("尺寸", EditorStyles.boldLabel);
|
||||
EditorGUILayout.PropertyField(sizeProp, new GUIContent("尺寸 (XY)"));
|
||||
EditorGUILayout.PropertyField(centerOffsetProp, new GUIContent("中心偏移"));
|
||||
|
||||
if (isPolygon)
|
||||
{
|
||||
EditorGUILayout.Space();
|
||||
EditorGUILayout.LabelField("多边形设置", EditorStyles.boldLabel);
|
||||
EditorGUILayout.PropertyField(sideCountProp, new GUIContent("边数"));
|
||||
}
|
||||
|
||||
EditorGUILayout.Space();
|
||||
EditorGUILayout.LabelField("倒角", EditorStyles.boldLabel);
|
||||
EditorGUILayout.Slider(chamferSizeProp, 0f, 0.5f, new GUIContent("倒角尺寸"));
|
||||
EditorGUILayout.IntSlider(chamferSegmentsProp, 1, 16, new GUIContent("细分段数"));
|
||||
|
||||
EditorGUILayout.Space();
|
||||
EditorGUILayout.LabelField("颜色", EditorStyles.boldLabel);
|
||||
EditorGUILayout.PropertyField(bgColorProp, new GUIContent("背景颜色"));
|
||||
EditorGUILayout.PropertyField(centerColorProp, new GUIContent("中心颜色"));
|
||||
EditorGUILayout.PropertyField(borderColorProp, new GUIContent("边框颜色"));
|
||||
|
||||
EditorGUILayout.Space();
|
||||
EditorGUILayout.LabelField("边框", EditorStyles.boldLabel);
|
||||
EditorGUILayout.PropertyField(borderThicknessProp, new GUIContent("边框厚度"));
|
||||
|
||||
EditorGUILayout.Space();
|
||||
EditorGUILayout.PropertyField(autoRebuildProp, new GUIContent("自动刷新"));
|
||||
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
|
||||
if (GUI.changed)
|
||||
{
|
||||
if (script.autoRebuild && script.isActiveAndEnabled)
|
||||
{
|
||||
script.RebuildPrimitive();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 24d520025eb64a84b91f7eb469d97e2b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Binary file not shown.
@@ -1,5 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7758a0679c9038144a4b0d007d44618f
|
||||
guid: 2a72833fcf054be4e83325b8149bfe8d
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
@@ -0,0 +1,43 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!21 &2100000
|
||||
Material:
|
||||
serializedVersion: 8
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_Name: XUIDotBasic
|
||||
m_Shader: {fileID: 4800000, guid: 858173d3f3725a1468cac9f50da734a4, type: 3}
|
||||
m_ValidKeywords: []
|
||||
m_InvalidKeywords: []
|
||||
m_LightmapFlags: 4
|
||||
m_EnableInstancingVariants: 0
|
||||
m_DoubleSidedGI: 0
|
||||
m_CustomRenderQueue: -1
|
||||
stringTagMap: {}
|
||||
disabledShaderPasses: []
|
||||
m_SavedProperties:
|
||||
serializedVersion: 3
|
||||
m_TexEnvs:
|
||||
- _MainTex:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
m_Ints: []
|
||||
m_Floats:
|
||||
- _ColorMask: 15
|
||||
- _Hardness: 0.001
|
||||
- _Keyword0: 0
|
||||
- _Radius: 0.28
|
||||
- _RectSize: 1
|
||||
- _Stencil: 0
|
||||
- _StencilComp: 8
|
||||
- _StencilOp: 0
|
||||
- _StencilReadMask: 255
|
||||
- _StencilWriteMask: 255
|
||||
- _UseUIAlphaClip: 0
|
||||
m_Colors:
|
||||
- _BackgroundColor: {r: 1, g: 1, b: 1, a: 0}
|
||||
- _Color: {r: 1, g: 1, b: 1, a: 1}
|
||||
m_BuildTextureStacks: []
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 27854d851ac720b4db774811988c6b95
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 2100000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,169 @@
|
||||
|
||||
Shader "XUIDotBasic"
|
||||
{
|
||||
Properties
|
||||
{
|
||||
[PerRendererData] _MainTex ("Sprite Texture", 2D) = "white" {}
|
||||
_Color ("Tint", Color) = (1,1,1,1)
|
||||
|
||||
_StencilComp ("Stencil Comparison", Float) = 8
|
||||
_Stencil ("Stencil ID", Float) = 0
|
||||
_StencilOp ("Stencil Operation", Float) = 0
|
||||
_StencilWriteMask ("Stencil Write Mask", Float) = 255
|
||||
_StencilReadMask ("Stencil Read Mask", Float) = 255
|
||||
|
||||
_ColorMask ("Color Mask", Float) = 15
|
||||
|
||||
[Toggle(UNITY_UI_ALPHACLIP)] _UseUIAlphaClip ("Use Alpha Clip", Float) = 0
|
||||
|
||||
_BackgroundColor( "BackgroundColor", Color ) = ( 1, 1, 1, 0 )
|
||||
_Radius( "Radius", Range( 0, 1 ) ) = 0.5
|
||||
_RectSize( "RectSize", Range( 0, 1 ) ) = 1
|
||||
|
||||
}
|
||||
|
||||
SubShader
|
||||
{
|
||||
LOD 0
|
||||
|
||||
Tags { "Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" "PreviewType"="Plane" "CanUseSpriteAtlas"="True" }
|
||||
|
||||
Stencil
|
||||
{
|
||||
Ref [_Stencil]
|
||||
ReadMask [_StencilReadMask]
|
||||
WriteMask [_StencilWriteMask]
|
||||
Comp [_StencilComp]
|
||||
Pass [_StencilOp]
|
||||
}
|
||||
|
||||
|
||||
Cull Off
|
||||
Lighting Off
|
||||
ZWrite Off
|
||||
ZTest [unity_GUIZTestMode]
|
||||
Blend One OneMinusSrcAlpha
|
||||
ColorMask [_ColorMask]
|
||||
|
||||
|
||||
Pass
|
||||
{
|
||||
Name "Default"
|
||||
CGPROGRAM
|
||||
#define ASE_VERSION 19908
|
||||
|
||||
#pragma vertex vert
|
||||
#pragma fragment frag
|
||||
#pragma target 3.5
|
||||
|
||||
#include "UnityCG.cginc"
|
||||
#include "UnityUI.cginc"
|
||||
|
||||
#pragma multi_compile_local _ UNITY_UI_CLIP_RECT
|
||||
#pragma multi_compile_local _ UNITY_UI_ALPHACLIP
|
||||
|
||||
#define ASE_NEEDS_FRAG_COLOR
|
||||
#define ASE_NEEDS_TEXTURE_COORDINATES0
|
||||
#define ASE_NEEDS_FRAG_TEXTURE_COORDINATES0
|
||||
|
||||
|
||||
struct appdata_t
|
||||
{
|
||||
float4 vertex : POSITION;
|
||||
float4 color : COLOR;
|
||||
float2 texcoord : TEXCOORD0;
|
||||
UNITY_VERTEX_INPUT_INSTANCE_ID
|
||||
|
||||
};
|
||||
|
||||
struct v2f
|
||||
{
|
||||
float4 vertex : SV_POSITION;
|
||||
fixed4 color : COLOR;
|
||||
float2 texcoord : TEXCOORD0;
|
||||
float4 worldPosition : TEXCOORD1;
|
||||
float4 mask : TEXCOORD2;
|
||||
UNITY_VERTEX_OUTPUT_STEREO
|
||||
|
||||
};
|
||||
|
||||
sampler2D _MainTex;
|
||||
fixed4 _Color;
|
||||
fixed4 _TextureSampleAdd;
|
||||
float4 _ClipRect;
|
||||
float4 _MainTex_ST;
|
||||
float _UIMaskSoftnessX;
|
||||
float _UIMaskSoftnessY;
|
||||
|
||||
uniform float4 _BackgroundColor;
|
||||
uniform float _RectSize;
|
||||
uniform float _Radius;
|
||||
|
||||
|
||||
v2f vert(appdata_t v )
|
||||
{
|
||||
v2f OUT;
|
||||
UNITY_SETUP_INSTANCE_ID(v);
|
||||
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(OUT);
|
||||
|
||||
|
||||
|
||||
v.vertex.xyz += float3( 0, 0, 0 ) ;
|
||||
|
||||
float4 vPosition = UnityObjectToClipPos(v.vertex);
|
||||
OUT.worldPosition = v.vertex;
|
||||
OUT.vertex = vPosition;
|
||||
|
||||
float2 pixelSize = vPosition.w;
|
||||
pixelSize /= float2(1, 1) * abs(mul((float2x2)UNITY_MATRIX_P, _ScreenParams.xy));
|
||||
|
||||
float4 clampedRect = clamp(_ClipRect, -2e10, 2e10);
|
||||
float2 maskUV = (v.vertex.xy - clampedRect.xy) / (clampedRect.zw - clampedRect.xy);
|
||||
OUT.texcoord = v.texcoord;
|
||||
OUT.mask = float4(v.vertex.xy * 2 - clampedRect.xy - clampedRect.zw, 0.25 / (0.25 * half2(_UIMaskSoftnessX, _UIMaskSoftnessY) + abs(pixelSize.xy)));
|
||||
|
||||
OUT.color = v.color * _Color;
|
||||
return OUT;
|
||||
}
|
||||
|
||||
fixed4 frag(v2f IN ) : SV_Target
|
||||
{
|
||||
//Round up the alpha color coming from the interpolator (to 1.0/256.0 steps)
|
||||
//The incoming alpha could have numerical instability, which makes it very sensible to
|
||||
//HDR color transparency blend, when it blends with the world's texture.
|
||||
const half alphaPrecision = half(0xff);
|
||||
const half invAlphaPrecision = half(1.0/alphaPrecision);
|
||||
IN.color.a = round(IN.color.a * alphaPrecision)*invAlphaPrecision;
|
||||
|
||||
float2 texCoord3 = IN.texcoord.xy * float2( 1,1 ) + float2( 0,0 );
|
||||
float temp_output_2_0_g2 = _RectSize;
|
||||
float temp_output_3_0_g2 = _RectSize;
|
||||
float2 appendResult21_g2 = (float2(temp_output_2_0_g2 , temp_output_3_0_g2));
|
||||
float Radius25_g2 = max( min( min( abs( ( _Radius * 2 ) ), abs( temp_output_2_0_g2 ) ), abs( temp_output_3_0_g2 ) ), 1E-05 );
|
||||
float temp_output_30_0_g2 = ( length( max( ( ( abs( (texCoord3*2.0 + -1.0) ) - appendResult21_g2 ) + Radius25_g2 ), 0.0 ) ) / Radius25_g2 );
|
||||
float temp_output_23_0 = saturate( ( ( 1.0 - temp_output_30_0_g2 ) / fwidth( temp_output_30_0_g2 ) ) );
|
||||
float4 lerpResult7 = lerp( _BackgroundColor , IN.color , temp_output_23_0);
|
||||
|
||||
|
||||
half4 color = lerpResult7;
|
||||
|
||||
#ifdef UNITY_UI_CLIP_RECT
|
||||
half2 m = saturate((_ClipRect.zw - _ClipRect.xy - abs(IN.mask.xy)) * IN.mask.zw);
|
||||
color.a *= m.x * m.y;
|
||||
#endif
|
||||
|
||||
#ifdef UNITY_UI_ALPHACLIP
|
||||
clip (color.a - 0.001);
|
||||
#endif
|
||||
|
||||
color.rgb *= color.a;
|
||||
|
||||
return color;
|
||||
}
|
||||
ENDCG
|
||||
}
|
||||
}
|
||||
CustomEditor "AmplifyShaderEditor.MaterialInspector"
|
||||
|
||||
Fallback Off
|
||||
}
|
||||
+2
-70
@@ -1,5 +1,4 @@
|
||||
// Made with Amplify Shader Editor v1.9.9.1
|
||||
// Available at the Unity Asset Store - http://u3d.as/y3X
|
||||
|
||||
Shader "XUIBasic"
|
||||
{
|
||||
Properties
|
||||
@@ -222,71 +221,4 @@ Shader "XUIBasic"
|
||||
CustomEditor "AmplifyShaderEditor.MaterialInspector"
|
||||
|
||||
Fallback Off
|
||||
}
|
||||
/*ASEBEGIN
|
||||
Version=19901
|
||||
Node;AmplifyShaderEditor.RangedFloatNode, AmplifyShaderEditor, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null;5;-1408,256;Inherit;False;Property;_CoordRotateAngle;CoordRotateAngle;2;0;Create;True;0;0;0;False;0;False;0;0;0;0;0;1;FLOAT;0
|
||||
Node;AmplifyShaderEditor.Vector4Node, AmplifyShaderEditor, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null;28;-1920,0;Inherit;False;Property;_CoordTransform;CoordTransform;0;0;Create;True;0;0;0;False;0;False;1,1,0,0;1,1,0,0;0;5;FLOAT4;0;FLOAT;1;FLOAT;2;FLOAT;3;FLOAT;4
|
||||
Node;AmplifyShaderEditor.SimpleMultiplyOpNode, AmplifyShaderEditor, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null;9;-1184,256;Inherit;False;2;2;0;FLOAT;0;False;1;FLOAT;2;False;1;FLOAT;0
|
||||
Node;AmplifyShaderEditor.ComponentMaskNode, AmplifyShaderEditor, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null;30;-1664,96;Inherit;False;False;False;True;True;1;0;FLOAT4;0,0,0,0;False;1;FLOAT2;0
|
||||
Node;AmplifyShaderEditor.ComponentMaskNode, AmplifyShaderEditor, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null;29;-1664,16;Inherit;False;True;True;False;False;1;0;FLOAT4;0,0,0,0;False;1;FLOAT2;0
|
||||
Node;AmplifyShaderEditor.TextureCoordinatesNode, AmplifyShaderEditor, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null;1;-1408,0;Inherit;False;0;-1;2;3;2;SAMPLER2D;;False;0;FLOAT2;1,1;False;1;FLOAT2;0,0;False;5;FLOAT2;0;FLOAT;1;FLOAT;2;FLOAT;3;FLOAT;4
|
||||
Node;AmplifyShaderEditor.PiNode, AmplifyShaderEditor, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null;8;-1024,256;Inherit;False;1;0;FLOAT;1;False;1;FLOAT;0
|
||||
Node;AmplifyShaderEditor.Vector2Node, AmplifyShaderEditor, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null;4;-1408,128;Inherit;False;Property;_CoordCenterv2;CoordCenter(v2);1;0;Create;True;0;0;0;False;0;False;0.5,0.5;0.5,0.5;0;3;FLOAT2;0;FLOAT;1;FLOAT;2
|
||||
Node;AmplifyShaderEditor.Vector2Node, AmplifyShaderEditor, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null;34;-768,-128;Inherit;False;Constant;_Vector0;Vector 0;11;0;Create;True;0;0;0;False;0;False;1,1;0,0;0;3;FLOAT2;0;FLOAT;1;FLOAT;2
|
||||
Node;AmplifyShaderEditor.RotatorNode, AmplifyShaderEditor, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null;2;-896,0;Inherit;False;3;0;FLOAT2;0,0;False;1;FLOAT2;0,0;False;2;FLOAT;1;False;1;FLOAT2;0
|
||||
Node;AmplifyShaderEditor.FunctionNode, AmplifyShaderEditor, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null;32;-512,0;Inherit;False;PMod;-1;;3;f21979a9080cb044ca372458f5a37d88;1,33,0;2;2;FLOAT2;0,0;False;3;FLOAT2;1,1;False;1;FLOAT2;0
|
||||
Node;AmplifyShaderEditor.AbsOpNode, AmplifyShaderEditor, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null;36;-480,128;Inherit;False;1;0;FLOAT2;0,0;False;1;FLOAT2;0
|
||||
Node;AmplifyShaderEditor.StaticSwitch, AmplifyShaderEditor, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null;26;-256,-128;Inherit;False;Property;_CoordRange;CoordRange;10;0;Create;True;0;0;0;False;0;False;0;0;0;True;;KeywordEnum;3;Normal;Mode;ABS;Create;True;True;All;9;1;FLOAT2;0,0;False;0;FLOAT2;0,0;False;2;FLOAT2;0,0;False;3;FLOAT2;0,0;False;4;FLOAT2;0,0;False;5;FLOAT2;0,0;False;6;FLOAT2;0,0;False;7;FLOAT2;0,0;False;8;FLOAT2;0,0;False;1;FLOAT2;0
|
||||
Node;AmplifyShaderEditor.ComponentMaskNode, AmplifyShaderEditor, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null;18;0,-128;Inherit;False;True;False;True;True;1;0;FLOAT2;0,0;False;1;FLOAT;0
|
||||
Node;AmplifyShaderEditor.RangedFloatNode, AmplifyShaderEditor, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null;23;0,96;Inherit;False;Property;_ExpNumber;ExpNumber;5;0;Create;True;0;0;0;False;0;False;1;0;0;0;0;1;FLOAT;0
|
||||
Node;AmplifyShaderEditor.SinOpNode, AmplifyShaderEditor, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null;19;256,-64;Inherit;False;1;0;FLOAT;0;False;1;FLOAT;0
|
||||
Node;AmplifyShaderEditor.CosOpNode, AmplifyShaderEditor, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null;20;256,0;Inherit;False;1;0;FLOAT;0;False;1;FLOAT;0
|
||||
Node;AmplifyShaderEditor.PowerNode, AmplifyShaderEditor, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null;22;256,64;Inherit;False;False;2;0;FLOAT;0;False;1;FLOAT;1;False;1;FLOAT;0
|
||||
Node;AmplifyShaderEditor.Vector2Node, AmplifyShaderEditor, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null;25;0,384;Inherit;False;Property;_PolygonSizev2;PolygonSize(v2);9;0;Create;True;0;0;0;False;0;False;0.5,0.5;0,0;0;3;FLOAT2;0;FLOAT;1;FLOAT;2
|
||||
Node;AmplifyShaderEditor.RangedFloatNode, AmplifyShaderEditor, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null;24;0,288;Inherit;False;Property;_PolygonSides;PolygonSides;8;0;Create;True;0;0;0;False;0;False;5;0;0;0;0;1;FLOAT;0
|
||||
Node;AmplifyShaderEditor.FunctionNode, AmplifyShaderEditor, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null;10;256,256;Inherit;False;Polygon;-1;;4;6906ef7087298c94c853d6753e182169;0;4;1;FLOAT2;0,0;False;2;FLOAT;5;False;3;FLOAT;0.5;False;4;FLOAT;0.5;False;1;FLOAT;0
|
||||
Node;AmplifyShaderEditor.StaticSwitch, AmplifyShaderEditor, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null;13;512,-128;Inherit;False;Property;_GradientType;GradientType;4;0;Create;True;0;0;0;False;0;False;0;0;0;True;;KeywordEnum;4;Linear;Sin;Cos;Exponential;Create;True;True;All;9;1;FLOAT;0;False;0;FLOAT;0;False;2;FLOAT;0;False;3;FLOAT;0;False;4;FLOAT;0;False;5;FLOAT;0;False;6;FLOAT;0;False;7;FLOAT;0;False;8;FLOAT;0;False;1;FLOAT;0
|
||||
Node;AmplifyShaderEditor.StaticSwitch, AmplifyShaderEditor, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null;11;896,-128;Inherit;False;Property;_ElementType;Element Type;3;0;Create;True;0;0;0;False;0;False;0;0;0;True;;KeywordEnum;2;Gradient;Polygon;Create;True;True;All;9;1;FLOAT;0;False;0;FLOAT;0;False;2;FLOAT;0;False;3;FLOAT;0;False;4;FLOAT;0;False;5;FLOAT;0;False;6;FLOAT;0;False;7;FLOAT;0;False;8;FLOAT;0;False;1;FLOAT;0
|
||||
Node;AmplifyShaderEditor.ColorNode, AmplifyShaderEditor, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null;15;896,-512;Inherit;False;Property;_ColorA;Color A;6;0;Create;True;0;0;0;False;0;False;0,1,0.07612991,1;0,1,0.07612991,1;True;True;0;6;COLOR;0;FLOAT;1;FLOAT;2;FLOAT;3;FLOAT;4;FLOAT3;5
|
||||
Node;AmplifyShaderEditor.ColorNode, AmplifyShaderEditor, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null;16;896,-320;Inherit;False;Property;_ColorB;Color B;7;0;Create;True;0;0;0;False;0;False;1,0,0,1;1,0,0,1;True;True;0;6;COLOR;0;FLOAT;1;FLOAT;2;FLOAT;3;FLOAT;4;FLOAT3;5
|
||||
Node;AmplifyShaderEditor.LerpOp, AmplifyShaderEditor, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null;14;1152,-384;Inherit;False;3;0;COLOR;0,0,0,0;False;1;COLOR;0,0,0,0;False;2;FLOAT;0;False;1;COLOR;0
|
||||
Node;AmplifyShaderEditor.DynamicAppendNode, AmplifyShaderEditor, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null;7;1312,-384;Inherit;False;FLOAT4;4;0;FLOAT4;0,0,0,0;False;1;FLOAT;0;False;2;FLOAT;0;False;3;FLOAT;1;False;1;FLOAT4;0
|
||||
Node;AmplifyShaderEditor.TemplateMultiPassMasterNode, AmplifyShaderEditor, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null;0;1472,-384;Float;False;True;-1;3;AmplifyShaderEditor.MaterialInspector;0;3;XUIBasic;5056123faa0c79b47ab6ad7e8bf059a4;True;Default;0;0;Default;2;False;True;3;1;False;;10;False;;0;1;False;;0;False;;False;False;False;False;False;False;False;False;False;False;False;False;True;2;False;;False;True;True;True;True;True;0;True;_ColorMask;False;False;False;False;False;False;False;True;True;0;True;_Stencil;255;True;_StencilReadMask;255;True;_StencilWriteMask;0;True;_StencilComp;0;True;_StencilOp;0;False;;0;False;;0;False;;0;False;;0;False;;0;False;;False;True;2;False;;True;0;True;unity_GUIZTestMode;False;True;5;Queue=Transparent=Queue=0;IgnoreProjector=True;RenderType=Transparent=RenderType;PreviewType=Plane;CanUseSpriteAtlas=True;False;False;0;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;True;3;False;0;;0;0;Standard;0;0;1;True;False;;False;0
|
||||
WireConnection;9;0;5;0
|
||||
WireConnection;30;0;28;0
|
||||
WireConnection;29;0;28;0
|
||||
WireConnection;1;0;29;0
|
||||
WireConnection;1;1;30;0
|
||||
WireConnection;8;0;9;0
|
||||
WireConnection;2;0;1;0
|
||||
WireConnection;2;1;4;0
|
||||
WireConnection;2;2;8;0
|
||||
WireConnection;32;2;2;0
|
||||
WireConnection;32;3;34;0
|
||||
WireConnection;36;0;2;0
|
||||
WireConnection;26;1;2;0
|
||||
WireConnection;26;0;32;0
|
||||
WireConnection;26;2;36;0
|
||||
WireConnection;18;0;26;0
|
||||
WireConnection;19;0;18;0
|
||||
WireConnection;20;0;18;0
|
||||
WireConnection;22;0;18;0
|
||||
WireConnection;22;1;23;0
|
||||
WireConnection;10;1;26;0
|
||||
WireConnection;10;2;24;0
|
||||
WireConnection;10;3;25;1
|
||||
WireConnection;10;4;25;2
|
||||
WireConnection;13;1;18;0
|
||||
WireConnection;13;0;19;0
|
||||
WireConnection;13;2;20;0
|
||||
WireConnection;13;3;22;0
|
||||
WireConnection;11;1;13;0
|
||||
WireConnection;11;0;10;0
|
||||
WireConnection;14;0;15;0
|
||||
WireConnection;14;1;16;0
|
||||
WireConnection;14;2;11;0
|
||||
WireConnection;7;0;14;0
|
||||
WireConnection;0;0;7;0
|
||||
ASEEND*/
|
||||
//CHKSM=EF1BCF878CBE9DBF87503B5564BA985D4602AE12
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b66080c7d48f7f342b79af0fdbedec87
|
||||
guid: df25563ae0441ef41b5b4c5b02eb44e2
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
@@ -0,0 +1,47 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!21 &2100000
|
||||
Material:
|
||||
serializedVersion: 8
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_Name: UICurve
|
||||
m_Shader: {fileID: 4800000, guid: ed13c09bee4e83848ad102719497b140, type: 3}
|
||||
m_Parent: {fileID: 0}
|
||||
m_ModifiedSerializedProperties: 0
|
||||
m_ValidKeywords: []
|
||||
m_InvalidKeywords:
|
||||
- _COLORMODE_SPHERE
|
||||
m_LightmapFlags: 4
|
||||
m_EnableInstancingVariants: 0
|
||||
m_DoubleSidedGI: 0
|
||||
m_CustomRenderQueue: -1
|
||||
stringTagMap: {}
|
||||
disabledShaderPasses: []
|
||||
m_LockedProperties:
|
||||
m_SavedProperties:
|
||||
serializedVersion: 3
|
||||
m_TexEnvs:
|
||||
- _MainTex:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
m_Ints: []
|
||||
m_Floats:
|
||||
- _ColorMask: 15
|
||||
- _Colormode: 1
|
||||
- _EdgeExp: 0.001
|
||||
- _EdgeStep: 0
|
||||
- _EdgeStepRange: 0.1
|
||||
- _Float0: 0.9
|
||||
- _Stencil: 0
|
||||
- _StencilComp: 8
|
||||
- _StencilOp: 0
|
||||
- _StencilReadMask: 255
|
||||
- _StencilWriteMask: 255
|
||||
- _UseUIAlphaClip: 0
|
||||
m_Colors:
|
||||
- _Color: {r: 1, g: 1, b: 1, a: 1}
|
||||
m_BuildTextureStacks: []
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6dd17cf52efd94849995132ec6a6854c
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 2100000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,41 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!21 &2100000
|
||||
Material:
|
||||
serializedVersion: 8
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_Name: UILineRender
|
||||
m_Shader: {fileID: 4800000, guid: 753025f901250db45b87f87c632c0bb0, type: 3}
|
||||
m_Parent: {fileID: 0}
|
||||
m_ModifiedSerializedProperties: 0
|
||||
m_ValidKeywords: []
|
||||
m_InvalidKeywords: []
|
||||
m_LightmapFlags: 4
|
||||
m_EnableInstancingVariants: 0
|
||||
m_DoubleSidedGI: 0
|
||||
m_CustomRenderQueue: -1
|
||||
stringTagMap: {}
|
||||
disabledShaderPasses: []
|
||||
m_LockedProperties:
|
||||
m_SavedProperties:
|
||||
serializedVersion: 3
|
||||
m_TexEnvs:
|
||||
- _MainTex:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
m_Ints: []
|
||||
m_Floats:
|
||||
- _ColorMask: 15
|
||||
- _Stencil: 0
|
||||
- _StencilComp: 8
|
||||
- _StencilOp: 0
|
||||
- _StencilReadMask: 255
|
||||
- _StencilWriteMask: 255
|
||||
- _UseUIAlphaClip: 0
|
||||
m_Colors:
|
||||
- _Color: {r: 1, g: 1, b: 1, a: 1}
|
||||
m_BuildTextureStacks: []
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7fd93918d0371ef419caaf4d889b9e8f
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 2100000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,55 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!21 &2100000
|
||||
Material:
|
||||
serializedVersion: 8
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_Name: UIPattern
|
||||
m_Shader: {fileID: 4800000, guid: f4e5237cd89192c4f8361db3dec5cc3b, type: 3}
|
||||
m_Parent: {fileID: 0}
|
||||
m_ModifiedSerializedProperties: 0
|
||||
m_ValidKeywords: []
|
||||
m_InvalidKeywords: []
|
||||
m_LightmapFlags: 4
|
||||
m_EnableInstancingVariants: 0
|
||||
m_DoubleSidedGI: 0
|
||||
m_CustomRenderQueue: -1
|
||||
stringTagMap: {}
|
||||
disabledShaderPasses: []
|
||||
m_LockedProperties:
|
||||
m_SavedProperties:
|
||||
serializedVersion: 3
|
||||
m_TexEnvs:
|
||||
- _MainTex:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
m_Ints: []
|
||||
m_Floats:
|
||||
- _BackGroundColorAlpha: 0
|
||||
- _BorderColorAlpha: 1
|
||||
- _BorderExp: 0.001
|
||||
- _BorderThinkness: 1
|
||||
- _ColorMask: 15
|
||||
- _ContentExp: 0.001
|
||||
- _ContentThinkness: 1
|
||||
- _Exp: 1
|
||||
- _Exp1: 0.001
|
||||
- _Fill: -0.51
|
||||
- _Stencil: 0
|
||||
- _StencilComp: 8
|
||||
- _StencilOp: 0
|
||||
- _StencilReadMask: 255
|
||||
- _StencilWriteMask: 255
|
||||
- _Thinkness: 0
|
||||
- _UseUIAlphaClip: 0
|
||||
m_Colors:
|
||||
- _BorderColor: {r: 1, g: 1, b: 1, a: 1}
|
||||
- _BorderColor2: {r: 0, g: 0, b: 0, a: 0}
|
||||
- _Color: {r: 1, g: 1, b: 1, a: 1}
|
||||
- _FillColor: {r: 0.6698113, g: 0.6698113, b: 0.6698113, a: 0.7803922}
|
||||
- _InlineBorderColor2: {r: 0, g: 0, b: 0, a: 0}
|
||||
m_BuildTextureStacks: []
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 546be89eca1f2ec42a5eb603594282c0
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 2100000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,167 @@
|
||||
|
||||
Shader "XericLibrary/UIGraph/XericUICurve"
|
||||
{
|
||||
Properties
|
||||
{
|
||||
[PerRendererData] _MainTex ("Sprite Texture", 2D) = "white" {}
|
||||
_Color ("Tint", Color) = (1,1,1,1)
|
||||
|
||||
_StencilComp ("Stencil Comparison", Float) = 8
|
||||
_Stencil ("Stencil ID", Float) = 0
|
||||
_StencilOp ("Stencil Operation", Float) = 0
|
||||
_StencilWriteMask ("Stencil Write Mask", Float) = 255
|
||||
_StencilReadMask ("Stencil Read Mask", Float) = 255
|
||||
|
||||
_ColorMask ("Color Mask", Float) = 15
|
||||
|
||||
[Toggle(UNITY_UI_ALPHACLIP)] _UseUIAlphaClip ("Use Alpha Clip", Float) = 0
|
||||
|
||||
_EdgeExp( "Edge Exp", Range( 0.001, 1 ) ) = 0.8671591
|
||||
_EdgeStep( "Edge Step", Range( 0, 1 ) ) = 0
|
||||
_EdgeStepRange( "Edge Step Range", Range( 0, 0.5 ) ) = 0.1
|
||||
|
||||
}
|
||||
|
||||
SubShader
|
||||
{
|
||||
LOD 0
|
||||
|
||||
Tags { "Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" "PreviewType"="Plane" "CanUseSpriteAtlas"="True" }
|
||||
|
||||
Stencil
|
||||
{
|
||||
Ref [_Stencil]
|
||||
ReadMask [_StencilReadMask]
|
||||
WriteMask [_StencilWriteMask]
|
||||
Comp [_StencilComp]
|
||||
Pass [_StencilOp]
|
||||
}
|
||||
|
||||
|
||||
Cull Off
|
||||
Lighting Off
|
||||
ZWrite Off
|
||||
ZTest [unity_GUIZTestMode]
|
||||
Blend One OneMinusSrcAlpha
|
||||
ColorMask [_ColorMask]
|
||||
|
||||
|
||||
Pass
|
||||
{
|
||||
Name "Default"
|
||||
CGPROGRAM
|
||||
#define ASE_VERSION 19908
|
||||
|
||||
#pragma vertex vert
|
||||
#pragma fragment frag
|
||||
#pragma target 3.0
|
||||
|
||||
#include "UnityCG.cginc"
|
||||
#include "UnityUI.cginc"
|
||||
|
||||
#pragma multi_compile_local _ UNITY_UI_CLIP_RECT
|
||||
#pragma multi_compile_local _ UNITY_UI_ALPHACLIP
|
||||
|
||||
#define ASE_NEEDS_FRAG_COLOR
|
||||
#define ASE_NEEDS_TEXTURE_COORDINATES0
|
||||
#define ASE_NEEDS_FRAG_TEXTURE_COORDINATES0
|
||||
#define ASE_NEEDS_TEXTURE_COORDINATES2
|
||||
|
||||
|
||||
struct appdata_t
|
||||
{
|
||||
float4 vertex : POSITION;
|
||||
float4 color : COLOR;
|
||||
float2 texcoord : TEXCOORD0;
|
||||
UNITY_VERTEX_INPUT_INSTANCE_ID
|
||||
float4 ase_texcoord2 : TEXCOORD2;
|
||||
};
|
||||
|
||||
struct v2f
|
||||
{
|
||||
float4 vertex : SV_POSITION;
|
||||
fixed4 color : COLOR;
|
||||
float2 texcoord : TEXCOORD0;
|
||||
float4 worldPosition : TEXCOORD1;
|
||||
float4 mask : TEXCOORD2;
|
||||
UNITY_VERTEX_OUTPUT_STEREO
|
||||
float4 ase_texcoord3 : TEXCOORD3;
|
||||
};
|
||||
|
||||
sampler2D _MainTex;
|
||||
fixed4 _Color;
|
||||
fixed4 _TextureSampleAdd;
|
||||
float4 _ClipRect;
|
||||
float4 _MainTex_ST;
|
||||
float _UIMaskSoftnessX;
|
||||
float _UIMaskSoftnessY;
|
||||
|
||||
uniform float _EdgeStep;
|
||||
uniform float _EdgeStepRange;
|
||||
uniform float _EdgeExp;
|
||||
|
||||
|
||||
v2f vert(appdata_t v )
|
||||
{
|
||||
v2f OUT;
|
||||
UNITY_SETUP_INSTANCE_ID(v);
|
||||
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(OUT);
|
||||
|
||||
OUT.ase_texcoord3 = v.ase_texcoord2;
|
||||
|
||||
v.vertex.xyz += float3( 0, 0, 0 ) ;
|
||||
|
||||
float4 vPosition = UnityObjectToClipPos(v.vertex);
|
||||
OUT.worldPosition = v.vertex;
|
||||
OUT.vertex = vPosition;
|
||||
|
||||
float2 pixelSize = vPosition.w;
|
||||
pixelSize /= float2(1, 1) * abs(mul((float2x2)UNITY_MATRIX_P, _ScreenParams.xy));
|
||||
|
||||
float4 clampedRect = clamp(_ClipRect, -2e10, 2e10);
|
||||
float2 maskUV = (v.vertex.xy - clampedRect.xy) / (clampedRect.zw - clampedRect.xy);
|
||||
OUT.texcoord = v.texcoord;
|
||||
OUT.mask = float4(v.vertex.xy * 2 - clampedRect.xy - clampedRect.zw, 0.25 / (0.25 * half2(_UIMaskSoftnessX, _UIMaskSoftnessY) + abs(pixelSize.xy)));
|
||||
|
||||
OUT.color = v.color * _Color;
|
||||
return OUT;
|
||||
}
|
||||
|
||||
fixed4 frag(v2f IN ) : SV_Target
|
||||
{
|
||||
//Round up the alpha color coming from the interpolator (to 1.0/256.0 steps)
|
||||
//The incoming alpha could have numerical instability, which makes it very sensible to
|
||||
//HDR color transparency blend, when it blends with the world's texture.
|
||||
const half alphaPrecision = half(0xff);
|
||||
const half invAlphaPrecision = half(1.0/alphaPrecision);
|
||||
IN.color.a = round(IN.color.a * alphaPrecision)*invAlphaPrecision;
|
||||
|
||||
float2 texCoord1 = IN.texcoord.xy * float2( 1,1 ) + float2( 0,0 );
|
||||
float smoothstepResult24 = smoothstep( ( _EdgeStep - ( _EdgeStepRange * 2.0 ) ) , ( _EdgeStep + _EdgeStepRange ) , sin( ( texCoord1.x * UNITY_PI ) ));
|
||||
float4 texCoord8 = IN.ase_texcoord3;
|
||||
texCoord8.xy = IN.ase_texcoord3.xy * float2( 1,1 ) + float2( 0,0 );
|
||||
float4 lerpResult19 = lerp( IN.color , ( IN.color * pow( saturate( smoothstepResult24 ) , _EdgeExp ) ) , texCoord8.x);
|
||||
|
||||
|
||||
half4 color = lerpResult19;
|
||||
|
||||
#ifdef UNITY_UI_CLIP_RECT
|
||||
half2 m = saturate((_ClipRect.zw - _ClipRect.xy - abs(IN.mask.xy)) * IN.mask.zw);
|
||||
color.a *= m.x * m.y;
|
||||
#endif
|
||||
|
||||
#ifdef UNITY_UI_ALPHACLIP
|
||||
clip (color.a - 0.001);
|
||||
#endif
|
||||
|
||||
color.rgb *= color.a;
|
||||
|
||||
return color;
|
||||
}
|
||||
ENDCG
|
||||
}
|
||||
}
|
||||
CustomEditor "AmplifyShaderEditor.MaterialInspector"
|
||||
|
||||
Fallback Off
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ed13c09bee4e83848ad102719497b140
|
||||
ShaderImporter:
|
||||
externalObjects: {}
|
||||
defaultTextures: []
|
||||
nonModifiableTextures: []
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+10
-16
@@ -1,6 +1,5 @@
|
||||
// Made with Amplify Shader Editor v1.9.9.1
|
||||
// Available at the Unity Asset Store - http://u3d.as/y3X
|
||||
Shader "XUIDotBasic"
|
||||
|
||||
Shader "XericLibrary/UIGraph/UILineRender"
|
||||
{
|
||||
Properties
|
||||
{
|
||||
@@ -48,7 +47,7 @@ Shader "XUIDotBasic"
|
||||
{
|
||||
Name "Default"
|
||||
CGPROGRAM
|
||||
#define ASE_VERSION 19901
|
||||
#define ASE_VERSION 19908
|
||||
|
||||
#pragma vertex vert
|
||||
#pragma fragment frag
|
||||
@@ -60,7 +59,9 @@ Shader "XUIDotBasic"
|
||||
#pragma multi_compile_local _ UNITY_UI_CLIP_RECT
|
||||
#pragma multi_compile_local _ UNITY_UI_ALPHACLIP
|
||||
|
||||
|
||||
#define ASE_NEEDS_TEXTURE_COORDINATES0
|
||||
#define ASE_NEEDS_FRAG_TEXTURE_COORDINATES0
|
||||
|
||||
|
||||
struct appdata_t
|
||||
{
|
||||
@@ -127,10 +128,11 @@ Shader "XUIDotBasic"
|
||||
const half invAlphaPrecision = half(1.0/alphaPrecision);
|
||||
IN.color.a = round(IN.color.a * alphaPrecision)*invAlphaPrecision;
|
||||
|
||||
float4 appendResult2 = (float4(0.0 , 0.0 , 0.0 , 1.0));
|
||||
float2 texCoord1 = IN.texcoord.xy * float2( 1,1 ) + float2( 0,0 );
|
||||
float4 appendResult3 = (float4(texCoord1 , 0.0 , 1.0));
|
||||
|
||||
|
||||
half4 color = appendResult2;
|
||||
half4 color = appendResult3;
|
||||
|
||||
#ifdef UNITY_UI_CLIP_RECT
|
||||
half2 m = saturate((_ClipRect.zw - _ClipRect.xy - abs(IN.mask.xy)) * IN.mask.zw);
|
||||
@@ -151,12 +153,4 @@ Shader "XUIDotBasic"
|
||||
CustomEditor "AmplifyShaderEditor.MaterialInspector"
|
||||
|
||||
Fallback Off
|
||||
}
|
||||
/*ASEBEGIN
|
||||
Version=19901
|
||||
Node;AmplifyShaderEditor.DynamicAppendNode, AmplifyShaderEditor, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null;2;-256,0;Inherit;False;FLOAT4;4;0;FLOAT;0;False;1;FLOAT;0;False;2;FLOAT;0;False;3;FLOAT;1;False;1;FLOAT4;0
|
||||
Node;AmplifyShaderEditor.TextureCoordinatesNode, AmplifyShaderEditor, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null;3;-1024,-128;Inherit;False;0;-1;2;3;2;SAMPLER2D;;False;0;FLOAT2;1,1;False;1;FLOAT2;0,0;False;5;FLOAT2;0;FLOAT;1;FLOAT;2;FLOAT;3;FLOAT;4
|
||||
Node;AmplifyShaderEditor.TemplateMultiPassMasterNode, AmplifyShaderEditor, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null;0;0,0;Float;False;True;-1;3;AmplifyShaderEditor.MaterialInspector;0;3;XUIDotBasic;5056123faa0c79b47ab6ad7e8bf059a4;True;Default;0;0;Default;2;False;True;3;1;False;;10;False;;0;1;False;;0;False;;False;False;False;False;False;False;False;False;False;False;False;False;True;2;False;;False;True;True;True;True;True;0;True;_ColorMask;False;False;False;False;False;False;False;True;True;0;True;_Stencil;255;True;_StencilReadMask;255;True;_StencilWriteMask;0;True;_StencilComp;0;True;_StencilOp;0;False;;0;False;;0;False;;0;False;;0;False;;0;False;;False;True;2;False;;True;0;True;unity_GUIZTestMode;False;True;5;Queue=Transparent=Queue=0;IgnoreProjector=True;RenderType=Transparent=RenderType;PreviewType=Plane;CanUseSpriteAtlas=True;False;False;0;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;True;3;False;0;;0;0;Standard;0;0;1;True;False;;False;0
|
||||
WireConnection;0;0;2;0
|
||||
ASEEND*/
|
||||
//CHKSM=2D7C452CAA9F9AC9B28539E122A9F5072E3B0DC3
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9dacf142d8c5aee44bbbd2bd080fa3f5
|
||||
ShaderImporter:
|
||||
externalObjects: {}
|
||||
defaultTextures: []
|
||||
nonModifiableTextures: []
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,192 @@
|
||||
|
||||
Shader "XericLibrary/UIGraph/UIPattern"
|
||||
{
|
||||
Properties
|
||||
{
|
||||
[PerRendererData] _MainTex ("Sprite Texture", 2D) = "white" {}
|
||||
_Color ("Tint", Color) = (1,1,1,1)
|
||||
|
||||
_StencilComp ("Stencil Comparison", Float) = 8
|
||||
_Stencil ("Stencil ID", Float) = 0
|
||||
_StencilOp ("Stencil Operation", Float) = 0
|
||||
_StencilWriteMask ("Stencil Write Mask", Float) = 255
|
||||
_StencilReadMask ("Stencil Read Mask", Float) = 255
|
||||
|
||||
_ColorMask ("Color Mask", Float) = 15
|
||||
|
||||
[Toggle(UNITY_UI_ALPHACLIP)] _UseUIAlphaClip ("Use Alpha Clip", Float) = 0
|
||||
|
||||
_BorderColorAlpha( "BorderColorAlpha", Range( -0.1, 1 ) ) = 1
|
||||
_BorderThinkness( "Border Thinkness", Range( 0.001, 1 ) ) = 1
|
||||
_BorderExp( "Border Exp", Range( 0.001, 1 ) ) = 0.001
|
||||
_BackGroundColorAlpha( "BackGroundColorAlpha", Range( -0.1, 1 ) ) = 0
|
||||
_ContentThinkness( "Content Thinkness", Range( 0.001, 1 ) ) = 1
|
||||
_ContentExp( "Content Exp", Range( 0.001, 1 ) ) = 0.001
|
||||
|
||||
}
|
||||
|
||||
SubShader
|
||||
{
|
||||
LOD 0
|
||||
|
||||
Tags { "Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" "PreviewType"="Plane" "CanUseSpriteAtlas"="True" }
|
||||
|
||||
Stencil
|
||||
{
|
||||
Ref [_Stencil]
|
||||
ReadMask [_StencilReadMask]
|
||||
WriteMask [_StencilWriteMask]
|
||||
Comp [_StencilComp]
|
||||
Pass [_StencilOp]
|
||||
}
|
||||
|
||||
|
||||
Cull Off
|
||||
Lighting Off
|
||||
ZWrite Off
|
||||
ZTest [unity_GUIZTestMode]
|
||||
Blend One OneMinusSrcAlpha
|
||||
ColorMask [_ColorMask]
|
||||
|
||||
|
||||
Pass
|
||||
{
|
||||
Name "Default"
|
||||
CGPROGRAM
|
||||
#define ASE_VERSION 19908
|
||||
|
||||
#pragma vertex vert
|
||||
#pragma fragment frag
|
||||
#pragma target 3.0
|
||||
|
||||
#include "UnityCG.cginc"
|
||||
#include "UnityUI.cginc"
|
||||
|
||||
#pragma multi_compile_local _ UNITY_UI_CLIP_RECT
|
||||
#pragma multi_compile_local _ UNITY_UI_ALPHACLIP
|
||||
|
||||
#define ASE_NEEDS_FRAG_NORMAL
|
||||
#define ASE_NEEDS_FRAG_COLOR
|
||||
#define ASE_NEEDS_TEXTURE_COORDINATES2
|
||||
#define ASE_NEEDS_FRAG_TANGENT
|
||||
|
||||
|
||||
struct appdata_t
|
||||
{
|
||||
float4 vertex : POSITION;
|
||||
float4 color : COLOR;
|
||||
float2 texcoord : TEXCOORD0;
|
||||
UNITY_VERTEX_INPUT_INSTANCE_ID
|
||||
float3 ase_normal : NORMAL;
|
||||
float4 ase_texcoord2 : TEXCOORD2;
|
||||
float4 ase_tangent : TANGENT;
|
||||
};
|
||||
|
||||
struct v2f
|
||||
{
|
||||
float4 vertex : SV_POSITION;
|
||||
fixed4 color : COLOR;
|
||||
float2 texcoord : TEXCOORD0;
|
||||
float4 worldPosition : TEXCOORD1;
|
||||
float4 mask : TEXCOORD2;
|
||||
UNITY_VERTEX_OUTPUT_STEREO
|
||||
float3 ase_normal : NORMAL;
|
||||
float4 ase_texcoord3 : TEXCOORD3;
|
||||
float4 ase_tangent : TANGENT;
|
||||
};
|
||||
|
||||
sampler2D _MainTex;
|
||||
fixed4 _Color;
|
||||
fixed4 _TextureSampleAdd;
|
||||
float4 _ClipRect;
|
||||
float4 _MainTex_ST;
|
||||
float _UIMaskSoftnessX;
|
||||
float _UIMaskSoftnessY;
|
||||
|
||||
uniform float _BackGroundColorAlpha;
|
||||
uniform float _ContentThinkness;
|
||||
uniform float _ContentExp;
|
||||
uniform float _BorderColorAlpha;
|
||||
uniform float _BorderThinkness;
|
||||
uniform float _BorderExp;
|
||||
|
||||
|
||||
v2f vert(appdata_t v )
|
||||
{
|
||||
v2f OUT;
|
||||
UNITY_SETUP_INSTANCE_ID(v);
|
||||
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(OUT);
|
||||
|
||||
OUT.ase_normal = v.ase_normal;
|
||||
OUT.ase_texcoord3.xy = v.ase_texcoord2.xy;
|
||||
OUT.ase_tangent = v.ase_tangent;
|
||||
|
||||
//setting value to unused interpolator channels and avoid initialization warnings
|
||||
OUT.ase_texcoord3.zw = 0;
|
||||
|
||||
v.vertex.xyz += float3( 0, 0, 0 ) ;
|
||||
|
||||
float4 vPosition = UnityObjectToClipPos(v.vertex);
|
||||
OUT.worldPosition = v.vertex;
|
||||
OUT.vertex = vPosition;
|
||||
|
||||
float2 pixelSize = vPosition.w;
|
||||
pixelSize /= float2(1, 1) * abs(mul((float2x2)UNITY_MATRIX_P, _ScreenParams.xy));
|
||||
|
||||
float4 clampedRect = clamp(_ClipRect, -2e10, 2e10);
|
||||
float2 maskUV = (v.vertex.xy - clampedRect.xy) / (clampedRect.zw - clampedRect.xy);
|
||||
OUT.texcoord = v.texcoord;
|
||||
OUT.mask = float4(v.vertex.xy * 2 - clampedRect.xy - clampedRect.zw, 0.25 / (0.25 * half2(_UIMaskSoftnessX, _UIMaskSoftnessY) + abs(pixelSize.xy)));
|
||||
|
||||
OUT.color = v.color * _Color;
|
||||
return OUT;
|
||||
}
|
||||
|
||||
fixed4 frag(v2f IN ) : SV_Target
|
||||
{
|
||||
//Round up the alpha color coming from the interpolator (to 1.0/256.0 steps)
|
||||
//The incoming alpha could have numerical instability, which makes it very sensible to
|
||||
//HDR color transparency blend, when it blends with the world's texture.
|
||||
const half alphaPrecision = half(0xff);
|
||||
const half invAlphaPrecision = half(1.0/alphaPrecision);
|
||||
IN.color.a = round(IN.color.a * alphaPrecision)*invAlphaPrecision;
|
||||
|
||||
float4 appendResult60 = (float4(IN.ase_normal , saturate( max( max( IN.ase_normal.x, IN.ase_normal.y ), IN.ase_normal.z ) )));
|
||||
float4 appendResult43 = (float4(IN.ase_normal , _BackGroundColorAlpha));
|
||||
float4 lerpResult50 = lerp( appendResult60 , appendResult43 , saturate( sign( ( _BackGroundColorAlpha + 0.001 ) ) ));
|
||||
float4 Background_Color39 = lerpResult50;
|
||||
float4 Center_Color40 = IN.color;
|
||||
float2 texCoord4 = IN.ase_texcoord3.xy * float2( 1,1 ) + float2( 0,0 );
|
||||
float lerpResult107 = lerp( texCoord4.x , 0.0 , texCoord4.y);
|
||||
float4 lerpResult64 = lerp( Background_Color39 , Center_Color40 , pow( ( ( _ContentThinkness - lerpResult107 ) / _ContentThinkness ) , _ContentExp ));
|
||||
float4 appendResult57 = (float4(IN.ase_tangent.xyz , saturate( max( max( IN.ase_tangent.xyz.x, IN.ase_tangent.xyz.y ), IN.ase_tangent.xyz.z ) )));
|
||||
float4 appendResult44 = (float4(IN.ase_tangent.xyz , _BorderColorAlpha));
|
||||
float4 lerpResult51 = lerp( appendResult57 , appendResult44 , saturate( sign( ( _BorderColorAlpha + 0.001 ) ) ));
|
||||
float4 Border_Color41 = lerpResult51;
|
||||
float lerpResult108 = lerp( 0.0 , texCoord4.x , texCoord4.y);
|
||||
float4 lerpResult82 = lerp( Background_Color39 , Border_Color41 , pow( ( ( _BorderThinkness - lerpResult108 ) / _BorderThinkness ) , _BorderExp ));
|
||||
float4 lerpResult109 = lerp( lerpResult64 , lerpResult82 , texCoord4.y);
|
||||
|
||||
|
||||
half4 color = lerpResult109;
|
||||
|
||||
#ifdef UNITY_UI_CLIP_RECT
|
||||
half2 m = saturate((_ClipRect.zw - _ClipRect.xy - abs(IN.mask.xy)) * IN.mask.zw);
|
||||
color.a *= m.x * m.y;
|
||||
#endif
|
||||
|
||||
#ifdef UNITY_UI_ALPHACLIP
|
||||
clip (color.a - 0.001);
|
||||
#endif
|
||||
|
||||
color.rgb *= color.a;
|
||||
|
||||
return color;
|
||||
}
|
||||
ENDCG
|
||||
}
|
||||
}
|
||||
CustomEditor "AmplifyShaderEditor.MaterialInspector"
|
||||
|
||||
Fallback Off
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f4e5237cd89192c4f8361db3dec5cc3b
|
||||
ShaderImporter:
|
||||
externalObjects: {}
|
||||
defaultTextures: []
|
||||
nonModifiableTextures: []
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Binary file not shown.
@@ -1,33 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c754cc17629bc0f4cac54d1417f746b3
|
||||
PluginImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
iconMap: {}
|
||||
executionOrder: {}
|
||||
defineConstraints: []
|
||||
isPreloaded: 0
|
||||
isOverridable: 1
|
||||
isExplicitlyReferenced: 0
|
||||
validateReferences: 1
|
||||
platformData:
|
||||
- first:
|
||||
Any:
|
||||
second:
|
||||
enabled: 1
|
||||
settings: {}
|
||||
- first:
|
||||
Editor: Editor
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
DefaultValueInitialized: true
|
||||
- first:
|
||||
Windows Store Apps: WindowsStoreApps
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
CPU: AnyCPU
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,5 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c9a8ab2ab6a3f6848a9c0a06cde9f082
|
||||
guid: 9a4fde83e9857614fadbbb44cf04d8da
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
@@ -0,0 +1,534 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
#if ENABLE_BURST
|
||||
using Unity.Jobs;
|
||||
using Unity.Collections;
|
||||
#endif
|
||||
|
||||
namespace XericLibrary.Runtime.UIGraph
|
||||
{
|
||||
/// <summary>
|
||||
/// 基于缓存结构的线段渲染器基类
|
||||
/// 数据由 NativeArray 管理,支持脏标记范围更新和 Jobs 并行生成
|
||||
/// </summary>
|
||||
[RequireComponent(typeof(CanvasRenderer))]
|
||||
public abstract class CacheLineRendererBase : MaskableGraphic
|
||||
{
|
||||
#region 数据缓存
|
||||
|
||||
/// <summary>
|
||||
/// 所有线段端点的扁平缓存
|
||||
/// </summary>
|
||||
protected List<LineVertexData> m_VertexData = new List<LineVertexData>();
|
||||
|
||||
/// <summary>
|
||||
/// 线段范围数组
|
||||
/// </summary>
|
||||
protected List<LineSegmentRange> m_SegmentRanges = new List<LineSegmentRange>();
|
||||
|
||||
/// <summary>
|
||||
/// 待添加的端点(Begin/End API 模式用)
|
||||
/// </summary>
|
||||
protected List<LineVertexData> m_PendingVertices = new List<LineVertexData>();
|
||||
|
||||
/// <summary>
|
||||
/// 当前 Begin/End 模式的 uvMode
|
||||
/// </summary>
|
||||
protected UVMode m_PendingUVMode = UVMode.ByDistance;
|
||||
|
||||
/// <summary>
|
||||
/// 是否正在 Begin/End 会话中
|
||||
/// </summary>
|
||||
protected bool m_IsBuilding;
|
||||
|
||||
/// <summary>
|
||||
/// 模型顶点缓存 (segmentCount × 5)
|
||||
/// </summary>
|
||||
protected List<UIVertex> m_MeshVertexCache = new List<UIVertex>();
|
||||
|
||||
/// <summary>
|
||||
/// 三角形索引缓存
|
||||
/// </summary>
|
||||
protected List<int> m_MeshIndexCache = new List<int>();
|
||||
|
||||
/// <summary>
|
||||
/// 脏标记范围起点(线段范围索引),-1 表示无脏标记
|
||||
/// </summary>
|
||||
protected int m_DirtyStartSegment = -1;
|
||||
|
||||
/// <summary>
|
||||
/// 脏标记范围终点(线段范围索引)
|
||||
/// </summary>
|
||||
protected int m_DirtyEndSegment = -1;
|
||||
|
||||
/// <summary>
|
||||
/// 是否使用 Jobs 模式
|
||||
/// </summary>
|
||||
protected static bool UseJobs =>
|
||||
#if ENABLE_BURST
|
||||
SystemInfo.graphicsDeviceType != UnityEngine.Rendering.GraphicsDeviceType.Null &&
|
||||
Application.platform != RuntimePlatform.WebGLPlayer;
|
||||
#else
|
||||
false;
|
||||
#endif
|
||||
|
||||
#endregion
|
||||
|
||||
#region 线段添加
|
||||
|
||||
/// <summary>
|
||||
/// 批量添加一根完整线段
|
||||
/// </summary>
|
||||
/// <param name="vertices">线段端点列表(至少2个)</param>
|
||||
/// <param name="uvMode">UV1 均分模式</param>
|
||||
/// <returns>线段范围索引</returns>
|
||||
public int AddLine(LineVertexData[] vertices, UVMode uvMode = UVMode.ByDistance)
|
||||
{
|
||||
if (vertices == null || vertices.Length < 2)
|
||||
return -1;
|
||||
|
||||
int startIndex = m_VertexData.Count;
|
||||
m_VertexData.AddRange(vertices);
|
||||
|
||||
var range = new LineSegmentRange
|
||||
{
|
||||
startVertexIndex = startIndex,
|
||||
vertexCount = vertices.Length,
|
||||
uvMode = uvMode
|
||||
};
|
||||
|
||||
int rangeIndex = m_SegmentRanges.Count;
|
||||
m_SegmentRanges.Add(range);
|
||||
|
||||
MarkDirty(rangeIndex);
|
||||
return rangeIndex;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 开始画线(记录模式)
|
||||
/// </summary>
|
||||
public void BeginLine(UVMode uvMode = UVMode.ByDistance)
|
||||
{
|
||||
m_IsBuilding = true;
|
||||
m_PendingUVMode = uvMode;
|
||||
m_PendingVertices.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 添加画线节点到当前记录
|
||||
/// </summary>
|
||||
public void AddLineNode(Vector2 position, Color32 color, float thickness, Vector4 userData = default)
|
||||
{
|
||||
if (!m_IsBuilding) return;
|
||||
m_PendingVertices.Add(new LineVertexData
|
||||
{
|
||||
position = position,
|
||||
color = color,
|
||||
thickness = thickness,
|
||||
userData = userData
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 添加画线节点
|
||||
/// </summary>
|
||||
public void AddLineNode(LineVertexData node)
|
||||
{
|
||||
if (!m_IsBuilding) return;
|
||||
m_PendingVertices.Add(node);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 结束画线,将挂起列表写入缓存
|
||||
/// </summary>
|
||||
public void EndLine()
|
||||
{
|
||||
if (!m_IsBuilding) return;
|
||||
m_IsBuilding = false;
|
||||
|
||||
if (m_PendingVertices.Count < 2)
|
||||
{
|
||||
m_PendingVertices.Clear();
|
||||
return;
|
||||
}
|
||||
|
||||
int startIndex = m_VertexData.Count;
|
||||
m_VertexData.AddRange(m_PendingVertices);
|
||||
|
||||
var range = new LineSegmentRange
|
||||
{
|
||||
startVertexIndex = startIndex,
|
||||
vertexCount = m_PendingVertices.Count,
|
||||
uvMode = m_PendingUVMode
|
||||
};
|
||||
|
||||
int rangeIndex = m_SegmentRanges.Count;
|
||||
m_SegmentRanges.Add(range);
|
||||
m_PendingVertices.Clear();
|
||||
|
||||
MarkDirty(rangeIndex);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 线段管理
|
||||
|
||||
/// <summary>
|
||||
/// 移除指定线段
|
||||
/// </summary>
|
||||
public void RemoveLine(int segmentRangeIndex)
|
||||
{
|
||||
if (segmentRangeIndex < 0 || segmentRangeIndex >= m_SegmentRanges.Count)
|
||||
return;
|
||||
|
||||
m_SegmentRanges.RemoveAt(segmentRangeIndex);
|
||||
// 移除后后续所有线段索引发生变化,需完全重建
|
||||
RebuildAll();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 清空所有线段
|
||||
/// </summary>
|
||||
public void ClearAll()
|
||||
{
|
||||
m_VertexData.Clear();
|
||||
m_SegmentRanges.Clear();
|
||||
m_MeshVertexCache.Clear();
|
||||
m_MeshIndexCache.Clear();
|
||||
m_DirtyStartSegment = -1;
|
||||
m_DirtyEndSegment = -1;
|
||||
SetVerticesDirty();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 标记指定线段为脏(需要重建)
|
||||
/// </summary>
|
||||
public void SetSegmentDirty(int segmentRangeIndex)
|
||||
{
|
||||
MarkDirty(segmentRangeIndex);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 脏标记缓存
|
||||
|
||||
/// <summary>
|
||||
/// 标记脏范围(合并到现有范围)
|
||||
/// </summary>
|
||||
protected void MarkDirty(int segmentIndex)
|
||||
{
|
||||
if (m_DirtyStartSegment < 0 || segmentIndex < m_DirtyStartSegment)
|
||||
m_DirtyStartSegment = segmentIndex;
|
||||
if (m_DirtyEndSegment < 0 || segmentIndex > m_DirtyEndSegment)
|
||||
m_DirtyEndSegment = segmentIndex;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 标记所有线段为脏(下次 LateUpdate 全量重绘)
|
||||
/// 适用于:更新顶点数据后,不改变元素数量的重绘
|
||||
/// </summary>
|
||||
public void RebuildAll()
|
||||
{
|
||||
m_DirtyStartSegment = 0;
|
||||
m_DirtyEndSegment = m_SegmentRanges.Count - 1;
|
||||
if (m_DirtyEndSegment < 0)
|
||||
m_DirtyStartSegment = -1;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 顶点生成
|
||||
|
||||
protected virtual void LateUpdate()
|
||||
{
|
||||
if (m_DirtyStartSegment < 0)
|
||||
return;
|
||||
|
||||
// 1) 统计脏范围内所有子段数(一个 range 有 vertexCount-1 个子段)
|
||||
int totalDirtySegments = 0;
|
||||
for (int ri = m_DirtyStartSegment; ri <= m_DirtyEndSegment; ri++)
|
||||
totalDirtySegments += m_SegmentRanges[ri].vertexCount - 1;
|
||||
|
||||
if (totalDirtySegments == 0)
|
||||
{
|
||||
m_DirtyStartSegment = -1;
|
||||
m_DirtyEndSegment = -1;
|
||||
return;
|
||||
}
|
||||
|
||||
// 2) 确保 mesh 顶点缓存足够大
|
||||
int totalSegments = GetTotalSegmentCount();
|
||||
int neededVertices = totalSegments * LineMeshGenerator.VerticesPerSegment;
|
||||
if (m_MeshVertexCache.Count < neededVertices)
|
||||
{
|
||||
int growBy = neededVertices - m_MeshVertexCache.Count;
|
||||
for (int i = 0; i < growBy; i++)
|
||||
m_MeshVertexCache.Add(new UIVertex());
|
||||
}
|
||||
|
||||
m_MeshIndexCache.Clear();
|
||||
|
||||
// 3) 按子段构建 SegmentJobInput(同时计算 ByDistance 的 totalDist 缓存)
|
||||
int globalSegBase = GetGlobalSegmentOffset(m_DirtyStartSegment);
|
||||
var inputs = new List<SegmentJobInput>(totalDirtySegments);
|
||||
int inSegIdx = 0;
|
||||
for (int ri = m_DirtyStartSegment; ri <= m_DirtyEndSegment; ri++)
|
||||
{
|
||||
var range = m_SegmentRanges[ri];
|
||||
float cachedTotalDist = (range.uvMode == UVMode.ByDistance)
|
||||
? CalculateRangeTotalDistance(range)
|
||||
: -1f;
|
||||
int subSegCount = range.vertexCount - 1;
|
||||
for (int si = 0; si < subSegCount; si++)
|
||||
{
|
||||
inputs.Add(BuildSegmentInput(range, si,
|
||||
globalSegBase + inSegIdx, totalSegments, cachedTotalDist));
|
||||
inSegIdx++;
|
||||
}
|
||||
}
|
||||
|
||||
// 4) 生成顶点(串行或 Jobs)
|
||||
if (UseJobs)
|
||||
{
|
||||
#if ENABLE_BURST
|
||||
GenerateMeshJobs(inputs, globalSegBase);
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
GenerateMeshSerial(inputs, globalSegBase);
|
||||
}
|
||||
|
||||
// 5) 生成三角形索引
|
||||
GenerateIndicesForRange(m_DirtyStartSegment, m_DirtyEndSegment,
|
||||
globalSegBase);
|
||||
|
||||
// 清除脏标记
|
||||
SetVerticesDirty();
|
||||
m_DirtyStartSegment = -1;
|
||||
m_DirtyEndSegment = -1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 串行生成顶点
|
||||
/// </summary>
|
||||
private void GenerateMeshSerial(List<SegmentJobInput> inputs, int globalSegBase)
|
||||
{
|
||||
var tempVerts = new UIVertex[LineMeshGenerator.VerticesPerSegment];
|
||||
for (int i = 0; i < inputs.Count; i++)
|
||||
{
|
||||
int meshVertStart = (globalSegBase + i) * LineMeshGenerator.VerticesPerSegment;
|
||||
LineMeshGenerator.GenerateSingleSegment(inputs[i], tempVerts, 0);
|
||||
for (int v = 0; v < LineMeshGenerator.VerticesPerSegment; v++)
|
||||
{
|
||||
int cacheIdx = meshVertStart + v;
|
||||
if (cacheIdx < m_MeshVertexCache.Count)
|
||||
m_MeshVertexCache[cacheIdx] = tempVerts[v];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if ENABLE_BURST
|
||||
/// <summary>
|
||||
/// Jobs 并行生成顶点
|
||||
/// </summary>
|
||||
private void GenerateMeshJobs(List<SegmentJobInput> inputs, int globalSegBase)
|
||||
{
|
||||
int count = inputs.Count;
|
||||
int outputSize = count * LineMeshGenerator.VerticesPerSegment;
|
||||
|
||||
var nativeInputs = new NativeArray<SegmentJobInput>(count, Allocator.TempJob);
|
||||
var nativeOutputs = new NativeArray<UIVertex>(outputSize, Allocator.TempJob);
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
nativeInputs[i] = inputs[i];
|
||||
|
||||
var handle = LineMeshGenerator.GenerateSegmentsJobs(nativeInputs, nativeOutputs);
|
||||
handle.Complete();
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
int srcStart = i * LineMeshGenerator.VerticesPerSegment;
|
||||
int dstStart = (globalSegBase + i) * LineMeshGenerator.VerticesPerSegment;
|
||||
for (int v = 0; v < LineMeshGenerator.VerticesPerSegment; v++)
|
||||
{
|
||||
if (dstStart + v < m_MeshVertexCache.Count)
|
||||
m_MeshVertexCache[dstStart + v] = nativeOutputs[srcStart + v];
|
||||
}
|
||||
}
|
||||
|
||||
nativeInputs.Dispose();
|
||||
nativeOutputs.Dispose();
|
||||
}
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// 生成指定 range 范围中所有子段的三角形索引
|
||||
/// 每条线的首个子段不生成转角连接三角形
|
||||
/// </summary>
|
||||
private void GenerateIndicesForRange(int startRange, int endRange,
|
||||
int globalSegBase)
|
||||
{
|
||||
int segIdx = globalSegBase;
|
||||
for (int ri = startRange; ri <= endRange; ri++)
|
||||
{
|
||||
int subSegCount = m_SegmentRanges[ri].vertexCount - 1;
|
||||
for (int si = 0; si < subSegCount; si++)
|
||||
{
|
||||
// 子段 si == 0 是该条线的首段 → 无前驱同线段 → hasPrevInLine = false
|
||||
bool hasPrevInLine = (si > 0);
|
||||
LineMeshGenerator.GenerateSegmentIndices(segIdx, hasPrevInLine, m_MeshIndexCache);
|
||||
segIdx++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 计算某条线段包含的段数(端点数量 - 1)
|
||||
/// </summary>
|
||||
protected static int GetSegmentCountForRange(in LineSegmentRange range) => range.vertexCount - 1;
|
||||
|
||||
/// <summary>
|
||||
/// 获取总段数(所有线段累加)
|
||||
/// </summary>
|
||||
protected int GetTotalSegmentCount()
|
||||
{
|
||||
int count = 0;
|
||||
for (int i = 0; i < m_SegmentRanges.Count; i++)
|
||||
count += GetSegmentCountForRange(m_SegmentRanges[i]);
|
||||
return count;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取指定线段范围的全局段偏移
|
||||
/// </summary>
|
||||
protected int GetGlobalSegmentOffset(int rangeIndex)
|
||||
{
|
||||
int offset = 0;
|
||||
for (int i = 0; i < rangeIndex; i++)
|
||||
offset += GetSegmentCountForRange(m_SegmentRanges[i]);
|
||||
return offset;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 计算指定 range 的总距离(用于 ByDistance UV 模式)
|
||||
/// </summary>
|
||||
private float CalculateRangeTotalDistance(in LineSegmentRange range)
|
||||
{
|
||||
float total = 0f;
|
||||
int segCount = range.vertexCount - 1;
|
||||
for (int i = 0; i < segCount; i++)
|
||||
{
|
||||
int idx = range.startVertexIndex + i;
|
||||
total += Vector2.Distance(m_VertexData[idx].position,
|
||||
m_VertexData[idx + 1].position);
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 构建指定范围内第 inSegmentIndex 段的输入参数
|
||||
/// </summary>
|
||||
protected SegmentJobInput BuildSegmentInput(
|
||||
in LineSegmentRange range, int inSegmentIndex,
|
||||
int globalSegmentIndex, int totalSegments,
|
||||
float cachedTotalDist = -1f)
|
||||
{
|
||||
int startIdx = range.startVertexIndex + inSegmentIndex;
|
||||
var start = m_VertexData[startIdx];
|
||||
var end = m_VertexData[startIdx + 1];
|
||||
|
||||
var input = new SegmentJobInput
|
||||
{
|
||||
start = start,
|
||||
end = end,
|
||||
segmentIndex = globalSegmentIndex,
|
||||
totalSegmentCount = totalSegments,
|
||||
uvMode = range.uvMode,
|
||||
hasPrev = inSegmentIndex > 0 ? (byte)1 : (byte)0,
|
||||
hasNext = (inSegmentIndex < range.vertexCount - 2) ? (byte)1 : (byte)0
|
||||
};
|
||||
|
||||
if (input.hasPrev == 1)
|
||||
input.prevEnd = m_VertexData[startIdx - 1];
|
||||
if (input.hasNext == 1)
|
||||
input.nextStart = m_VertexData[startIdx + 2];
|
||||
|
||||
// 计算 startLineUV / endLineUV
|
||||
int segCount = GetSegmentCountForRange(range);
|
||||
if (range.uvMode == UVMode.BySegment)
|
||||
{
|
||||
float t0 = (float)inSegmentIndex / segCount;
|
||||
float t1 = (float)(inSegmentIndex + 1) / segCount;
|
||||
input.startLineUV = t0;
|
||||
input.endLineUV = t1;
|
||||
}
|
||||
else // ByDistance
|
||||
{
|
||||
float totalDist = cachedTotalDist >= 0f
|
||||
? cachedTotalDist
|
||||
: CalculateRangeTotalDistance(range);
|
||||
|
||||
if (totalDist < float.Epsilon)
|
||||
{
|
||||
input.startLineUV = (float)inSegmentIndex / segCount;
|
||||
input.endLineUV = (float)(inSegmentIndex + 1) / segCount;
|
||||
}
|
||||
else
|
||||
{
|
||||
float distSoFar = 0f;
|
||||
for (int i = 0; i < inSegmentIndex; i++)
|
||||
{
|
||||
int idx = range.startVertexIndex + i;
|
||||
distSoFar += Vector2.Distance(m_VertexData[idx].position,
|
||||
m_VertexData[idx + 1].position);
|
||||
}
|
||||
float segDist = Vector2.Distance(start.position, end.position);
|
||||
input.startLineUV = distSoFar / totalDist;
|
||||
input.endLineUV = (distSoFar + segDist) / totalDist;
|
||||
}
|
||||
}
|
||||
|
||||
return input;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region OnPopulateMesh
|
||||
|
||||
protected override void OnPopulateMesh(VertexHelper vh)
|
||||
{
|
||||
vh.Clear();
|
||||
|
||||
if (m_MeshVertexCache.Count == 0 || m_MeshIndexCache.Count == 0)
|
||||
return;
|
||||
|
||||
// 将缓存的顶点和索引写入 VertexHelper
|
||||
vh.AddUIVertexStream(m_MeshVertexCache, m_MeshIndexCache);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 生命周期
|
||||
|
||||
protected override void OnEnable()
|
||||
{
|
||||
base.OnEnable();
|
||||
RebuildAll();
|
||||
}
|
||||
|
||||
protected override void OnDisable()
|
||||
{
|
||||
base.OnDisable();
|
||||
}
|
||||
|
||||
protected override void OnDestroy()
|
||||
{
|
||||
base.OnDestroy();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ad4b412c6e5cfe14194c22997929cdee
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,245 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace XericLibrary.Runtime.UIGraph
|
||||
{
|
||||
/// <summary>
|
||||
/// 曲线缓存渲染器基类
|
||||
/// 管理 CurveCacheEntry 列表 + 扁平控制点/宽度/箭头缓存 + 脏标记增量重建
|
||||
/// LateUpdate 中只重建脏标记范围内的曲线
|
||||
/// </summary>
|
||||
[RequireComponent(typeof(CanvasRenderer))]
|
||||
public abstract class CurveCacheRendererBase : MaskableGraphic
|
||||
{
|
||||
#region 默认材质
|
||||
|
||||
private static Material s_DefaultMaterial;
|
||||
private const string DefaultShaderPath = "XericLibrary/UIGraph/XericUICurve";
|
||||
|
||||
public override Material defaultMaterial
|
||||
{
|
||||
get
|
||||
{
|
||||
if (s_DefaultMaterial == null)
|
||||
{
|
||||
var shader = Shader.Find(DefaultShaderPath);
|
||||
if (shader != null)
|
||||
s_DefaultMaterial = new Material(shader);
|
||||
else
|
||||
s_DefaultMaterial = base.defaultMaterial;
|
||||
}
|
||||
return s_DefaultMaterial;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 数据缓存
|
||||
|
||||
/// <summary>曲线条目列表</summary>
|
||||
protected List<CurveCacheEntry> m_Curves = new List<CurveCacheEntry>();
|
||||
|
||||
/// <summary>所有曲线的控制点扁平存储</summary>
|
||||
protected List<Vector2> m_ControlPoints = new List<Vector2>();
|
||||
|
||||
/// <summary>所有曲线的宽度扁平存储(与控制点一一对应)</summary>
|
||||
protected List<float> m_Widths = new List<float>();
|
||||
|
||||
/// <summary>所有曲线的箭头扁平存储</summary>
|
||||
protected List<ArrowHeadData> m_Arrows = new List<ArrowHeadData>();
|
||||
|
||||
/// <summary>模型顶点缓存</summary>
|
||||
protected List<UIVertex> m_MeshVertexCache = new List<UIVertex>();
|
||||
|
||||
/// <summary>三角形索引缓存</summary>
|
||||
protected List<int> m_MeshIndexCache = new List<int>();
|
||||
|
||||
/// <summary>脏标记范围起点(曲线索引),-1 表示无脏标记</summary>
|
||||
protected int m_DirtyStartIndex = -1;
|
||||
|
||||
/// <summary>脏标记范围终点(曲线索引)</summary>
|
||||
protected int m_DirtyEndIndex = -1;
|
||||
|
||||
#endregion
|
||||
|
||||
#region API
|
||||
|
||||
/// <summary>
|
||||
/// 添加一条曲线
|
||||
/// </summary>
|
||||
/// <param name="entry">曲线缓存条目(控制点计数和起始索引需预先设置)</param>
|
||||
/// <param name="controlPoints">控制点数组</param>
|
||||
/// <param name="widths">宽度数组(长度需与控制点一致)</param>
|
||||
/// <param name="arrows">箭头数组(可选)</param>
|
||||
/// <returns>曲线索引</returns>
|
||||
public int AddCurve(CurveCacheEntry entry, Vector2[] controlPoints, float[] widths, ArrowHeadData[] arrows = null)
|
||||
{
|
||||
int index = m_Curves.Count;
|
||||
|
||||
// 填充起始索引
|
||||
entry.controlPointStartIndex = m_ControlPoints.Count;
|
||||
entry.widthStartIndex = m_Widths.Count;
|
||||
entry.arrowStartIndex = (arrows != null && arrows.Length > 0) ? m_Arrows.Count : -1;
|
||||
entry.arrowCount = arrows?.Length ?? 0;
|
||||
entry.controlPointCount = controlPoints.Length;
|
||||
|
||||
m_Curves.Add(entry);
|
||||
m_ControlPoints.AddRange(controlPoints);
|
||||
m_Widths.AddRange(widths);
|
||||
if (arrows != null)
|
||||
m_Arrows.AddRange(arrows);
|
||||
|
||||
MarkDirty(index);
|
||||
return index;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 移除指定曲线
|
||||
/// </summary>
|
||||
public void RemoveCurve(int index)
|
||||
{
|
||||
if (index < 0 || index >= m_Curves.Count) return;
|
||||
m_Curves.RemoveAt(index);
|
||||
RebuildAll();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 标记指定曲线为脏
|
||||
/// </summary>
|
||||
public void SetCurveDirty(int index)
|
||||
{
|
||||
MarkDirty(index);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 清空所有曲线
|
||||
/// </summary>
|
||||
public void ClearAll()
|
||||
{
|
||||
m_Curves.Clear();
|
||||
m_ControlPoints.Clear();
|
||||
m_Widths.Clear();
|
||||
m_Arrows.Clear();
|
||||
m_MeshVertexCache.Clear();
|
||||
m_MeshIndexCache.Clear();
|
||||
m_DirtyStartIndex = -1;
|
||||
m_DirtyEndIndex = -1;
|
||||
SetVerticesDirty();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 脏标记
|
||||
|
||||
protected void MarkDirty(int index)
|
||||
{
|
||||
if (m_DirtyStartIndex < 0 || index < m_DirtyStartIndex)
|
||||
m_DirtyStartIndex = index;
|
||||
if (m_DirtyEndIndex < 0 || index > m_DirtyEndIndex)
|
||||
m_DirtyEndIndex = index;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 标记所有曲线为脏(下次 LateUpdate 全量重绘)
|
||||
/// 适用于:更新控制点/宽度后,不改变元素数量的重绘
|
||||
/// </summary>
|
||||
public void RebuildAll()
|
||||
{
|
||||
m_DirtyStartIndex = 0;
|
||||
m_DirtyEndIndex = m_Curves.Count - 1;
|
||||
if (m_DirtyEndIndex < 0)
|
||||
m_DirtyStartIndex = -1;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region LateUpdate
|
||||
|
||||
protected virtual void LateUpdate()
|
||||
{
|
||||
if (m_DirtyStartIndex < 0) return;
|
||||
|
||||
int start = m_DirtyStartIndex;
|
||||
|
||||
// 统计脏范围前的顶点/索引数(这些无需重建)
|
||||
int preVertexCount = 0;
|
||||
int preIndexCount = 0;
|
||||
for (int i = 0; i < start; i++)
|
||||
{
|
||||
preVertexCount += m_Curves[i].vertexCount;
|
||||
preIndexCount += m_Curves[i].triangleCount * 3;
|
||||
}
|
||||
|
||||
// 从 start 开始重建所有曲线
|
||||
var tempVerts = new List<UIVertex>();
|
||||
var tempIndices = new List<int>();
|
||||
int vAccum = preVertexCount;
|
||||
int iAccum = preIndexCount;
|
||||
for (int i = start; i < m_Curves.Count; i++)
|
||||
{
|
||||
var entry = m_Curves[i];
|
||||
int vertBefore = tempVerts.Count;
|
||||
int idxBefore = tempIndices.Count;
|
||||
CurveMeshGenerator.GenerateCurve(
|
||||
entry, m_ControlPoints, m_Widths, m_Arrows,
|
||||
tempVerts, tempIndices);
|
||||
var updated = entry;
|
||||
updated.startVertexIndex = vAccum;
|
||||
updated.vertexCount = tempVerts.Count - vertBefore;
|
||||
updated.startIndexIndex = iAccum;
|
||||
updated.triangleCount = (tempIndices.Count - idxBefore) / 3;
|
||||
m_Curves[i] = updated;
|
||||
vAccum += updated.vertexCount;
|
||||
iAccum += updated.triangleCount * 3;
|
||||
}
|
||||
|
||||
// 合并到主缓存
|
||||
int totalVertices = vAccum;
|
||||
int totalIndices = iAccum;
|
||||
|
||||
while (m_MeshVertexCache.Count < totalVertices)
|
||||
m_MeshVertexCache.Add(new UIVertex());
|
||||
while (m_MeshIndexCache.Count < totalIndices)
|
||||
m_MeshIndexCache.Add(0);
|
||||
|
||||
for (int i = 0; i < tempVerts.Count; i++)
|
||||
m_MeshVertexCache[preVertexCount + i] = tempVerts[i];
|
||||
|
||||
for (int i = 0; i < tempIndices.Count; i++)
|
||||
m_MeshIndexCache[preIndexCount + i] = tempIndices[i] + preVertexCount;
|
||||
|
||||
SetVerticesDirty();
|
||||
m_DirtyStartIndex = -1;
|
||||
m_DirtyEndIndex = -1;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region OnPopulateMesh
|
||||
|
||||
protected override void OnPopulateMesh(VertexHelper vh)
|
||||
{
|
||||
vh.Clear();
|
||||
if (m_MeshVertexCache.Count == 0 || m_MeshIndexCache.Count == 0) return;
|
||||
vh.AddUIVertexStream(m_MeshVertexCache, m_MeshIndexCache);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 生命周期
|
||||
|
||||
protected override void OnEnable()
|
||||
{
|
||||
base.OnEnable();
|
||||
RebuildAll();
|
||||
}
|
||||
|
||||
protected override void OnDestroy()
|
||||
{
|
||||
base.OnDestroy();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 905c29aca6f80f64f8da8cf8579868ae
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,333 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using XericLibrary.Runtime.MacroLibrary;
|
||||
|
||||
namespace XericLibrary.Runtime.UIGraph
|
||||
{
|
||||
/// <summary>
|
||||
/// 曲线网格生成器
|
||||
/// 将贝塞尔曲线细分为线段带状三角片 + 生成箭头装饰
|
||||
///
|
||||
/// 核心算法:
|
||||
/// 1. 在 t∈[0,1] 上均匀采样 N+1 个点
|
||||
/// 2. 每个采样点计算位置、切线、宽度、颜色
|
||||
/// 3. 切线转 90° 得切法线,沿切法线位移 halfWidth 得左右边顶点
|
||||
/// 4. 顺序连接左右边顶点形成 ribbon 带状:
|
||||
/// 左[i]──右[i]──左[i+1]──右[i+1]──...
|
||||
/// 每个四边形拆两个三角形
|
||||
/// </summary>
|
||||
public static class CurveMeshGenerator
|
||||
{
|
||||
/// <summary>圆圈箭头多边形边数</summary>
|
||||
private const int CircleArrowSides = 8;
|
||||
|
||||
/// <summary>计算切线时的 epsilon 采样偏移</summary>
|
||||
private const float TangentEpsilon = 0.001f;
|
||||
|
||||
/// <summary>
|
||||
/// 生成单条曲线的网格数据
|
||||
/// </summary>
|
||||
public static void GenerateCurve(
|
||||
in CurveCacheEntry entry,
|
||||
List<Vector2> controlPointsPool,
|
||||
List<float> widthsPool,
|
||||
List<ArrowHeadData> arrowsPool,
|
||||
List<UIVertex> vertexList,
|
||||
List<int> indexList)
|
||||
{
|
||||
int segCount = entry.tessellationSegments;
|
||||
if (segCount < 1) segCount = 4;
|
||||
int sampleCount = segCount + 1; // N+1 个采样点
|
||||
|
||||
int cpStart = entry.controlPointStartIndex;
|
||||
int cpCount = entry.controlPointCount;
|
||||
int wStart = entry.widthStartIndex;
|
||||
|
||||
var ctrlPts = new Vector2[cpCount];
|
||||
var wPts = new float[cpCount];
|
||||
for (int i = 0; i < cpCount; i++)
|
||||
{
|
||||
ctrlPts[i] = controlPointsPool[cpStart + i];
|
||||
wPts[i] = widthsPool[wStart + i];
|
||||
}
|
||||
|
||||
int baseVertex = vertexList.Count;
|
||||
UIVertex vert = new UIVertex
|
||||
{
|
||||
normal = Vector3.back,
|
||||
tangent = new Vector4(1f, 0f, 0f, -1f),
|
||||
uv1 = new Vector2(1f, 0f),
|
||||
};
|
||||
|
||||
// 1. 采样 + 生成左右边顶点
|
||||
for (int s = 0; s < sampleCount; s++)
|
||||
{
|
||||
float t = (float)s / segCount;
|
||||
|
||||
Vector2 pos = MacroCurve.BezierCurve(ctrlPts, t); // 曲线上点
|
||||
float width = MacroCurve.BezierCurve(wPts, t); // 当前宽度
|
||||
Vector2 tangent = ComputeTangent(ctrlPts, t); // 切线方向
|
||||
Vector2 normal = new Vector2(-tangent.y, tangent.x); // 切法线(左转90°)
|
||||
|
||||
MacroCurve.BezierCurve1(entry.startColor, entry.endColor, t, out Color col);
|
||||
Color32 color = col;
|
||||
|
||||
float halfW = width * 0.5f;
|
||||
float uv = (float)s / segCount;
|
||||
|
||||
// 左边顶点
|
||||
vert.position = pos - normal * halfW;
|
||||
vert.color = color;
|
||||
vert.uv0 = new Vector2(0f, uv);
|
||||
vertexList.Add(vert);
|
||||
|
||||
// 右边顶点
|
||||
vert.position = pos + normal * halfW;
|
||||
vert.color = color;
|
||||
vert.uv0 = new Vector2(1f, uv);
|
||||
vertexList.Add(vert);
|
||||
}
|
||||
|
||||
// 2. 连接带状三角形
|
||||
// 左[i]=v[i*2], 右[i]=v[i*2+1]
|
||||
// 四边形 i→i+1: (左i, 右i, 左i+1) + (右i, 右i+1, 左i+1)
|
||||
for (int s = 0; s < segCount; s++)
|
||||
{
|
||||
int bv = baseVertex + s * 2;
|
||||
|
||||
indexList.Add(bv + 0); // 左i
|
||||
indexList.Add(bv + 1); // 右i
|
||||
indexList.Add(bv + 2); // 左i+1
|
||||
|
||||
indexList.Add(bv + 1); // 右i
|
||||
indexList.Add(bv + 3); // 右i+1
|
||||
indexList.Add(bv + 2); // 左i+1
|
||||
}
|
||||
|
||||
// 3. 生成箭头
|
||||
if (entry.arrowCount > 0 && entry.arrowStartIndex >= 0)
|
||||
{
|
||||
for (int a = 0; a < entry.arrowCount; a++)
|
||||
{
|
||||
var arrow = arrowsPool[entry.arrowStartIndex + a];
|
||||
GenerateArrow(arrow, ctrlPts, vertexList, indexList);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 计算曲线上 t 处的切线方向(差分法)
|
||||
/// </summary>
|
||||
private static Vector2 ComputeTangent(Vector2[] ctrlPts, float t)
|
||||
{
|
||||
float t0 = Mathf.Max(0f, t - TangentEpsilon);
|
||||
float t1 = Mathf.Min(1f, t + TangentEpsilon);
|
||||
Vector2 tangent = MacroCurve.BezierCurve(ctrlPts, t1) - MacroCurve.BezierCurve(ctrlPts, t0);
|
||||
if (tangent.sqrMagnitude < float.Epsilon)
|
||||
tangent = Vector2.up;
|
||||
else
|
||||
tangent.Normalize();
|
||||
return tangent;
|
||||
}
|
||||
|
||||
#region 箭头
|
||||
|
||||
private static void GenerateArrow(
|
||||
ArrowHeadData arrow,
|
||||
Vector2[] controlPoints,
|
||||
List<UIVertex> vertexList,
|
||||
List<int> indexList)
|
||||
{
|
||||
float t = Mathf.Clamp01(arrow.progress);
|
||||
Vector2 pos = MacroCurve.BezierCurve(controlPoints, t);
|
||||
Vector2 tangent = ComputeTangent(controlPoints, t);
|
||||
|
||||
if (arrow.reversed)
|
||||
tangent = -tangent;
|
||||
|
||||
// 深度补偿:半长偏移(箭头中心在本地x=0,尖端在+width*0.5,尾部在-width*0.5)
|
||||
pos += tangent * arrow.width * 0.5f * arrow.depthCompensation;
|
||||
|
||||
int baseVert = vertexList.Count;
|
||||
GenerateArrowShape(arrow.shape, pos, tangent, arrow.width, arrow.height, arrow.color,
|
||||
vertexList, indexList, baseVert, t);
|
||||
}
|
||||
|
||||
private static void GenerateArrowShape(
|
||||
ArrowShape shape,
|
||||
Vector2 position,
|
||||
Vector2 tangent,
|
||||
float width,
|
||||
float height,
|
||||
Color32 color,
|
||||
List<UIVertex> vertexList,
|
||||
List<int> indexList,
|
||||
int baseVertex,
|
||||
float progressT)
|
||||
{
|
||||
float cos = tangent.x;
|
||||
float sin = tangent.y;
|
||||
|
||||
Vector2 TransformPoint(float ux, float uy)
|
||||
{
|
||||
float x = ux * width;
|
||||
float y = uy * height;
|
||||
float rx = x * cos - y * sin;
|
||||
float ry = x * sin + y * cos;
|
||||
return position + new Vector2(rx, ry);
|
||||
}
|
||||
|
||||
UIVertex vert = new UIVertex
|
||||
{
|
||||
color = color,
|
||||
normal = Vector3.back,
|
||||
tangent = new Vector4(1f, 0f, 0f, -1f),
|
||||
uv0 = new Vector2(0.5f, progressT),
|
||||
uv1 = new Vector2(0f, 1f)
|
||||
};
|
||||
|
||||
switch (shape)
|
||||
{
|
||||
case ArrowShape.Triangle:
|
||||
{
|
||||
vert.position = TransformPoint(0.5f, 0f);
|
||||
vertexList.Add(vert);
|
||||
vert.position = TransformPoint(-0.5f, -0.3f);
|
||||
vertexList.Add(vert);
|
||||
vert.position = TransformPoint(-0.5f, 0.3f);
|
||||
vertexList.Add(vert);
|
||||
|
||||
indexList.Add(baseVertex + 0);
|
||||
indexList.Add(baseVertex + 1);
|
||||
indexList.Add(baseVertex + 2);
|
||||
break;
|
||||
}
|
||||
|
||||
case ArrowShape.HollowTriangle:
|
||||
{
|
||||
float tailOff = -0.1f;
|
||||
|
||||
// 外三角
|
||||
vert.position = TransformPoint(0.5f, 0f);
|
||||
vertexList.Add(vert);
|
||||
vert.position = TransformPoint(-0.5f, -0.3f);
|
||||
vertexList.Add(vert);
|
||||
vert.position = TransformPoint(-0.5f, 0.3f);
|
||||
vertexList.Add(vert);
|
||||
|
||||
// 内三角(缩小 + 向尾部偏移)
|
||||
vert.position = TransformPoint(0.5f * 0.5f + tailOff, 0f);
|
||||
vertexList.Add(vert);
|
||||
vert.position = TransformPoint(-0.5f * 0.5f + tailOff, -0.3f * 0.5f * 0.6f);
|
||||
vertexList.Add(vert);
|
||||
vert.position = TransformPoint(-0.5f * 0.5f + tailOff, 0.3f * 0.5f * 0.6f);
|
||||
vertexList.Add(vert);
|
||||
|
||||
indexList.Add(baseVertex + 1); indexList.Add(baseVertex + 4); indexList.Add(baseVertex + 5);
|
||||
indexList.Add(baseVertex + 1); indexList.Add(baseVertex + 5); indexList.Add(baseVertex + 2);
|
||||
indexList.Add(baseVertex + 0); indexList.Add(baseVertex + 1); indexList.Add(baseVertex + 4);
|
||||
indexList.Add(baseVertex + 0); indexList.Add(baseVertex + 4); indexList.Add(baseVertex + 3);
|
||||
indexList.Add(baseVertex + 0); indexList.Add(baseVertex + 3); indexList.Add(baseVertex + 5);
|
||||
indexList.Add(baseVertex + 0); indexList.Add(baseVertex + 5); indexList.Add(baseVertex + 2);
|
||||
break;
|
||||
}
|
||||
|
||||
case ArrowShape.Arrow:
|
||||
{
|
||||
float headBaseU = 0.5f - 0.35f; // 头部基座 x=0.15
|
||||
float shaftEndU = -0.8f; // 杆尾
|
||||
|
||||
vert.position = TransformPoint(0.5f, 0f);
|
||||
vertexList.Add(vert); // 0: 尖端
|
||||
vert.position = TransformPoint(headBaseU, -0.25f);
|
||||
vertexList.Add(vert); // 1: 左翼
|
||||
vert.position = TransformPoint(headBaseU, 0.25f);
|
||||
vertexList.Add(vert); // 2: 右翼
|
||||
vert.position = TransformPoint(headBaseU, -0.06f);
|
||||
vertexList.Add(vert); // 3: 杆起点下
|
||||
vert.position = TransformPoint(headBaseU, 0.06f);
|
||||
vertexList.Add(vert); // 4: 杆起点上
|
||||
vert.position = TransformPoint(shaftEndU, -0.06f);
|
||||
vertexList.Add(vert); // 5: 杆尾下
|
||||
vert.position = TransformPoint(shaftEndU, 0.06f);
|
||||
vertexList.Add(vert); // 6: 杆尾上
|
||||
|
||||
indexList.Add(baseVertex + 0); indexList.Add(baseVertex + 1); indexList.Add(baseVertex + 2);
|
||||
indexList.Add(baseVertex + 2); indexList.Add(baseVertex + 4); indexList.Add(baseVertex + 6);
|
||||
indexList.Add(baseVertex + 2); indexList.Add(baseVertex + 6); indexList.Add(baseVertex + 1);
|
||||
indexList.Add(baseVertex + 1); indexList.Add(baseVertex + 6); indexList.Add(baseVertex + 5);
|
||||
indexList.Add(baseVertex + 1); indexList.Add(baseVertex + 5); indexList.Add(baseVertex + 3);
|
||||
break;
|
||||
}
|
||||
|
||||
case ArrowShape.Circle:
|
||||
{
|
||||
float r = 0.4f;
|
||||
vert.position = TransformPoint(0f, 0f);
|
||||
vertexList.Add(vert);
|
||||
for (int i = 0; i < CircleArrowSides; i++)
|
||||
{
|
||||
float angle = i * Mathf.PI * 2f / CircleArrowSides;
|
||||
vert.position = TransformPoint(Mathf.Cos(angle) * r, Mathf.Sin(angle) * r);
|
||||
vertexList.Add(vert);
|
||||
}
|
||||
for (int i = 0; i < CircleArrowSides; i++)
|
||||
{
|
||||
int next = (i + 1) % CircleArrowSides;
|
||||
indexList.Add(baseVertex + 0);
|
||||
indexList.Add(baseVertex + 1 + i);
|
||||
indexList.Add(baseVertex + 1 + next);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case ArrowShape.Square:
|
||||
{
|
||||
float hs = 0.35f;
|
||||
vert.position = TransformPoint(-hs, -hs);
|
||||
vertexList.Add(vert);
|
||||
vert.position = TransformPoint(hs, -hs);
|
||||
vertexList.Add(vert);
|
||||
vert.position = TransformPoint(hs, hs);
|
||||
vertexList.Add(vert);
|
||||
vert.position = TransformPoint(-hs, hs);
|
||||
vertexList.Add(vert);
|
||||
indexList.Add(baseVertex + 0); indexList.Add(baseVertex + 1); indexList.Add(baseVertex + 2);
|
||||
indexList.Add(baseVertex + 0); indexList.Add(baseVertex + 2); indexList.Add(baseVertex + 3);
|
||||
break;
|
||||
}
|
||||
|
||||
case ArrowShape.ReverseArrow:
|
||||
{
|
||||
float rHeadBaseU = 0.5f - 0.35f;
|
||||
float rShaftEndU = -0.8f;
|
||||
|
||||
vert.position = TransformPoint(0.5f, 0f);
|
||||
vertexList.Add(vert);
|
||||
vert.position = TransformPoint(rHeadBaseU, -0.25f);
|
||||
vertexList.Add(vert);
|
||||
vert.position = TransformPoint(rHeadBaseU, 0.25f);
|
||||
vertexList.Add(vert);
|
||||
vert.position = TransformPoint(rHeadBaseU, -0.06f);
|
||||
vertexList.Add(vert);
|
||||
vert.position = TransformPoint(rHeadBaseU, 0.06f);
|
||||
vertexList.Add(vert);
|
||||
vert.position = TransformPoint(rShaftEndU, -0.06f);
|
||||
vertexList.Add(vert);
|
||||
vert.position = TransformPoint(rShaftEndU, 0.06f);
|
||||
vertexList.Add(vert);
|
||||
|
||||
indexList.Add(baseVertex + 0); indexList.Add(baseVertex + 1); indexList.Add(baseVertex + 2);
|
||||
indexList.Add(baseVertex + 2); indexList.Add(baseVertex + 4); indexList.Add(baseVertex + 6);
|
||||
indexList.Add(baseVertex + 2); indexList.Add(baseVertex + 6); indexList.Add(baseVertex + 1);
|
||||
indexList.Add(baseVertex + 1); indexList.Add(baseVertex + 6); indexList.Add(baseVertex + 5);
|
||||
indexList.Add(baseVertex + 1); indexList.Add(baseVertex + 5); indexList.Add(baseVertex + 3);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3b0e0346140ad4847a65df78853dd3dd
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,86 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace XericLibrary.Runtime.UIGraph
|
||||
{
|
||||
/// <summary>
|
||||
/// 箭头形状枚举
|
||||
/// </summary>
|
||||
public enum ArrowShape : byte
|
||||
{
|
||||
/// <summary>实心三角箭头</summary>
|
||||
Triangle,
|
||||
/// <summary>空心三角箭头</summary>
|
||||
HollowTriangle,
|
||||
/// <summary>箭矢箭头(翼+杆)</summary>
|
||||
Arrow,
|
||||
/// <summary>圆形头</summary>
|
||||
Circle,
|
||||
/// <summary>方形头</summary>
|
||||
Square,
|
||||
/// <summary>反向箭头(箭尾)</summary>
|
||||
ReverseArrow,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 箭头装饰数据
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public struct ArrowHeadData
|
||||
{
|
||||
/// <summary>箭头形状</summary>
|
||||
public ArrowShape shape;
|
||||
/// <summary>是否反向</summary>
|
||||
public bool reversed;
|
||||
/// <summary>箭头出现在曲线上的位置 (0~1)</summary>
|
||||
[Range(0f, 1f)]
|
||||
public float progress;
|
||||
/// <summary>箭头宽度(沿切线方向)</summary>
|
||||
public float width;
|
||||
/// <summary>箭头高度(垂直切线方向)</summary>
|
||||
public float height;
|
||||
/// <summary>深度补偿:-1~1,箭头沿切线偏移 width*0.5*depth 距离。
|
||||
/// -1:尾部对准曲线点(头部向前伸)
|
||||
/// 0:中心对准曲线点
|
||||
/// +1:头部对准曲线点(尾部向后拉)</summary>
|
||||
[Range(-1f, 1f)]
|
||||
public float depthCompensation;
|
||||
/// <summary>箭头颜色</summary>
|
||||
public Color32 color;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 曲线缓存条目
|
||||
/// 控制点、宽度、箭头数据统一存放在外部扁平列表中,
|
||||
/// 本结构体只记录起始索引和计数。
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public struct CurveCacheEntry
|
||||
{
|
||||
/// <summary>在网格顶点缓存中的起始索引(由 Base 类回填)</summary>
|
||||
public int startVertexIndex;
|
||||
/// <summary>该曲线占用的顶点数(由 Base 类回填)</summary>
|
||||
public int vertexCount;
|
||||
/// <summary>在网格索引缓存中的起始索引(由 Base 类回填)</summary>
|
||||
public int startIndexIndex;
|
||||
/// <summary>三角形数量(由 Base 类回填)</summary>
|
||||
public int triangleCount;
|
||||
|
||||
/// <summary>控制点数量(≥4,默认三阶贝塞尔)</summary>
|
||||
public int controlPointCount;
|
||||
/// <summary>在统一控制点缓存中的起始索引</summary>
|
||||
public int controlPointStartIndex;
|
||||
/// <summary>在统一宽度缓存中的起始索引</summary>
|
||||
public int widthStartIndex;
|
||||
/// <summary>起始颜色</summary>
|
||||
public Color32 startColor;
|
||||
/// <summary>结束颜色</summary>
|
||||
public Color32 endColor;
|
||||
/// <summary>在统一箭头缓存中的起始索引(-1 表示无箭头)</summary>
|
||||
public int arrowStartIndex;
|
||||
/// <summary>箭头数量</summary>
|
||||
public int arrowCount;
|
||||
/// <summary>曲线细分段数</summary>
|
||||
public int tessellationSegments;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f6ce7575b6b9e6a45bdba82ae35018e8
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,274 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
#if ENABLE_BURST
|
||||
using Unity.Burst;
|
||||
using Unity.Jobs;
|
||||
using Unity.Collections;
|
||||
#endif
|
||||
|
||||
namespace XericLibrary.Runtime.UIGraph
|
||||
{
|
||||
/// <summary>
|
||||
/// 线段→模型顶点生成器
|
||||
/// 核心算法:每段线生成 5 个模型顶点 + 3 个三角形
|
||||
/// </summary>
|
||||
public static class LineMeshGenerator
|
||||
{
|
||||
/// <summary>
|
||||
/// 每段线的模型顶点数
|
||||
/// </summary>
|
||||
public const int VerticesPerSegment = 5;
|
||||
|
||||
/// <summary>
|
||||
/// 每段线的三角形索引数(3 个三角形 × 3 索引)
|
||||
/// </summary>
|
||||
public const int IndicesPerSegment = 9;
|
||||
|
||||
/// <summary>
|
||||
/// 计算单段线段对应的 5 个模型顶点(串行 & Job 共用)
|
||||
/// </summary>
|
||||
/// <param name="input">线段生成参数</param>
|
||||
/// <param name="outputVertices">输出顶点数组,须预留 VerticesPerSegment 个位置</param>
|
||||
/// <param name="outputStart">写入 outputVertices 的起始偏移</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void GenerateSingleSegment(in SegmentJobInput input,
|
||||
UIVertex[] outputVertices, int outputStart = 0)
|
||||
{
|
||||
Vector2 startPos = input.start.position;
|
||||
Vector2 endPos = input.end.position;
|
||||
Vector2 dir = endPos - startPos;
|
||||
float length = dir.magnitude;
|
||||
if (length < float.Epsilon)
|
||||
{
|
||||
// 退化为零长度线段,生成空顶点
|
||||
for (int i = 0; i < VerticesPerSegment; i++)
|
||||
{
|
||||
outputVertices[outputStart + i] = new UIVertex
|
||||
{
|
||||
position = startPos,
|
||||
color = Color.clear,
|
||||
uv0 = Vector2.zero,
|
||||
uv1 = Vector2.zero,
|
||||
uv2 = Vector4.zero,
|
||||
uv3 = Vector4.zero
|
||||
};
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
Vector2 normal = dir / length;
|
||||
Vector2 subTangent = Vector2.Perpendicular(normal);
|
||||
|
||||
// 起点和终点的实际宽度(两端可不同)
|
||||
float halfW0 = input.start.thickness * 0.5f;
|
||||
float halfW1 = input.end.thickness * 0.5f;
|
||||
|
||||
// 5 顶点布局
|
||||
// V0 = start + normal * -halfW (左下)
|
||||
// V1 = start + normal + halfW (左上)
|
||||
// V2 = end + normal * -halfW (右下)
|
||||
// V3 = end + normal + halfW (右上)
|
||||
// V4 = end (中心 → 转角连接)
|
||||
Vector3 v0 = startPos - subTangent * halfW0;
|
||||
Vector3 v1 = startPos + subTangent * halfW0;
|
||||
Vector3 v2 = endPos - subTangent * halfW1;
|
||||
Vector3 v3 = endPos + subTangent * halfW1;
|
||||
Vector3 v4 = endPos;
|
||||
|
||||
Color32 c0 = Color32.Lerp(input.start.color, input.end.color, 0f);
|
||||
Color32 c1 = c0;
|
||||
Color32 c2 = Color32.Lerp(input.start.color, input.end.color, 1f);
|
||||
Color32 c3 = c2;
|
||||
Color32 c4 = c2;
|
||||
|
||||
float uvSX = 0f;
|
||||
float uvEX = 1f;
|
||||
float uvSY = input.startLineUV;
|
||||
float uvEY = input.endLineUV;
|
||||
|
||||
UIVertex vert = new UIVertex
|
||||
{
|
||||
normal = Vector3.back,
|
||||
tangent = new Vector4(1f, 0f, 0f, -1f)
|
||||
};
|
||||
|
||||
Vector4 userDataStart = input.start.userData;
|
||||
Vector4 userDataEnd = input.end.userData;
|
||||
|
||||
// V0
|
||||
vert.position = v0;
|
||||
vert.color = c0;
|
||||
vert.uv0 = new Vector2(uvSX, uvSY);
|
||||
vert.uv1 = new Vector2(0f, uvSY); // 整线UV (u=0 表示线的起点侧)
|
||||
vert.uv2 = userDataStart;
|
||||
vert.uv3 = Vector4.zero;
|
||||
outputVertices[outputStart + 0] = vert;
|
||||
|
||||
// V1
|
||||
vert.position = v1;
|
||||
vert.color = c1;
|
||||
vert.uv0 = new Vector2(uvEX, uvSY);
|
||||
vert.uv1 = new Vector2(1f, uvSY); // 整线UV (u=1 表示线的终点侧)
|
||||
vert.uv2 = userDataStart;
|
||||
vert.uv3 = Vector4.zero;
|
||||
outputVertices[outputStart + 1] = vert;
|
||||
|
||||
// V2
|
||||
vert.position = v2;
|
||||
vert.color = c2;
|
||||
vert.uv0 = new Vector2(uvSX, uvEY);
|
||||
vert.uv1 = new Vector2(0f, uvEY);
|
||||
vert.uv2 = userDataEnd;
|
||||
vert.uv3 = Vector4.zero;
|
||||
outputVertices[outputStart + 2] = vert;
|
||||
|
||||
// V3
|
||||
vert.position = v3;
|
||||
vert.color = c3;
|
||||
vert.uv0 = new Vector2(uvEX, uvEY);
|
||||
vert.uv1 = new Vector2(1f, uvEY);
|
||||
vert.uv2 = userDataEnd;
|
||||
vert.uv3 = Vector4.zero;
|
||||
outputVertices[outputStart + 3] = vert;
|
||||
|
||||
// V4
|
||||
vert.position = v4;
|
||||
vert.color = c4;
|
||||
vert.uv0 = new Vector2(0.5f, uvEY);
|
||||
vert.uv1 = new Vector2(0.5f, uvEY);
|
||||
vert.uv2 = userDataEnd;
|
||||
vert.uv3 = Vector4.zero;
|
||||
outputVertices[outputStart + 4] = vert;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 生成单段线段的三角形索引(写入 indexList)
|
||||
/// </summary>
|
||||
/// <param name="segmentIndex">子段全局索引</param>
|
||||
/// <param name="hasPrevInLine">上一子段是否属于同一条线(控制转角连接三角形)</param>
|
||||
/// <param name="indexList">输出的三角形索引列表</param>
|
||||
public static void GenerateSegmentIndices(int segmentIndex, bool hasPrevInLine, List<int> indexList)
|
||||
{
|
||||
int baseVert = segmentIndex * VerticesPerSegment;
|
||||
|
||||
// 三角形 1: V0-V1-V3 (矩形上半)
|
||||
indexList.Add(baseVert + 0);
|
||||
indexList.Add(baseVert + 1);
|
||||
indexList.Add(baseVert + 3);
|
||||
|
||||
// 三角形 2: V3-V2-V0 (矩形下半)
|
||||
indexList.Add(baseVert + 3);
|
||||
indexList.Add(baseVert + 2);
|
||||
indexList.Add(baseVert + 0);
|
||||
|
||||
// 三角形 3: 转角连接三角形
|
||||
// 仅在「同一折线上的相邻子段」之间绘制
|
||||
// 连接上一段的 V4(上一段终点中心)到当前段的 V0, V1
|
||||
if (hasPrevInLine)
|
||||
{
|
||||
int prevV4 = (segmentIndex - 1) * VerticesPerSegment + 4;
|
||||
indexList.Add(prevV4);
|
||||
indexList.Add(baseVert + 0);
|
||||
indexList.Add(baseVert + 1);
|
||||
}
|
||||
}
|
||||
|
||||
#region Jobs 模式
|
||||
|
||||
#if ENABLE_BURST
|
||||
/// <summary>
|
||||
/// Burst 编译的 IJobParallelFor:并行生成各段线的模型顶点
|
||||
/// </summary>
|
||||
[BurstCompile]
|
||||
public struct GenerateVerticesJob : IJobParallelFor
|
||||
{
|
||||
[ReadOnly] public NativeArray<SegmentJobInput> Inputs;
|
||||
[WriteOnly] public NativeArray<UIVertex> OutputVertices;
|
||||
|
||||
public void Execute(int index)
|
||||
{
|
||||
int start = index * VerticesPerSegment;
|
||||
var input = Inputs[index];
|
||||
Vector2 startPos = input.start.position;
|
||||
Vector2 endPos = input.end.position;
|
||||
Vector2 dir = endPos - startPos;
|
||||
float length = dir.magnitude;
|
||||
if (length < float.Epsilon)
|
||||
{
|
||||
for (int i = 0; i < VerticesPerSegment; i++)
|
||||
OutputVertices[start + i] = new UIVertex();
|
||||
return;
|
||||
}
|
||||
|
||||
Vector2 normal = dir / length;
|
||||
Vector2 subTangent = new Vector2(-normal.y, normal.x);
|
||||
|
||||
float halfW0 = input.start.thickness * 0.5f;
|
||||
float halfW1 = input.end.thickness * 0.5f;
|
||||
|
||||
Vector3 v0 = startPos - subTangent * halfW0;
|
||||
Vector3 v1 = startPos + subTangent * halfW0;
|
||||
Vector3 v2 = endPos - subTangent * halfW1;
|
||||
Vector3 v3 = endPos + subTangent * halfW1;
|
||||
Vector3 v4 = endPos;
|
||||
|
||||
Color32 c0 = Color32.Lerp(input.start.color, input.end.color, 0f);
|
||||
Color32 c2 = Color32.Lerp(input.start.color, input.end.color, 1f);
|
||||
|
||||
float uvSY = input.startLineUV;
|
||||
float uvEY = input.endLineUV;
|
||||
Vector4 userDataStart = input.start.userData;
|
||||
Vector4 userDataEnd = input.end.userData;
|
||||
|
||||
UIVertex vert = new UIVertex
|
||||
{
|
||||
normal = Vector3.back,
|
||||
tangent = new Vector4(1f, 0f, 0f, -1f)
|
||||
};
|
||||
|
||||
vert.position = v0; vert.color = c0;
|
||||
vert.uv0 = new Vector2(0f, uvSY); vert.uv1 = new Vector2(0f, uvSY); vert.uv2 = userDataStart;
|
||||
OutputVertices[start + 0] = vert;
|
||||
|
||||
vert.position = v1; vert.color = c0;
|
||||
vert.uv0 = new Vector2(1f, uvSY); vert.uv1 = new Vector2(1f, uvSY); vert.uv2 = userDataStart;
|
||||
OutputVertices[start + 1] = vert;
|
||||
|
||||
vert.position = v2; vert.color = c2;
|
||||
vert.uv0 = new Vector2(0f, uvEY); vert.uv1 = new Vector2(0f, uvEY); vert.uv2 = userDataEnd;
|
||||
OutputVertices[start + 2] = vert;
|
||||
|
||||
vert.position = v3; vert.color = c2;
|
||||
vert.uv0 = new Vector2(1f, uvEY); vert.uv1 = new Vector2(1f, uvEY); vert.uv2 = userDataEnd;
|
||||
OutputVertices[start + 3] = vert;
|
||||
|
||||
vert.position = v4; vert.color = c2;
|
||||
vert.uv0 = new Vector2(0.5f, uvEY); vert.uv1 = new Vector2(0.5f, uvEY); vert.uv2 = userDataEnd;
|
||||
OutputVertices[start + 4] = vert;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 使用 Jobs 并行生成线段顶点
|
||||
/// </summary>
|
||||
public static JobHandle GenerateSegmentsJobs(
|
||||
NativeArray<SegmentJobInput> inputs,
|
||||
NativeArray<UIVertex> outputVertices,
|
||||
JobHandle dependsOn = default)
|
||||
{
|
||||
var job = new GenerateVerticesJob
|
||||
{
|
||||
Inputs = inputs,
|
||||
OutputVertices = outputVertices
|
||||
};
|
||||
return job.Schedule(inputs.Length, 1, dependsOn);
|
||||
}
|
||||
#endif
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 24c8f213d27953c419e17ce974bbf594
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,58 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace XericLibrary.Runtime.UIGraph
|
||||
{
|
||||
/// <summary>
|
||||
/// 线段端点数据(所有字段需为 blittable 类型以支持 NativeArray/Job)
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public struct LineVertexData
|
||||
{
|
||||
public Vector2 position; // 端点位置(局部坐标)
|
||||
public Color32 color; // 端点颜色
|
||||
public float thickness; // 端点宽度
|
||||
public Vector4 userData; // 自定义扩展数据 → 映射到 UV2
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// UV1 均分模式(每条线段独立)
|
||||
/// </summary>
|
||||
public enum UVMode : byte
|
||||
{
|
||||
/// <summary>按实际线路距离均分</summary>
|
||||
ByDistance,
|
||||
/// <summary>按线段数量均分</summary>
|
||||
BySegment,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 线段范围记录
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public struct LineSegmentRange
|
||||
{
|
||||
public int startVertexIndex; // 在 vertexData 中的起始索引
|
||||
public int vertexCount; // 该线段端点数量(>= 2)
|
||||
public UVMode uvMode; // UV1 均分模式
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Job 输入:单段线段的顶点生成参数
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public struct SegmentJobInput
|
||||
{
|
||||
public LineVertexData start;
|
||||
public LineVertexData end;
|
||||
public LineVertexData prevEnd; // 上个线段终点(仅 hasPrev=1 时有效)
|
||||
public LineVertexData nextStart; // 下个线段起点(仅 hasNext=1 时有效)
|
||||
public byte hasPrev; // 0=无效, 1=有效
|
||||
public byte hasNext; // 0=无效, 1=有效
|
||||
public int segmentIndex;
|
||||
public int totalSegmentCount;
|
||||
public float startLineUV;
|
||||
public float endLineUV;
|
||||
public UVMode uvMode;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ba184a89add023d42b93dbc715acc1c2
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,229 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace XericLibrary.Runtime.UIGraph
|
||||
{
|
||||
/// <summary>
|
||||
/// 图元缓存渲染器基类
|
||||
/// 管理 PrimitiveCacheEntry 列表 + 脏标记范围更新
|
||||
/// LateUpdate 中只重建脏标记范围内的图元
|
||||
/// </summary>
|
||||
[RequireComponent(typeof(CanvasRenderer))]
|
||||
public abstract class PrimitiveCacheRendererBase : MaskableGraphic
|
||||
{
|
||||
#region 默认材质
|
||||
|
||||
private static Material s_DefaultMaterial;
|
||||
private const string DefaultShaderPath = "XericLibrary/UIGraph/UIPattern";
|
||||
|
||||
/// <summary>
|
||||
/// 获取图元渲染器的默认材质(基于 UIPattern.shader)
|
||||
/// </summary>
|
||||
public override Material defaultMaterial
|
||||
{
|
||||
get
|
||||
{
|
||||
if (s_DefaultMaterial == null)
|
||||
{
|
||||
var shader = Shader.Find(DefaultShaderPath);
|
||||
if (shader != null)
|
||||
s_DefaultMaterial = new Material(shader);
|
||||
else
|
||||
s_DefaultMaterial = base.defaultMaterial;
|
||||
}
|
||||
return s_DefaultMaterial;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 数据缓存
|
||||
|
||||
/// <summary>
|
||||
/// 图元列表
|
||||
/// </summary>
|
||||
protected List<PrimitiveCacheEntry> m_Primitives = new List<PrimitiveCacheEntry>();
|
||||
|
||||
/// <summary>
|
||||
/// 模型顶点缓存
|
||||
/// </summary>
|
||||
protected List<UIVertex> m_MeshVertexCache = new List<UIVertex>();
|
||||
|
||||
/// <summary>
|
||||
/// 三角形索引缓存
|
||||
/// </summary>
|
||||
protected List<int> m_MeshIndexCache = new List<int>();
|
||||
|
||||
/// <summary>
|
||||
/// 脏标记范围起点(图元索引),-1 表示无脏标记
|
||||
/// </summary>
|
||||
protected int m_DirtyStartIndex = -1;
|
||||
|
||||
/// <summary>
|
||||
/// 脏标记范围终点(图元索引)
|
||||
/// </summary>
|
||||
protected int m_DirtyEndIndex = -1;
|
||||
|
||||
#endregion
|
||||
|
||||
#region API
|
||||
|
||||
/// <summary>
|
||||
/// 添加一个图元
|
||||
/// </summary>
|
||||
/// <returns>图元索引</returns>
|
||||
public int AddPrimitive(PrimitiveCacheEntry entry)
|
||||
{
|
||||
int index = m_Primitives.Count;
|
||||
m_Primitives.Add(entry);
|
||||
MarkDirty(index);
|
||||
return index;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 移除指定图元
|
||||
/// </summary>
|
||||
public void RemovePrimitive(int index)
|
||||
{
|
||||
if (index < 0 || index >= m_Primitives.Count) return;
|
||||
m_Primitives.RemoveAt(index);
|
||||
RebuildAll();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 标记指定图元为脏
|
||||
/// </summary>
|
||||
public void SetPrimitiveDirty(int index)
|
||||
{
|
||||
MarkDirty(index);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 清空所有图元
|
||||
/// </summary>
|
||||
public void ClearAll()
|
||||
{
|
||||
m_Primitives.Clear();
|
||||
m_MeshVertexCache.Clear();
|
||||
m_MeshIndexCache.Clear();
|
||||
m_DirtyStartIndex = -1;
|
||||
m_DirtyEndIndex = -1;
|
||||
SetVerticesDirty();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 脏标记
|
||||
|
||||
protected void MarkDirty(int index)
|
||||
{
|
||||
if (m_DirtyStartIndex < 0 || index < m_DirtyStartIndex)
|
||||
m_DirtyStartIndex = index;
|
||||
if (m_DirtyEndIndex < 0 || index > m_DirtyEndIndex)
|
||||
m_DirtyEndIndex = index;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 标记所有图元为脏(下次 LateUpdate 全量重绘)
|
||||
/// 适用于:更新顶点坐标后,不改变元素数量的重绘
|
||||
/// </summary>
|
||||
public void RebuildAll()
|
||||
{
|
||||
m_DirtyStartIndex = 0;
|
||||
m_DirtyEndIndex = m_Primitives.Count - 1;
|
||||
if (m_DirtyEndIndex < 0)
|
||||
m_DirtyStartIndex = -1;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region LateUpdate
|
||||
|
||||
protected virtual void LateUpdate()
|
||||
{
|
||||
if (m_DirtyStartIndex < 0) return;
|
||||
|
||||
// 全量重建策略:从脏范围起点开始,重绘该条目及后续所有条目
|
||||
// 保证所有条目 startVertexIndex/startIndexIndex 始终正确
|
||||
int start = m_DirtyStartIndex;
|
||||
|
||||
// 统计脏范围前的顶点/索引数(这些条目无需重建)
|
||||
int preVertexCount = 0;
|
||||
int preIndexCount = 0;
|
||||
for (int i = 0; i < start; i++)
|
||||
{
|
||||
preVertexCount += m_Primitives[i].vertexCount;
|
||||
preIndexCount += m_Primitives[i].triangleCount * 3;
|
||||
}
|
||||
|
||||
// 从 start 开始重建所有条目
|
||||
var tempVerts = new List<UIVertex>();
|
||||
var tempIndices = new List<int>();
|
||||
int vAccum = preVertexCount;
|
||||
int iAccum = preIndexCount;
|
||||
for (int i = start; i < m_Primitives.Count; i++)
|
||||
{
|
||||
var entry = m_Primitives[i];
|
||||
int vertBefore = tempVerts.Count;
|
||||
int idxBefore = tempIndices.Count;
|
||||
PrimitiveMeshGenerator.GeneratePrimitive(entry, tempVerts, tempIndices);
|
||||
var updated = entry;
|
||||
updated.startVertexIndex = vAccum;
|
||||
updated.vertexCount = tempVerts.Count - vertBefore;
|
||||
updated.startIndexIndex = iAccum;
|
||||
updated.triangleCount = (tempIndices.Count - idxBefore) / 3;
|
||||
m_Primitives[i] = updated;
|
||||
vAccum += updated.vertexCount;
|
||||
iAccum += updated.triangleCount * 3;
|
||||
}
|
||||
|
||||
// 合并到主缓存
|
||||
int totalVertices = vAccum;
|
||||
int totalIndices = iAccum;
|
||||
|
||||
while (m_MeshVertexCache.Count < totalVertices)
|
||||
m_MeshVertexCache.Add(new UIVertex());
|
||||
while (m_MeshIndexCache.Count < totalIndices)
|
||||
m_MeshIndexCache.Add(0);
|
||||
|
||||
for (int i = 0; i < tempVerts.Count; i++)
|
||||
m_MeshVertexCache[preVertexCount + i] = tempVerts[i];
|
||||
|
||||
for (int i = 0; i < tempIndices.Count; i++)
|
||||
m_MeshIndexCache[preIndexCount + i] = tempIndices[i] + preVertexCount;
|
||||
|
||||
SetVerticesDirty();
|
||||
m_DirtyStartIndex = -1;
|
||||
m_DirtyEndIndex = -1;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region OnPopulateMesh
|
||||
|
||||
protected override void OnPopulateMesh(VertexHelper vh)
|
||||
{
|
||||
vh.Clear();
|
||||
if (m_MeshVertexCache.Count == 0 || m_MeshIndexCache.Count == 0) return;
|
||||
vh.AddUIVertexStream(m_MeshVertexCache, m_MeshIndexCache);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 生命周期
|
||||
|
||||
protected override void OnEnable()
|
||||
{
|
||||
base.OnEnable();
|
||||
RebuildAll();
|
||||
}
|
||||
|
||||
protected override void OnDestroy()
|
||||
{
|
||||
base.OnDestroy();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 28047425f50534b46a5ad5b7a924bd30
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,390 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using XericLibrary.Runtime.MacroLibrary;
|
||||
|
||||
namespace XericLibrary.Runtime.UIGraph
|
||||
{
|
||||
/// <summary>
|
||||
/// 图元网格生成器
|
||||
/// 为多边形/矩形生成 triangle-fan 网格,支持倒角
|
||||
/// </summary>
|
||||
public static class PrimitiveMeshGenerator
|
||||
{
|
||||
/// <summary>
|
||||
/// 生成单个图元的网格顶点和索引
|
||||
/// </summary>
|
||||
public static void GeneratePrimitive(
|
||||
in PrimitiveCacheEntry entry,
|
||||
List<UIVertex> vertexList,
|
||||
List<int> indexList)
|
||||
{
|
||||
// 将归一化 chamferSize [0,1] 映射为实际像素倒角尺寸
|
||||
float minDim = Mathf.Min(entry.size.x, entry.size.y);
|
||||
float scaledChamfer = entry.@params.chamferSize * minDim * 0.5f;
|
||||
|
||||
PrimitiveParamHelper helper = new PrimitiveParamHelper
|
||||
{
|
||||
center = entry.center,
|
||||
size = entry.size,
|
||||
sizeMode = entry.sizeMode,
|
||||
type = entry.type,
|
||||
sideCount = (int)entry.@params.sideCount,
|
||||
chamferSize = scaledChamfer,
|
||||
chamferSegments = entry.@params.chamferSegments,
|
||||
faceCount = 0
|
||||
};
|
||||
|
||||
// 1. 计算外围顶点
|
||||
var corners = ComputeCorners(helper);
|
||||
if (corners.Count < 3) return;
|
||||
|
||||
// 应用角度旋转
|
||||
if (Mathf.Abs(entry.angle) > 0.001f)
|
||||
{
|
||||
float rad = entry.angle * Mathf.Deg2Rad;
|
||||
float cos = Mathf.Cos(rad);
|
||||
float sin = Mathf.Sin(rad);
|
||||
for (int i = 0; i < corners.Count; i++)
|
||||
{
|
||||
Vector2 rel = corners[i] - entry.center;
|
||||
corners[i] = entry.center + new Vector2(
|
||||
rel.x * cos - rel.y * sin,
|
||||
rel.x * sin + rel.y * cos);
|
||||
}
|
||||
}
|
||||
|
||||
int baseVertex = vertexList.Count;
|
||||
int typeIndex = (int)entry.type;
|
||||
|
||||
// 颜色打包到法线/副法线
|
||||
// normal: (bg.r/255, bg.g/255, bg.b/255)
|
||||
// tangent: (border.r/255, border.g/255, border.b/255, border.a/255)
|
||||
Color32 bg = entry.@params.bgColor;
|
||||
Vector3 normalCol = new Vector3(bg.r / 255f, bg.g / 255f, bg.b / 255f);
|
||||
Color32 bc = entry.@params.borderColor;
|
||||
Vector4 tangentCol = new Vector4(bc.r / 255f, bc.g / 255f, bc.b / 255f, bc.a / 255f);
|
||||
|
||||
// 2. 添加中心点
|
||||
UIVertex centerVert = new UIVertex
|
||||
{
|
||||
position = (Vector3)entry.center,
|
||||
color = entry.@params.centerColor,
|
||||
normal = normalCol,
|
||||
tangent = tangentCol,
|
||||
uv0 = new Vector2(0.5f, 0.5f),
|
||||
uv1 = new Vector2(0f, 0f),
|
||||
};
|
||||
vertexList.Add(centerVert);
|
||||
|
||||
// 3. 添加外围顶点 + 创建三角形扇
|
||||
for (int i = 0; i < corners.Count; i++)
|
||||
{
|
||||
Vector2 pos = corners[i];
|
||||
float dist = Vector2.Distance(pos, entry.center);
|
||||
float maxDist = Mathf.Max(entry.size.x, entry.size.y) * 0.5f;
|
||||
float normalizedDist = maxDist > 0.001f ? Mathf.Clamp01(dist / maxDist) : 0f;
|
||||
|
||||
UIVertex vert = new UIVertex
|
||||
{
|
||||
position = (Vector3)pos,
|
||||
color = entry.@params.centerColor,
|
||||
normal = normalCol,
|
||||
tangent = tangentCol,
|
||||
uv0 = new Vector2(
|
||||
(pos.x - entry.center.x) / entry.size.x + 0.5f,
|
||||
(pos.y - entry.center.y) / entry.size.y + 0.5f),
|
||||
uv1 = new Vector2(normalizedDist, 0f),
|
||||
};
|
||||
vertexList.Add(vert);
|
||||
|
||||
// 三角形扇: (中心, i, i+1)
|
||||
if (i > 0)
|
||||
{
|
||||
indexList.Add(baseVertex); // 中心
|
||||
indexList.Add(baseVertex + i); // 当前角
|
||||
indexList.Add(baseVertex + i + 1); // 下一个角
|
||||
}
|
||||
}
|
||||
|
||||
// 闭合:最后一个角 → 第一个角
|
||||
if (corners.Count > 2)
|
||||
{
|
||||
indexList.Add(baseVertex);
|
||||
indexList.Add(baseVertex + corners.Count);
|
||||
indexList.Add(baseVertex + 1);
|
||||
}
|
||||
|
||||
// 4. 生成物理边框 — 复制外围顶点并向外偏移一圈,条带拼接
|
||||
float borderThickness = entry.@params.borderThickness;
|
||||
if (borderThickness > 0.001f && corners.Count >= 3)
|
||||
{
|
||||
int n = corners.Count;
|
||||
int borderBase = vertexList.Count;
|
||||
|
||||
// 计算每个顶点的向外偏移方向(从中心指向顶点的方向)
|
||||
var outDirs = new Vector2[n];
|
||||
for (int i = 0; i < n; i++)
|
||||
{
|
||||
Vector2 dir = corners[i] - entry.center;
|
||||
outDirs[i] = dir.sqrMagnitude > 0.001f ? dir.normalized : Vector2.up;
|
||||
}
|
||||
|
||||
// 添加内圈(原始位置)+ 外圈(偏移位置),isBorder=1
|
||||
for (int i = 0; i < n; i++)
|
||||
{
|
||||
Vector2 pos = corners[i];
|
||||
Vector2 outPos = pos + outDirs[i] * borderThickness;
|
||||
|
||||
// 内圈顶点 (borderProgress=0)
|
||||
UIVertex inner = new UIVertex
|
||||
{
|
||||
position = (Vector3)pos,
|
||||
color = entry.@params.borderColor,
|
||||
normal = normalCol,
|
||||
tangent = tangentCol,
|
||||
uv0 = new Vector2(
|
||||
(pos.x - entry.center.x) / entry.size.x + 0.5f,
|
||||
(pos.y - entry.center.y) / entry.size.y + 0.5f),
|
||||
uv1 = new Vector2(0f, 1f),
|
||||
};
|
||||
vertexList.Add(inner);
|
||||
|
||||
// 外圈顶点 (borderProgress=1)
|
||||
UIVertex outer = new UIVertex
|
||||
{
|
||||
position = (Vector3)outPos,
|
||||
color = entry.@params.borderColor,
|
||||
normal = normalCol,
|
||||
tangent = tangentCol,
|
||||
uv0 = new Vector2(
|
||||
(outPos.x - entry.center.x) / entry.size.x + 0.5f,
|
||||
(outPos.y - entry.center.y) / entry.size.y + 0.5f),
|
||||
uv1 = new Vector2(1f, 1f),
|
||||
};
|
||||
vertexList.Add(outer);
|
||||
}
|
||||
|
||||
// 连接条带三角形
|
||||
for (int i = 0; i < n; i++)
|
||||
{
|
||||
int next = (i + 1) % n;
|
||||
int iI = borderBase + i * 2; // 内圈 i
|
||||
int oI = borderBase + i * 2 + 1; // 外圈 i
|
||||
int iN = borderBase + next * 2; // 内圈 next
|
||||
int oN = borderBase + next * 2 + 1; // 外圈 next
|
||||
|
||||
indexList.Add(iI);
|
||||
indexList.Add(oI);
|
||||
indexList.Add(iN);
|
||||
|
||||
indexList.Add(oI);
|
||||
indexList.Add(oN);
|
||||
indexList.Add(iN);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 计算图元的外围顶点列表
|
||||
/// </summary>
|
||||
private static List<Vector2> ComputeCorners(in PrimitiveParamHelper helper)
|
||||
{
|
||||
if (helper.type == PrimitiveType.Polygon)
|
||||
return ComputePolygonCorners(helper);
|
||||
else
|
||||
return ComputeRectangleCorners(helper);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 计算正多边形的外围顶点
|
||||
/// </summary>
|
||||
private static List<Vector2> ComputePolygonCorners(in PrimitiveParamHelper helper)
|
||||
{
|
||||
int sides = Mathf.Max(3, helper.sideCount);
|
||||
float angleStep = 360f / sides;
|
||||
float startAngle = 90f;
|
||||
|
||||
// 计算半径
|
||||
float minSize = Mathf.Min(helper.size.x, helper.size.y);
|
||||
float radius = minSize * 0.5f;
|
||||
|
||||
if (helper.sizeMode == SizeMode.CircumscribedCircle)
|
||||
{
|
||||
// 外切圆:半径延伸到各顶点
|
||||
// 外切圆半径 = minSize/2
|
||||
// 实际半径需要让多边形的边与外切圆相切
|
||||
// 对于外切圆,多边形的顶点到中心的距离 = r / cos(π/N)
|
||||
float apothemAngle = Mathf.PI / sides;
|
||||
radius = radius / Mathf.Cos(apothemAngle);
|
||||
}
|
||||
// InscribedCircle 保持 radius = minSize/2
|
||||
|
||||
// 计算基本多边形顶点
|
||||
var corners = new List<Vector2>(sides);
|
||||
for (int i = 0; i < sides; i++)
|
||||
{
|
||||
float angleDeg = startAngle + i * angleStep;
|
||||
float angle = angleDeg * Mathf.Deg2Rad;
|
||||
corners.Add(new Vector2(
|
||||
helper.center.x + Mathf.Cos(angle) * radius,
|
||||
helper.center.y + Mathf.Sin(angle) * radius));
|
||||
}
|
||||
|
||||
// 应用 XY 缩放
|
||||
for (int i = 0; i < corners.Count; i++)
|
||||
{
|
||||
Vector2 dir = corners[i] - helper.center;
|
||||
corners[i] = helper.center + new Vector2(
|
||||
dir.x * helper.size.x / minSize,
|
||||
dir.y * helper.size.y / minSize);
|
||||
}
|
||||
|
||||
// 处理倒角
|
||||
if (helper.chamferSize > 0.001f)
|
||||
corners = ApplyChamfer(corners, helper.chamferSize, helper.chamferSegments);
|
||||
|
||||
return corners;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 计算矩形的四个角
|
||||
/// </summary>
|
||||
private static List<Vector2> ComputeRectangleCorners(in PrimitiveParamHelper helper)
|
||||
{
|
||||
float halfX, halfY;
|
||||
|
||||
switch (helper.sizeMode)
|
||||
{
|
||||
case SizeMode.InscribedCircle:
|
||||
{
|
||||
float minSize = Mathf.Min(helper.size.x, helper.size.y);
|
||||
halfX = minSize * 0.5f;
|
||||
halfY = minSize * 0.5f;
|
||||
break;
|
||||
}
|
||||
case SizeMode.CircumscribedCircle:
|
||||
{
|
||||
float minSize = Mathf.Min(helper.size.x, helper.size.y);
|
||||
float radius = minSize * 0.5f;
|
||||
halfX = radius;
|
||||
halfY = radius;
|
||||
break;
|
||||
}
|
||||
case SizeMode.InscribedEllipse:
|
||||
{
|
||||
halfX = helper.size.x * 0.5f;
|
||||
halfY = helper.size.y * 0.5f;
|
||||
break;
|
||||
}
|
||||
case SizeMode.CircumscribedRatio:
|
||||
{
|
||||
float minSize = Mathf.Min(helper.size.x, helper.size.y);
|
||||
float radius = minSize * 0.5f;
|
||||
halfX = radius * (helper.size.x / minSize);
|
||||
halfY = radius * (helper.size.y / minSize);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
halfX = helper.size.x * 0.5f;
|
||||
halfY = helper.size.y * 0.5f;
|
||||
break;
|
||||
}
|
||||
|
||||
var corners = new List<Vector2>(4)
|
||||
{
|
||||
helper.center + new Vector2(-halfX, -halfY),
|
||||
helper.center + new Vector2( halfX, -halfY),
|
||||
helper.center + new Vector2( halfX, halfY),
|
||||
helper.center + new Vector2(-halfX, halfY),
|
||||
};
|
||||
|
||||
if (helper.chamferSize > 0.001f)
|
||||
corners = ApplyChamfer(corners, helper.chamferSize, helper.chamferSegments);
|
||||
|
||||
return corners;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 对多边形角应用倒角(Bezier 曲线圆角)
|
||||
/// </summary>
|
||||
private static List<Vector2> ApplyChamfer(
|
||||
List<Vector2> corners, float chamferSize, int segments)
|
||||
{
|
||||
int n = corners.Count;
|
||||
// 计算每条边的长度,取最短边的一半作为倒角上限
|
||||
float minEdgeHalf = float.MaxValue;
|
||||
for (int i = 0; i < n; i++)
|
||||
{
|
||||
int next = (i + 1) % n;
|
||||
float edgeLen = Vector2.Distance(corners[i], corners[next]);
|
||||
minEdgeHalf = Mathf.Min(minEdgeHalf, edgeLen * 0.5f);
|
||||
}
|
||||
float clampedChamfer = Mathf.Min(chamferSize, minEdgeHalf);
|
||||
if (clampedChamfer < 0.001f) return corners;
|
||||
|
||||
var result = new List<Vector2>();
|
||||
for (int i = 0; i < n; i++)
|
||||
{
|
||||
int prev = (i - 1 + n) % n;
|
||||
int curr = i;
|
||||
int next = (i + 1) % n;
|
||||
|
||||
Vector2 pCurr = corners[curr];
|
||||
Vector2 pPrev = corners[prev];
|
||||
Vector2 pNext = corners[next];
|
||||
|
||||
float edgeLenIn = Vector2.Distance(pPrev, pCurr);
|
||||
float edgeLenOut = Vector2.Distance(pCurr, pNext);
|
||||
float clampIn = Mathf.Min(clampedChamfer, edgeLenIn * 0.5f);
|
||||
float clampOut = Mathf.Min(clampedChamfer, edgeLenOut * 0.5f);
|
||||
|
||||
// p0 在入边 (prev→curr) 上,距角 clampIn
|
||||
// p3 在出边 (curr→next) 上,距角 clampOut
|
||||
Vector2 dirFromCurrToPrev = (pPrev - pCurr).normalized;
|
||||
Vector2 dirFromCurrToNext = (pNext - pCurr).normalized;
|
||||
|
||||
Vector2 p0 = pCurr + dirFromCurrToPrev * clampIn;
|
||||
Vector2 p3 = pCurr + dirFromCurrToNext * clampOut;
|
||||
|
||||
// 三次 Bezier 控制点:向角的方向内收,使曲线从 p0 平滑过渡到 p3
|
||||
// 控制点位于 p0→pCorner 和 p3→pCorner 之间
|
||||
float c = 0.5522847498f; // 四分之一圆的 Bezier 近似常数
|
||||
float bezierR = Mathf.Min(clampIn, clampOut);
|
||||
Vector2 p1 = p0 + (pCurr - p0).normalized * bezierR * c;
|
||||
Vector2 p2 = p3 + (pCurr - p3).normalized * bezierR * c;
|
||||
|
||||
// 添加 p0(每条边的起点)
|
||||
result.Add(p0);
|
||||
|
||||
// 插值 Bezier 曲线上的中间点
|
||||
for (int j = 1; j < segments; j++)
|
||||
{
|
||||
float t = (float)j / segments;
|
||||
MacroCurve.BezierCurve3(p0, p1, p2, p3, t, out Vector2 pos);
|
||||
result.Add(pos);
|
||||
}
|
||||
|
||||
// 添加 p3(每条边的终点,也是下条边的起点)
|
||||
result.Add(p3);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 内部辅助结构(避免在 ComputeCorners 中传递过多参数)
|
||||
/// </summary>
|
||||
private struct PrimitiveParamHelper
|
||||
{
|
||||
public Vector2 center;
|
||||
public Vector2 size;
|
||||
public SizeMode sizeMode;
|
||||
public PrimitiveType type;
|
||||
public int sideCount;
|
||||
public float chamferSize;
|
||||
public int chamferSegments;
|
||||
public int faceCount; // 输出:实际生成的三角形数
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9ae63ff723ba749448c841d5afe21ac2
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,80 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace XericLibrary.Runtime.UIGraph
|
||||
{
|
||||
/// <summary>
|
||||
/// 图元类型
|
||||
/// </summary>
|
||||
public enum PrimitiveType : byte
|
||||
{
|
||||
/// <summary>正多边形(内切圆/外切圆)</summary>
|
||||
Polygon,
|
||||
/// <summary>矩形(内切椭圆/外切圆比例)</summary>
|
||||
Rectangle,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 尺寸模式
|
||||
/// </summary>
|
||||
public enum SizeMode : byte
|
||||
{
|
||||
/// <summary>内切圆 — 半径延伸到各边中点</summary>
|
||||
InscribedCircle,
|
||||
/// <summary>外切圆 — 半径延伸到各顶点</summary>
|
||||
CircumscribedCircle,
|
||||
/// <summary>内切椭圆 — XY各自独立的内切半径</summary>
|
||||
InscribedEllipse,
|
||||
/// <summary>外切圆比例 — 外切圆半径 + XY比例</summary>
|
||||
CircumscribedRatio,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 图元参数
|
||||
/// 顶点通道分配:
|
||||
/// uv0: 面片内归一化坐标 (0~1)
|
||||
/// uv1: (centerDist, isBorder)
|
||||
/// normal: (bg.r, bg.g, bg.b) — 0~1
|
||||
/// tangent: (border.r, border.g, border.b, border.a) — 0~1
|
||||
/// </summary>
|
||||
public struct PrimitiveParams
|
||||
{
|
||||
public float axisScaleX;
|
||||
public float axisScaleY;
|
||||
public float sideCount;
|
||||
public float chamferSize;
|
||||
/// <summary>倒角细分段数(仅网格生成使用)</summary>
|
||||
public int chamferSegments;
|
||||
public Color32 bgColor;
|
||||
public Color32 centerColor;
|
||||
public Color32 borderColor;
|
||||
public float borderThickness;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 图元缓存条目
|
||||
/// </summary>
|
||||
public struct PrimitiveCacheEntry
|
||||
{
|
||||
/// <summary>在网格顶点缓存中的起始索引</summary>
|
||||
public int startVertexIndex;
|
||||
/// <summary>该图元占用的顶点数</summary>
|
||||
public int vertexCount;
|
||||
/// <summary>在网格索引缓存中的起始索引</summary>
|
||||
public int startIndexIndex;
|
||||
/// <summary>三角形数量</summary>
|
||||
public int triangleCount;
|
||||
|
||||
public PrimitiveType type;
|
||||
public SizeMode sizeMode;
|
||||
|
||||
/// <summary>图元中心(局部坐标)</summary>
|
||||
public Vector2 center;
|
||||
/// <summary>图元尺寸</summary>
|
||||
public Vector2 size;
|
||||
/// <summary>旋转角度(度数,0 = 正上方)</summary>
|
||||
public float angle;
|
||||
|
||||
/// <summary>图元参数</summary>
|
||||
public PrimitiveParams @params;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b233b049f5be67e47a89d1f9bee867b2
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,121 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
#endif
|
||||
|
||||
namespace XericLibrary.Runtime.UIGraph
|
||||
{
|
||||
/// <summary>
|
||||
/// 曲线绘制器组件
|
||||
/// 支持多阶贝塞尔曲线,可配置控制点、宽度渐变、颜色和箭头装饰
|
||||
/// </summary>
|
||||
[AddComponentMenu("Xeric Library/UI/UICurveRenderer", 16)]
|
||||
public class UICurveRenderer : CurveCacheRendererBase
|
||||
{
|
||||
/// <summary>控制点,(x,y)=位置, z=宽度</summary>
|
||||
public Vector3[] controlPoints = new Vector3[]
|
||||
{
|
||||
new Vector3(-150f, 0f, 10f),
|
||||
new Vector3(-50f, 100f, 10f),
|
||||
new Vector3(50f, -100f, 10f),
|
||||
new Vector3(150f, 0f, 10f),
|
||||
};
|
||||
|
||||
public Color32 startColor = Color.white;
|
||||
public Color32 endColor = Color.white;
|
||||
|
||||
[Range(4, 128)]
|
||||
public int tessellationSegments = 32;
|
||||
|
||||
public List<ArrowHeadData> arrows = new List<ArrowHeadData>();
|
||||
|
||||
public bool autoRebuild = true;
|
||||
|
||||
private CurveCacheEntry? m_CurrentEntry;
|
||||
|
||||
protected override void OnEnable()
|
||||
{
|
||||
base.OnEnable();
|
||||
if (autoRebuild) RebuildCurve();
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
protected override void OnValidate()
|
||||
{
|
||||
base.OnValidate();
|
||||
if (controlPoints == null || controlPoints.Length < 2)
|
||||
controlPoints = new Vector3[] { new Vector3(0f, 0f, 10f), new Vector3(100f, 0f, 10f) };
|
||||
if (tessellationSegments < 4) tessellationSegments = 4;
|
||||
if (autoRebuild && isActiveAndEnabled)
|
||||
{
|
||||
EditorApplication.delayCall += () =>
|
||||
{
|
||||
if (this != null && isActiveAndEnabled)
|
||||
RebuildCurve();
|
||||
};
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// 重建曲线
|
||||
/// </summary>
|
||||
public void RebuildCurve()
|
||||
{
|
||||
ClearAll();
|
||||
|
||||
if (controlPoints == null || controlPoints.Length < 2) return;
|
||||
|
||||
// 从 Vector3[] 中提取 Vector2[] 位置和 float[] 宽度
|
||||
int count = controlPoints.Length;
|
||||
var ctrlPts2 = new Vector2[count];
|
||||
var widths = new float[count];
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
ctrlPts2[i] = (Vector2)controlPoints[i];
|
||||
widths[i] = controlPoints[i].z;
|
||||
}
|
||||
|
||||
var entry = new CurveCacheEntry
|
||||
{
|
||||
startColor = startColor,
|
||||
endColor = endColor,
|
||||
tessellationSegments = tessellationSegments,
|
||||
};
|
||||
|
||||
AddCurve(entry, ctrlPts2, widths, arrows?.ToArray());
|
||||
m_CurrentEntry = m_Curves.Count > 0 ? m_Curves[0] : (CurveCacheEntry?)null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置控制点(z=宽度)
|
||||
/// </summary>
|
||||
public void SetControlPoints(Vector3[] points)
|
||||
{
|
||||
controlPoints = points;
|
||||
RebuildCurve();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 添加箭头
|
||||
/// </summary>
|
||||
public void AddArrow(ArrowHeadData arrow)
|
||||
{
|
||||
if (arrows == null) arrows = new List<ArrowHeadData>();
|
||||
arrows.Add(arrow);
|
||||
RebuildCurve();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 移除箭头
|
||||
/// </summary>
|
||||
public void RemoveArrow(int index)
|
||||
{
|
||||
if (arrows == null || index < 0 || index >= arrows.Count) return;
|
||||
arrows.RemoveAt(index);
|
||||
RebuildCurve();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3189cafe7a5628048b2243986703e3e6
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,115 @@
|
||||
using UnityEngine;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
#endif
|
||||
|
||||
namespace XericLibrary.Runtime.UIGraph
|
||||
{
|
||||
/// <summary>
|
||||
/// 新一代线段渲染器组件
|
||||
/// 基于 CacheLineRendererBase 的缓存 + Jobs 架构
|
||||
/// </summary>
|
||||
[AddComponentMenu("Xeric Library/UI/UILineRenderer V2", 15)]
|
||||
public class UILineRendererV2 : CacheLineRendererBase
|
||||
{
|
||||
[Header("全局默认设置")]
|
||||
[Range(0.01f, 200f)]
|
||||
[Tooltip("默认线段宽度")]
|
||||
public float defaultThickness = 10f;
|
||||
|
||||
[Tooltip("默认线段颜色")]
|
||||
public Color defaultColor = Color.white;
|
||||
|
||||
[Tooltip("默认 UV1 均分模式")]
|
||||
public UVMode defaultUVMode = UVMode.ByDistance;
|
||||
|
||||
/// <summary>
|
||||
/// 绘制一条简单线段(2个端点)
|
||||
/// </summary>
|
||||
public void DrawLine(Vector2 from, Vector2 to)
|
||||
{
|
||||
DrawLine(from, to, defaultColor, defaultThickness, defaultUVMode);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 绘制一条简单线段(指定颜色和宽度)
|
||||
/// </summary>
|
||||
public void DrawLine(Vector2 from, Vector2 to, Color color, float thickness, UVMode uvMode = UVMode.ByDistance)
|
||||
{
|
||||
BeginLine(uvMode);
|
||||
AddLineNode(from, color, thickness);
|
||||
AddLineNode(to, color, thickness);
|
||||
EndLine();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 绘制折线(多点连接)
|
||||
/// </summary>
|
||||
public void DrawPolyline(Vector2[] points, Color color, float thickness, UVMode uvMode = UVMode.ByDistance)
|
||||
{
|
||||
if (points == null || points.Length < 2) return;
|
||||
|
||||
LineVertexData[] vertices = new LineVertexData[points.Length];
|
||||
for (int i = 0; i < points.Length; i++)
|
||||
{
|
||||
vertices[i] = new LineVertexData
|
||||
{
|
||||
position = points[i],
|
||||
color = color,
|
||||
thickness = thickness,
|
||||
userData = Vector4.zero
|
||||
};
|
||||
}
|
||||
AddLine(vertices, uvMode);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 绘制折线(每个端点独立颜色/宽度)
|
||||
/// </summary>
|
||||
public void DrawPolyline(LineVertexData[] vertices, UVMode uvMode = UVMode.ByDistance)
|
||||
{
|
||||
if (vertices == null || vertices.Length < 2) return;
|
||||
AddLine(vertices, uvMode);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 简单绘制矩形框
|
||||
/// </summary>
|
||||
public void DrawRect(Rect rect, Color color, float thickness)
|
||||
{
|
||||
Vector2 center = rect.center;
|
||||
float hw = rect.width * 0.5f;
|
||||
float hh = rect.height * 0.5f;
|
||||
|
||||
var pts = new Vector2[]
|
||||
{
|
||||
new Vector2(center.x - hw, center.y - hh),
|
||||
new Vector2(center.x + hw, center.y - hh),
|
||||
new Vector2(center.x + hw, center.y + hh),
|
||||
new Vector2(center.x - hw, center.y + hh),
|
||||
new Vector2(center.x - hw, center.y - hh),
|
||||
};
|
||||
DrawPolyline(pts, color, thickness, UVMode.ByDistance);
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
protected override void OnValidate()
|
||||
{
|
||||
base.OnValidate();
|
||||
if (defaultThickness < 0.01f)
|
||||
defaultThickness = 0.01f;
|
||||
}
|
||||
|
||||
[MenuItem("GameObject/Xeric Library/UI/UILineRenderer V2", false, 10)]
|
||||
private static void CreateGameObject()
|
||||
{
|
||||
var go = new GameObject(nameof(UILineRendererV2), typeof(UILineRendererV2));
|
||||
if (Selection.activeGameObject != null)
|
||||
go.transform.SetParent(Selection.activeGameObject.transform);
|
||||
Undo.RegisterCreatedObjectUndo(go, $"Create {nameof(UILineRendererV2)}");
|
||||
Selection.activeObject = go;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d43989c17d4c7d640988acb51713390a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,144 @@
|
||||
using UnityEngine;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
#endif
|
||||
|
||||
namespace XericLibrary.Runtime.UIGraph
|
||||
{
|
||||
/// <summary>
|
||||
/// 图元绘制器组件
|
||||
/// 支持多边形/矩形的内切圆/外切圆/椭圆等尺寸模式
|
||||
/// 参数写入 UV2-UV7 通道
|
||||
/// </summary>
|
||||
[AddComponentMenu("Xeric Library/UI/UIPrimitiveRenderer", 15)]
|
||||
public class UIPrimitiveRenderer : PrimitiveCacheRendererBase
|
||||
{
|
||||
public PrimitiveType primitiveType = PrimitiveType.Polygon;
|
||||
|
||||
public SizeMode sizeMode = SizeMode.InscribedCircle;
|
||||
public Vector2 size = new Vector2(100f, 100f);
|
||||
public Vector2 centerOffset = Vector2.zero;
|
||||
[Range(-180f, 180f)]
|
||||
public float angle = 0f;
|
||||
|
||||
[Range(3, 64)]
|
||||
public int sideCount = 6;
|
||||
|
||||
[Range(0f, 1f)]
|
||||
public float chamferSize = 0f;
|
||||
|
||||
[Range(1, 16)]
|
||||
public int chamferSegments = 4;
|
||||
|
||||
public Color bgColor = Color.gray;
|
||||
public Color centerColor = Color.white;
|
||||
public Color borderColor = Color.black;
|
||||
|
||||
[Range(0f, 50f)]
|
||||
public float borderThickness = 2f;
|
||||
|
||||
public bool autoRebuild = true;
|
||||
|
||||
private PrimitiveCacheEntry? m_CurrentEntry;
|
||||
|
||||
protected override void OnEnable()
|
||||
{
|
||||
base.OnEnable();
|
||||
if (autoRebuild) RebuildPrimitive();
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
protected override void OnValidate()
|
||||
{
|
||||
base.OnValidate();
|
||||
if (sideCount < 3) sideCount = 3;
|
||||
if (autoRebuild && isActiveAndEnabled)
|
||||
{
|
||||
// 使用 EditorApplication.delayCall 避免 OnValidate 中的 SetVerticesDirty 警告
|
||||
EditorApplication.delayCall += () =>
|
||||
{
|
||||
if (this != null && isActiveAndEnabled)
|
||||
RebuildPrimitive();
|
||||
};
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// 重建图元
|
||||
/// </summary>
|
||||
public void RebuildPrimitive()
|
||||
{
|
||||
// 清除旧的图元
|
||||
ClearAll();
|
||||
|
||||
var entry = new PrimitiveCacheEntry
|
||||
{
|
||||
type = primitiveType,
|
||||
sizeMode = sizeMode,
|
||||
center = centerOffset,
|
||||
size = size,
|
||||
angle = angle,
|
||||
@params = new PrimitiveParams
|
||||
{
|
||||
axisScaleX = 1f,
|
||||
axisScaleY = 1f,
|
||||
sideCount = sideCount,
|
||||
chamferSize = chamferSize,
|
||||
chamferSegments = chamferSegments,
|
||||
bgColor = bgColor,
|
||||
centerColor = centerColor,
|
||||
borderColor = borderColor,
|
||||
borderThickness = borderThickness,
|
||||
}
|
||||
};
|
||||
|
||||
AddPrimitive(entry);
|
||||
m_CurrentEntry = entry;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新图元参数(不触发完全重建,只标记脏)
|
||||
/// </summary>
|
||||
public void UpdateParams(System.Action<PrimitiveCacheEntry> updater)
|
||||
{
|
||||
if (m_Primitives.Count == 0) return;
|
||||
var entry = m_Primitives[0];
|
||||
updater?.Invoke(entry);
|
||||
m_Primitives[0] = entry;
|
||||
SetPrimitiveDirty(0);
|
||||
}
|
||||
|
||||
#region 便捷方法
|
||||
|
||||
/// <summary>
|
||||
/// 快速设置为正多边形
|
||||
/// </summary>
|
||||
public void SetPolygon(int sides, float radius, Color? fill = null, Color? border = null)
|
||||
{
|
||||
primitiveType = PrimitiveType.Polygon;
|
||||
sizeMode = SizeMode.InscribedCircle;
|
||||
size = new Vector2(radius * 2f, radius * 2f);
|
||||
sideCount = Mathf.Max(3, sides);
|
||||
if (fill.HasValue) bgColor = fill.Value;
|
||||
if (border.HasValue) borderColor = border.Value;
|
||||
RebuildPrimitive();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 快速设置为矩形
|
||||
/// </summary>
|
||||
public void SetRectangle(float width, float height, Color? fill = null, Color? border = null)
|
||||
{
|
||||
primitiveType = PrimitiveType.Rectangle;
|
||||
sizeMode = SizeMode.InscribedEllipse;
|
||||
size = new Vector2(width, height);
|
||||
if (fill.HasValue) bgColor = fill.Value;
|
||||
if (border.HasValue) borderColor = border.Value;
|
||||
RebuildPrimitive();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a7f57f5609d9f3a46aa9d50dc1a47904
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,478 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Pool;
|
||||
using UnityEngine.UI;
|
||||
using XericLibrary.Runtime.MacroLibrary;
|
||||
|
||||
namespace XericLibrary.Runtime.UIGraph
|
||||
{
|
||||
/// <summary>
|
||||
/// UILineRenderer 的工具类,提供静态方法用于线条渲染相关的操作
|
||||
/// </summary>
|
||||
internal static class XericRendererUtils
|
||||
{
|
||||
#region 数学计算
|
||||
|
||||
/// <summary>
|
||||
/// 计算线路的总长度
|
||||
/// </summary>
|
||||
public static float CalculateTotalLength(this Vector2[] points, bool cycleLoop = false)
|
||||
{
|
||||
if (points.Length < 2)
|
||||
return 0f;
|
||||
|
||||
var totalLength = 0f;
|
||||
for (int i = 0; i < points.Length - 1; i++)
|
||||
totalLength += Vector2.Distance(points[i], points[i + 1]);
|
||||
|
||||
if (cycleLoop)
|
||||
totalLength += Vector2.Distance(points[0], points[^1]);
|
||||
|
||||
return totalLength;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 计算一个点指向另一个点的角度(以度为单位)
|
||||
/// </summary>
|
||||
public static float RotatePointTowards(Vector2 vertex, Vector2 target)
|
||||
{
|
||||
return (Mathf.Atan2(target.y - vertex.y, target.x - vertex.x) * Mathf.Rad2Deg);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 如果需要居中显示,则偏移到组件中心
|
||||
/// </summary>
|
||||
public static Vector2 SwitchCenterOffset(bool center, Vector2 rectSize) =>
|
||||
center ? Vector2.zero : -(rectSize / 2);
|
||||
|
||||
private static bool GetNormalizedVector(Vector2 startPoint, Vector2 endPoint,
|
||||
out Vector2 vector, out float distance, out Vector2 normal, out Vector2 subTangent,
|
||||
bool reverseVector = false)
|
||||
{
|
||||
vector = reverseVector ? startPoint - endPoint : endPoint - startPoint;
|
||||
distance = vector.magnitude;
|
||||
normal = Vector2.zero;
|
||||
subTangent = Vector2.zero;
|
||||
|
||||
if (distance <= float.Epsilon)
|
||||
return false;
|
||||
|
||||
normal = vector / distance;
|
||||
subTangent = Vector2.Perpendicular(normal);
|
||||
return true;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region uv计算
|
||||
|
||||
private static void GetCurrentLengthUVY(float startLength, float currentLength, float totalLength,
|
||||
out float startUVY, out float endUVY)
|
||||
{
|
||||
startUVY = totalLength > 0 ? startLength / totalLength : 0f;
|
||||
endUVY = totalLength > 0 ? (startLength + currentLength) / totalLength : 0f;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 简易线路绘制 DSL5
|
||||
|
||||
public static List<UIVertex> GetSingleLineSegmentVerts_DSL5(List<UIVertex> vertices, Vector2 startPoint,
|
||||
Vector2 endPoint,
|
||||
float thickness, Color color, Vector2 normal, Vector2 subTangent, float startUVY, float endUVY,
|
||||
Vector3 offset)
|
||||
{
|
||||
if (vertices == null)
|
||||
throw new Exception("没有指定线段片段顶点容器列表");
|
||||
vertices.Clear();
|
||||
UIVertex vertex = UIVertex.simpleVert;
|
||||
vertex.color = color;
|
||||
|
||||
var width = thickness / 2;
|
||||
|
||||
vertex.position = (startPoint + subTangent * width);
|
||||
vertex.position += offset;
|
||||
vertex.uv0 = new Vector2(0f, startUVY);
|
||||
vertices.Add(vertex);
|
||||
|
||||
vertex.position = (startPoint + subTangent * -width);
|
||||
vertex.position += offset;
|
||||
vertex.uv0 = new Vector2(1f, startUVY);
|
||||
vertices.Add(vertex);
|
||||
|
||||
vertex.position = (endPoint + subTangent * width);
|
||||
vertex.position += offset;
|
||||
vertex.uv0 = new Vector2(0f, endUVY);
|
||||
vertices.Add(vertex);
|
||||
|
||||
vertex.position = (endPoint + subTangent * -width);
|
||||
vertex.position += offset;
|
||||
vertex.uv0 = new Vector2(1f, endUVY);
|
||||
vertices.Add(vertex);
|
||||
|
||||
vertex.position = endPoint;
|
||||
vertex.position += offset;
|
||||
vertex.uv0 = new Vector2(0.5f, endUVY);
|
||||
vertices.Add(vertex);
|
||||
return vertices;
|
||||
}
|
||||
|
||||
public static void AddSingleLineSegmentVertsAndTriangle_DSL5(this VertexHelper vh, int segmentPartIndex,
|
||||
IEnumerable<UIVertex> verts)
|
||||
{
|
||||
foreach (var vert in verts)
|
||||
vh.AddVert(vert);
|
||||
int index = segmentPartIndex * 5;
|
||||
|
||||
vh.AddTriangle(index, index + 1, index + 3);
|
||||
vh.AddTriangle(index + 3, index + 2, index);
|
||||
|
||||
if (segmentPartIndex != 0)
|
||||
{
|
||||
vh.AddTriangle(index, index - 1, index - 3);
|
||||
vh.AddTriangle(index + 1, index - 1, index - 2);
|
||||
}
|
||||
}
|
||||
|
||||
public static void PopulateLineByPointsArray_DSL5(this VertexHelper vh, Vector2[] points,
|
||||
float thickness, bool cycleLoop, Color lineColor, Vector3 offset,
|
||||
bool enableArrow,
|
||||
Color arrowColor, Vector2 arrowSize, float pointProgress, float arrowPointProgress,
|
||||
bool absProgressPoint = true, bool reverseDir = false)
|
||||
{
|
||||
if (points.Length < 2)
|
||||
return;
|
||||
|
||||
var totalLength = points.CalculateTotalLength(cycleLoop);
|
||||
var currentLength = 0f;
|
||||
var lineVertices = ListPool<UIVertex>.Get();
|
||||
var arrowVertices = ListPool<UIVertex>.Get();
|
||||
var arrowindices = ListPool<int>.Get();
|
||||
for (int i = 0; i < points.Length - (cycleLoop ? 0 : 1); i++)
|
||||
{
|
||||
var thisPoint = points[i];
|
||||
var nextPoint = points[(i + 1) % points.Length];
|
||||
|
||||
if (!GetNormalizedVector(thisPoint, nextPoint,
|
||||
out var vector, out var distance, out var normal, out var subTangent))
|
||||
continue;
|
||||
GetCurrentLengthUVY(currentLength, distance, totalLength, out var startUvy, out var endUvy);
|
||||
|
||||
vh.AddSingleLineSegmentVertsAndTriangle_DSL5(i,
|
||||
GetSingleLineSegmentVerts_DSL5(lineVertices, thisPoint, nextPoint, thickness, lineColor, normal,
|
||||
subTangent, startUvy, endUvy, offset));
|
||||
|
||||
if (enableArrow)
|
||||
{
|
||||
PopulateTriangleArrowByPointArray(arrowVertices, thisPoint, nextPoint, arrowColor, arrowSize.y,
|
||||
arrowSize.x, pointProgress, arrowPointProgress, absProgressPoint, reverseDir);
|
||||
arrowindices.AddFaceByConsecutiveVertices(arrowVertices, i * 3, 3, i * 3);
|
||||
}
|
||||
currentLength += distance;
|
||||
}
|
||||
|
||||
if (enableArrow)
|
||||
{
|
||||
vh.AddUIVertexStream(arrowVertices, arrowindices);
|
||||
}
|
||||
ListPool<UIVertex>.Release(lineVertices);
|
||||
ListPool<UIVertex>.Release(arrowVertices);
|
||||
ListPool<int>.Release(arrowindices);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 圆弧过度的线路绘制 CFAL
|
||||
|
||||
public static void AddSingleLineSegmentVertsAndTriangle_CFAL(this VertexHelper vh, int segmentPartIndex,
|
||||
IEnumerable<UIVertex> verts)
|
||||
{
|
||||
foreach (var vert in verts)
|
||||
vh.AddVert(vert);
|
||||
int index = segmentPartIndex * 5;
|
||||
|
||||
vh.AddTriangle(index, index + 1, index + 3);
|
||||
vh.AddTriangle(index + 3, index + 2, index);
|
||||
|
||||
if (segmentPartIndex != 0)
|
||||
{
|
||||
vh.AddTriangle(index, index - 1, index - 3);
|
||||
vh.AddTriangle(index + 1, index - 1, index - 2);
|
||||
}
|
||||
}
|
||||
|
||||
public static List<UIVertex> GetSingleLineSegmentVerts_CFAL(List<UIVertex> vertices, Vector2 startPoint,
|
||||
Vector2 endPoint, Vector2 nextPoint, Vector2 prevPoint,
|
||||
float thickness, Color color, float startLength, out float distance, float totalLength, Vector3 offset,
|
||||
int chamferSegments = 0, float innerRadius = 0f)
|
||||
{
|
||||
if (vertices == null)
|
||||
throw new Exception("没有指定线段片段顶点容器列表");
|
||||
vertices.Clear();
|
||||
|
||||
UIVertex vertex = UIVertex.simpleVert;
|
||||
vertex.color = color;
|
||||
|
||||
if (!GetNormalizedVector(startPoint, endPoint,
|
||||
out var vector, out distance, out var normal, out var subTangent))
|
||||
return vertices;
|
||||
|
||||
var width = thickness / 2;
|
||||
|
||||
GetCurrentLengthUVY(startLength, distance, totalLength, out var startUVY, out var endUVY);
|
||||
|
||||
float startAngle = 0f;
|
||||
float endAngle = 0f;
|
||||
|
||||
if (prevPoint != startPoint)
|
||||
{
|
||||
var prevVector = startPoint - prevPoint;
|
||||
var prevNormal = prevVector.normalized;
|
||||
startAngle = Vector2.Angle(normal, -prevNormal);
|
||||
}
|
||||
|
||||
if (nextPoint != endPoint)
|
||||
{
|
||||
var nextVector = nextPoint - endPoint;
|
||||
var nextNormal = nextVector.normalized;
|
||||
endAngle = Vector2.Angle(-normal, nextNormal);
|
||||
}
|
||||
|
||||
float startOffset = 0f;
|
||||
float endOffset = 0f;
|
||||
|
||||
if (Mathf.Abs(startAngle) > 0.1f)
|
||||
{
|
||||
if (innerRadius > 0f)
|
||||
{
|
||||
startOffset = (width + innerRadius) / Mathf.Sin(startAngle * Mathf.Deg2Rad / 2) - innerRadius;
|
||||
}
|
||||
else
|
||||
{
|
||||
startOffset = width / Mathf.Sin(startAngle * Mathf.Deg2Rad / 2);
|
||||
}
|
||||
}
|
||||
|
||||
if (Mathf.Abs(endAngle) > 0.1f)
|
||||
{
|
||||
if (innerRadius > 0f)
|
||||
{
|
||||
endOffset = (width + innerRadius) / Mathf.Sin(endAngle * Mathf.Deg2Rad / 2) - innerRadius;
|
||||
}
|
||||
else
|
||||
{
|
||||
endOffset = width / Mathf.Sin(endAngle * Mathf.Deg2Rad / 2);
|
||||
}
|
||||
}
|
||||
|
||||
vertex.position = (startPoint + subTangent * width + normal * startOffset);
|
||||
vertex.position += offset;
|
||||
vertex.uv0 = new Vector2(0f, startUVY);
|
||||
vertices.Add(vertex);
|
||||
|
||||
vertex.position = (startPoint + subTangent * -width + normal * startOffset);
|
||||
vertex.position += offset;
|
||||
vertex.uv0 = new Vector2(1f, startUVY);
|
||||
vertices.Add(vertex);
|
||||
|
||||
vertex.position = (endPoint + subTangent * width + normal * -endOffset);
|
||||
vertex.position += offset;
|
||||
vertex.uv0 = new Vector2(0f, endUVY);
|
||||
vertices.Add(vertex);
|
||||
|
||||
vertex.position = (endPoint + subTangent * -width + normal * -endOffset);
|
||||
vertex.position += offset;
|
||||
vertex.uv0 = new Vector2(1f, endUVY);
|
||||
vertices.Add(vertex);
|
||||
|
||||
vertex.position = (endPoint + normal * -endOffset);
|
||||
vertex.position += offset;
|
||||
vertex.uv0 = new Vector2(0.5f, endUVY);
|
||||
vertices.Add(vertex);
|
||||
|
||||
return vertices;
|
||||
}
|
||||
|
||||
public static void PopulateLineByPointsArray_CFAL(this VertexHelper vh, Vector2[] points, float thickness,
|
||||
bool cycleLoop, Color color, Vector3 offset,
|
||||
int chamferSegments = 0, float innerRadius = 0f)
|
||||
{
|
||||
if (points.Length < 2)
|
||||
return;
|
||||
|
||||
var totalLength = points.CalculateTotalLength(cycleLoop);
|
||||
var currentLength = 0f;
|
||||
var vertices = ListPool<UIVertex>.Get();
|
||||
|
||||
for (int i = 0; i < points.Length - (cycleLoop ? 0 : 1); i++)
|
||||
{
|
||||
var startPoint = points[i];
|
||||
var endPoint = points[(i + 1) % points.Length];
|
||||
var prevPoint = i > 0 ? points[i - 1] : cycleLoop ? points[^1] : startPoint;
|
||||
var nextPoint = points[(i + 2) % points.Length];
|
||||
|
||||
vh.AddSingleLineSegmentVertsAndTriangle_CFAL(i,
|
||||
GetSingleLineSegmentVerts_CFAL(vertices, startPoint, endPoint, nextPoint, prevPoint,
|
||||
thickness, color, currentLength, out var distance, totalLength, offset,
|
||||
chamferSegments, innerRadius));
|
||||
currentLength += distance;
|
||||
}
|
||||
|
||||
ListPool<UIVertex>.Release(vertices);
|
||||
}
|
||||
|
||||
public static void CreateLineSegment_CFAL(this VertexHelper vh, Vector2 startPoint, Vector2 endPoint,
|
||||
float angle0, float angle3, float thickness, bool center, Vector2 rectSize, Color color, float startLength,
|
||||
float totalLength)
|
||||
{
|
||||
Vector2 offset = center ? (rectSize / 2) : Vector2.zero;
|
||||
|
||||
UIVertex vertex = UIVertex.simpleVert;
|
||||
vertex.color = color;
|
||||
|
||||
if (!GetNormalizedVector(startPoint, endPoint,
|
||||
out var vector, out var distance, out var normal, out var subTangent))
|
||||
return;
|
||||
|
||||
var width = thickness / 2;
|
||||
var pointOffset1 = Mathf.Abs(angle0) <= 0.1f
|
||||
? 0
|
||||
: width / Mathf.Sin(angle0 * Mathf.Deg2Rad) + (angle0 > 90 ? width : 0);
|
||||
var pointOffset2 = Mathf.Abs(angle3) <= 0.1f
|
||||
? 0
|
||||
: width / Mathf.Sin(angle3 * Mathf.Deg2Rad) + (angle3 > 90 ? width : 0);
|
||||
float startUVY = totalLength > 0 ? startLength / totalLength : 0f;
|
||||
float endUVY = totalLength > 0 ? (startLength + distance) / totalLength : 0f;
|
||||
|
||||
vertex.position = (Vector3)(startPoint + subTangent * width + normal * pointOffset1) - (Vector3)offset;
|
||||
vertex.uv0 = new Vector2(0f, startUVY);
|
||||
vh.AddVert(vertex);
|
||||
|
||||
vertex.position = (Vector3)(startPoint + subTangent * -width + normal * pointOffset1) - (Vector3)offset;
|
||||
vertex.uv0 = new Vector2(1f, startUVY);
|
||||
vh.AddVert(vertex);
|
||||
|
||||
vertex.position = (Vector3)(endPoint + subTangent * width + normal * -pointOffset2) - (Vector3)offset;
|
||||
vertex.uv0 = new Vector2(0f, endUVY);
|
||||
vh.AddVert(vertex);
|
||||
|
||||
vertex.position = (Vector3)(endPoint + subTangent * -width + normal * -pointOffset2) - (Vector3)offset;
|
||||
vertex.uv0 = new Vector2(1f, endUVY);
|
||||
vh.AddVert(vertex);
|
||||
|
||||
vertex.position = (Vector3)(endPoint + normal * -pointOffset2) - (Vector3)offset;
|
||||
vertex.uv0 = new Vector2(0.5f, endUVY);
|
||||
vh.AddVert(vertex);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 箭头绘制工具
|
||||
|
||||
public static List<UIVertex> PopulateTriangleArrowByPointArray(List<UIVertex> vertices, Vector2 start,
|
||||
Vector2 end, Color color, float sizeL, float sizeW, float pointProgress, float arrowPointProgress,
|
||||
bool absProgressPoint = true, bool reverseDir = false)
|
||||
{
|
||||
GetNormalizedVector(start, end, out var vector, out var length, out var normal, out var tangent,
|
||||
reverseDir);
|
||||
|
||||
var progressPosition = absProgressPoint
|
||||
? pointProgress * normal
|
||||
: pointProgress * vector;
|
||||
progressPosition += reverseDir ? end : start;
|
||||
progressPosition += arrowPointProgress * sizeL * normal;
|
||||
|
||||
UIVertex vertex = UIVertex.simpleVert;
|
||||
vertex.color = color;
|
||||
|
||||
vertex.position = progressPosition;
|
||||
vertices.Add(vertex);
|
||||
|
||||
vertex.position = progressPosition + -sizeL * normal + sizeW * 0.5f * tangent;
|
||||
vertices.Add(vertex);
|
||||
vertex.position += (Vector3)(-sizeW * tangent);
|
||||
vertices.Add(vertex);
|
||||
|
||||
return vertices;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 顶点工具集
|
||||
|
||||
/// <summary>
|
||||
/// 获取点在矩形中的UV坐标
|
||||
/// </summary>
|
||||
public static void GetUVAtRect(this Vector2 pos, Rect rect, out float u, out float v)
|
||||
{
|
||||
u = (pos.x - rect.xMin) / rect.width;
|
||||
v = (pos.y - rect.yMin) / rect.height;
|
||||
}
|
||||
|
||||
public static void Get(this IList<UIVertex> vertexList, Vector4 uv2)
|
||||
{
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 顶点列表工具
|
||||
|
||||
private static readonly Color32 s_DefaultColor =
|
||||
new Color32(byte.MaxValue, byte.MaxValue, byte.MaxValue, byte.MaxValue);
|
||||
|
||||
private static readonly Vector4 s_DefaultTangent = new Vector4(1f, 0.0f, 0.0f, -1f);
|
||||
|
||||
/// <summary>
|
||||
/// 添加一个顶点信息
|
||||
/// </summary>
|
||||
public static void AddVert(this ICollection<UIVertex> vertexList, Vector3 position, Vector4 uv, Color32 color)
|
||||
{
|
||||
vertexList.Add(new UIVertex()
|
||||
{
|
||||
position = position,
|
||||
normal = Vector3.back,
|
||||
tangent = s_DefaultTangent,
|
||||
color = color,
|
||||
uv0 = uv,
|
||||
uv1 = uv,
|
||||
uv2 = uv,
|
||||
uv3 = uv
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 添加一个三角面索引
|
||||
/// </summary>
|
||||
public static void AddTriangle(this ICollection<int> indices, int index0, int index1, int index2)
|
||||
{
|
||||
indices.Add(index0);
|
||||
indices.Add(index1);
|
||||
indices.Add(index2);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 向顶点管理器添加顶点列表中的顶点构成的面
|
||||
/// </summary>
|
||||
public static void AddFaceByConsecutiveVertices(this ICollection<int> indices, IList<UIVertex> vertexList,
|
||||
int startVertexIndex, int faceVertexRange, int centerVertexIndex)
|
||||
{
|
||||
if (faceVertexRange < 3)
|
||||
throw new IndexOutOfRangeException("无法创建小于3个点的面");
|
||||
var length = startVertexIndex + faceVertexRange - 1;
|
||||
for (int i = startVertexIndex + 1; i < length; i++)
|
||||
{
|
||||
if (i < 0 || i > vertexList.Count)
|
||||
throw new IndexOutOfRangeException(
|
||||
$"在向顶点集添加顶点时,从{startVertexIndex}开始的{faceVertexRange}个顶点(以及{centerVertexIndex})可能超出{vertexList.Count}的范围(预定范围{length}),导致构建超出预期");
|
||||
var next = startVertexIndex + 1;
|
||||
|
||||
if (next >= vertexList.Count)
|
||||
return;
|
||||
indices.AddTriangle(centerVertexIndex, startVertexIndex, startVertexIndex + 1);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3a46796ddebc719488396e15845faa2e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -302,16 +302,16 @@ public static class MacroDXXL
|
||||
|
||||
#region 标注 / 标签 / 文本
|
||||
|
||||
/// <summary>在 3D 位置绘制文本标签</summary>
|
||||
public static void Label(Vector3 position, string text, Color? color = null, float size = 0.1f)
|
||||
/// <summary>在 3D 位置绘制文本标签(rotation 控制朝向,默认 Quaternion.identity 即 xy 平面正向)</summary>
|
||||
public static void Label(Vector3 position, string text, Color? color = null, float size = 0.1f, Quaternion? rotation = null)
|
||||
{
|
||||
DrawText.Write(text, position, color ?? Color.white, size, default(Vector3), default(Vector3));
|
||||
}
|
||||
DrawText.Write(text, position, color ?? Color.white, size, rotation ?? Quaternion.identity);
|
||||
}
|
||||
|
||||
/// <summary>在 3D 位置绘制带框文本标签</summary>
|
||||
public static void LabelFramed(Vector3 position, string text, Color? color = null, float size = 0.1f)
|
||||
/// <summary>在 3D 位置绘制带框文本标签(rotation 控制朝向,默认 Quaternion.identity 即 xy 平面正向)</summary>
|
||||
public static void LabelFramed(Vector3 position, string text, Color? color = null, float size = 0.1f, Quaternion? rotation = null)
|
||||
{
|
||||
DrawText.WriteFramed(text, position, color ?? Color.white, size, default(Vector3), default(Vector3));
|
||||
DrawText.WriteFramed(text, position, color ?? Color.white, size, rotation ?? Quaternion.identity);
|
||||
}
|
||||
|
||||
/// <summary>给 GameObject 绘制屏幕空间标签(始终面向相机)</summary>
|
||||
|
||||
@@ -30,7 +30,6 @@ namespace Deconstruction.Runtime
|
||||
{
|
||||
LayoutRebuilder.ForceRebuildLayoutImmediate(rectTrans);
|
||||
}
|
||||
|
||||
|
||||
private static HashSet<RectTransform> _rebuildSet = new HashSet<RectTransform>();
|
||||
/// <summary>
|
||||
|
||||
Binary file not shown.
@@ -1,33 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 39959342f844a444e83cf0f85dcdda9f
|
||||
PluginImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
iconMap: {}
|
||||
executionOrder: {}
|
||||
defineConstraints: []
|
||||
isPreloaded: 0
|
||||
isOverridable: 1
|
||||
isExplicitlyReferenced: 0
|
||||
validateReferences: 1
|
||||
platformData:
|
||||
- first:
|
||||
Any:
|
||||
second:
|
||||
enabled: 1
|
||||
settings: {}
|
||||
- first:
|
||||
Editor: Editor
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
DefaultValueInitialized: true
|
||||
- first:
|
||||
Windows Store Apps: WindowsStoreApps
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
CPU: AnyCPU
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Binary file not shown.
@@ -1,8 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7758a0679c9038144a4b0d007d44618f
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,8 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f8777f3ce94f12747a9c712730114c5b
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,16 +0,0 @@
|
||||
This package contains third-party software components governed by the license(s) indicated below:
|
||||
---------
|
||||
|
||||
Component Name: [provide component name]
|
||||
|
||||
License Type: [Provide license type, i.e. "MIT", "Apache 2.0"]
|
||||
|
||||
[Provide License Details]
|
||||
|
||||
---------
|
||||
Component Name: [provide component name]
|
||||
|
||||
License Type: [Provide license type, i.e. "MIT", "Apache 2.0"]
|
||||
|
||||
[Provide License Details]
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 63d179caae5c9e049a9a6ed376a54f9c
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+1
-1
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user