update 0.6.5

This commit is contained in:
2026-09-30 08:56:49 +08:00
parent d9f9b2a90f
commit 016aee57ee
30 changed files with 2471 additions and 7 deletions
@@ -0,0 +1,81 @@
using UnityEditor;
using UnityEngine;
using XericLibrary.Runtime.UIGraph;
namespace XericLibraryEditor.XericComponent
{
/// <summary>
/// UISplineCurveRenderer 的 Inspector:在默认字段上面加一条烘焙工具栏。
/// 烘焙系数(采样数、触发时机)本来就是公开字段,这里只补"手动烘一次"和状态读数。
/// </summary>
[CustomEditor(typeof(UISplineCurveRenderer))]
public class UISplineCurveRendererEditor : Editor
{
private UISplineCurveRenderer script;
private void OnEnable()
{
script = (UISplineCurveRenderer)target;
}
public override void OnInspectorGUI()
{
DrawBakeBar();
serializedObject.Update();
DrawDefaultInspector();
serializedObject.ApplyModifiedProperties();
}
/// <summary>
/// 烘焙状态条:读数 + 手动烘焙按钮。
/// 烘焙结果落在 XericRectSpline 的序列化字段里,所以按完要标脏,
/// 否则存不进场景,下次打开还得重烘。
/// </summary>
private void DrawBakeBar()
{
if (script == null)
return;
var path = script.splinePath;
var spline = path != null ? path.xericSpline : null;
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
EditorGUILayout.LabelField("弧长烘焙", EditorStyles.boldLabel);
EditorGUILayout.LabelField("采样点",
spline != null ? spline.BakeCount.ToString() : "未绑定样条");
EditorGUILayout.LabelField("曲线长度",
spline != null ? spline.SplineLength.ToString("0.###") : "-");
EditorGUILayout.LabelField("已烘焙次数", script.BakeRunCount.ToString());
EditorGUILayout.LabelField("校正状态",
script.progressMapping == SplineProgressMapping.BakedDistance
? (spline != null && spline.BakeCount >= script.bakeSubdivide
? "按烘焙距离校正"
: "烘焙数据不够,暂时退回参数映射")
: "未启用(直接按参数)");
using (new EditorGUI.DisabledScope(spline == null))
{
if (GUILayout.Button("烘焙一次", GUILayout.Height(24)))
{
// XericRectSpline 是普通 [Serializable] 类,不是 Object,
// 能登记撤销/标脏的只有承载它的那个路径组件
Undo.RecordObject(path, "烘焙样条");
script.BakeNow();
EditorUtility.SetDirty(path);
EditorUtility.SetDirty(script);
SceneView.RepaintAll();
}
}
if (script.bakeTrigger == SplineBakeTrigger.Manual)
EditorGUILayout.HelpBox(
"触发时机是 Manual:几何怎么改都不会自动烘,进度贴不贴得准就看上面这个按钮按没按。",
MessageType.Info);
EditorGUILayout.EndVertical();
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4cb828489da70e091cbdc53afe9881ec
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
+8
View File
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 3dd3579dda7928297735f22b46528170
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
+212
View File
@@ -0,0 +1,212 @@
using UnityEditor;
using UnityEngine;
using XericLibrary.Runtime;
using XericLibrary.Runtime.UIGraph;
using XericLibrary.Runtime.XericComponent;
namespace XericLibraryEditor.Spline
{
/// <summary>
/// 样条曲线路径的 Hierarchy 右键创建菜单。
/// 此文件直接放在 Release/Editor/ 下由 Unity 编译,
/// 不经过外部 DLL 管线,确保菜单立即生效。
/// </summary>
public static class SplinePathCreateMenuItems
{
private const string MenuRoot = "GameObject/Xeric Library/Spline/";
/// <summary>
/// 创建带 XericRectSplinePath 的 UI 节点(二维样条)
/// </summary>
[MenuItem(MenuRoot + "2D Spline", false, 20)]
private static void CreateRectSplinePath(MenuCommand menuCommand)
{
var go = CreateUiGameObject("2D Spline", new Vector2(300f, 150f));
var path = go.AddComponent<XericRectSplinePath>();
// 给两个默认控制点,创建后场景里立刻有点可选中
path.xericSpline.Add(MakeDefaultPoint(new Vector2(0.25f, 0.5f)));
path.xericSpline.Add(MakeDefaultPoint(new Vector2(0.75f, 0.5f)));
GameObjectUtility.SetParentAndAlign(go, menuCommand.context as GameObject);
Finish(go, "Create 2D Spline");
}
/// <summary>
/// 创建带 XericSplinePath 的场景节点(三维样条)
/// </summary>
[MenuItem(MenuRoot + "3D Spline", false, 21)]
private static void CreateSplinePath(MenuCommand menuCommand)
{
var go = new GameObject("3D Spline");
var path = go.AddComponent<XericSplinePath>();
path.xericSpline.Add(new XericSpline.SplinePoint(Vector3.left));
path.xericSpline.Add(new XericSpline.SplinePoint(Vector3.right));
GameObjectUtility.SetParentAndAlign(go, menuCommand.context as GameObject);
Finish(go, "Create 3D Spline");
}
/// <summary>
/// 创建由二维样条驱动的 UI 曲线渲染节点,并自动绑定同物体上的样条路径
/// </summary>
[MenuItem(MenuRoot + "Spline Curve Renderer", false, 22)]
private static void CreateSplineCurveRenderer(MenuCommand menuCommand)
{
var go = CreateUiGameObject("Spline Curve Renderer", new Vector2(300f, 150f));
var path = go.AddComponent<XericRectSplinePath>();
path.xericSpline.Add(MakeDefaultPoint(new Vector2(0.1f, 0.5f)));
path.xericSpline.Add(MakeDefaultPoint(new Vector2(0.9f, 0.5f)));
var renderer = go.AddComponent<UISplineCurveRenderer>();
renderer.splinePath = path;
GameObjectUtility.SetParentAndAlign(go, menuCommand.context as GameObject);
Finish(go, "Create Spline Curve Renderer");
}
/// <summary>
/// 建一条能直接跑起来的曲线进度条:底轨铺满 + 填充到 60%,两条面片共用同一条样条
/// </summary>
[MenuItem(MenuRoot + "Progress Bar Demo", false, 23)]
private static void CreateProgressBarDemo(MenuCommand menuCommand)
{
var go = CreateUiGameObject("Spline Progress Bar", new Vector2(520f, 220f));
var path = go.AddComponent<XericRectSplinePath>();
// S 形:弧长校正在这条形状上才看得出差别(匀速直线段上校正前后一模一样)
AddDemoPoint(path, new Vector2(0.08f, 0.3f), new Vector2(0.28f, 0f));
AddDemoPoint(path, new Vector2(0.5f, 0.78f), new Vector2(0.3f, 0f));
AddDemoPoint(path, new Vector2(0.92f, 0.22f), new Vector2(0.26f, 0f));
var renderer = go.AddComponent<UISplineCurveRenderer>();
renderer.splinePath = path;
// 面片0=底轨铺满并压暗,面片1=填充到 60% 保持全亮
renderer.headTailPairs = new[] { 1f, 0f, 0.6f, 0f };
renderer.ribbonTints = new[]
{
new Color32(90, 90, 90, 255),
new Color32(255, 255, 255, 255),
};
renderer.curveWidth = 14f;
renderer.fillRibbonIndex = 1;
go.AddComponent<UISplineProgressDriver>();
ApplyProgressMaterial(renderer);
PlaceUnderCanvas(go, menuCommand);
Finish(go, "Create Spline Progress Bar");
}
/// <summary>
/// 换上真正消费 uv0.y 的材质,并配一张沿路径方向的手风琴条纹,这样一进来就能看见流动。
/// 纹理是运行时生成的(不落盘、不进版本库),要长期用请自己把图拖到 progressTexture 上。
/// </summary>
private static void ApplyProgressMaterial(UISplineCurveRenderer renderer)
{
var shader = Shader.Find("XericLibrary/UIGraph/XericUICurveProgress");
if (shader == null || !shader.isSupported)
{
// 必须写全:本文件在 XericLibraryEditor.Spline 里,裸 Debug 会被
// 外层命名空间 XericLibraryEditor.Debug 抢走
UnityEngine.Debug.LogWarning("[Spline Progress] 找不到 XericUICurveProgress 着色器,演示沿用默认材质(默认材质不读 uv0.y,也没有采贴图)");
return;
}
var texture = new Texture2D(4, 128, TextureFormat.RGBA32, false);
texture.wrapMode = TextureWrapMode.Repeat;
for (int y = 0; y < texture.height; y++)
{
// 沿 uv.y(也就是路径进度)方向的亮带 + 暗谷
float wave = 0.5f + 0.5f * Mathf.Sin(y * Mathf.PI * 2f / texture.height * 3f);
var color = new Color(1f, 1f, 1f, Mathf.Lerp(0.25f, 1f, wave));
for (int x = 0; x < texture.width; x++)
texture.SetPixel(x, y, color);
}
texture.Apply();
var material = new Material(shader);
material.SetFloat("_Tile", 5f);
material.SetFloat("_Scroll", 0.45f);
material.SetFloat("_EdgeExp", 0.85f);
material.SetFloat("_EdgeStepRange", 0.12f);
renderer.progressTexture = texture;
renderer.material = material;
}
// 工具方法
/// <summary>默认手柄偏移(锚点空间):手柄全零时贝塞尔段退化成直线,看不出这是一条曲线</summary>
private const float DefaultHandleOffset = 0.1f;
/// <summary>
/// 造一个带默认手柄的端点。与 2D 编辑器里插入端点用的是同一套默认值,
/// 两边各自实现(编辑器在 DLL 里,这里要当场编译,不能等它重新构建)。
/// </summary>
private static XericRectSpline.RectSplinePoint MakeDefaultPoint(Vector2 anchor)
{
var point = new XericRectSpline.RectSplinePoint(anchor);
// Local 是相对锚点的偏移;StartHandle/EndHandle 是叠在锚点上的绝对值
point.StartHandleLocal = new Vector2(-DefaultHandleOffset, 0f);
point.EndHandleLocal = new Vector2(DefaultHandleOffset, 0f);
return point;
}
private static void AddDemoPoint(XericRectSplinePath path, Vector2 anchor, Vector2 handleOffset)
{
var point = new XericRectSpline.RectSplinePoint(anchor);
// Start/EndHandle 是"叠在锚点上的"绝对锚点值,不是偏移量
point.EndHandle = anchor + handleOffset;
point.StartHandle = anchor - handleOffset;
path.xericSpline.Add(point);
}
/// <summary>
/// Graphic 不在 Canvas 下就画不出来,演示物必须落到某个 Canvas 里;
/// 场景里没有就现建一个(与 BlueprintMenuItems 同一套路)
/// </summary>
private static void PlaceUnderCanvas(GameObject go, MenuCommand menuCommand)
{
var parent = menuCommand.context as GameObject;
if (parent != null && parent.GetComponentInParent<Canvas>() != null)
{
GameObjectUtility.SetParentAndAlign(go, parent);
return;
}
var canvas = Object.FindObjectOfType<Canvas>();
if (canvas == null)
{
var canvasGo = new GameObject("Spline Demo Canvas",
typeof(Canvas), typeof(UnityEngine.UI.CanvasScaler), typeof(UnityEngine.UI.GraphicRaycaster));
canvasGo.GetComponent<Canvas>().renderMode = RenderMode.ScreenSpaceOverlay;
Undo.RegisterCreatedObjectUndo(canvasGo, "Create Canvas For Spline Demo");
}
GameObjectUtility.SetParentAndAlign(go, canvas.gameObject);
}
/// <summary>
/// 创建带 RectTransform 的 UI 节点;界面样条的计算范围就是这个矩形
/// </summary>
private static GameObject CreateUiGameObject(string name, Vector2 size)
{
var go = new GameObject(name, typeof(RectTransform));
var rt = go.GetComponent<RectTransform>();
rt.sizeDelta = size;
rt.anchorMin = new Vector2(0.5f, 0.5f);
rt.anchorMax = new Vector2(0.5f, 0.5f);
rt.pivot = new Vector2(0.5f, 0.5f);
return go;
}
private static void Finish(GameObject go, string undoName)
{
Undo.RegisterCreatedObjectUndo(go, undoName);
Selection.activeObject = go;
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 543e1ea7a6089fd6a835ee1d69cd721c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
Binary file not shown.