点击预览位置,alt——上下挪动位置

Signed-off-by: TRAfoer <lhf190@outlook.com>
This commit is contained in:
2026-02-13 17:40:50 +08:00
parent c21dc74576
commit 96a2c60e16
34 changed files with 23940 additions and 211416 deletions

View File

@@ -16,6 +16,7 @@ namespace Ichni.Editor
base.Initialize(baseElement, title, parameterName);
if (parameterName != string.Empty)
{
toggle.isOn = (bool)ReflectionHelper.GetDeepValue(connectedBaseElement, parameterName);
toggle.onValueChanged.AddListener(ApplyParameters);
}

View File

@@ -1,5 +1,6 @@
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using DG.Tweening;
using Ichni.RhythmGame;
@@ -160,6 +161,124 @@ namespace Ichni.Editor
EditorManager.instance.uiManager.inspector.SetInspector(connectedGameElement);
}
public void MoveTab(bool isUp)
{
// 1. 基础合法性检查:根节点或孤立节点无法移动
if (parentTab == null || connectedGameElement.parentElement == null)
return;
// 获取当前层级的列表
var tabList = parentTab.childTabList;
int tabIndex = tabList.IndexOf(this);
var elementList = connectedGameElement.parentElement.childElementList;
int elemIndex = elementList.IndexOf(connectedGameElement);
// 计算目标索引
int targetTabIndex = isUp ? tabIndex - 1 : tabIndex + 1;
int targetElemIndex = isUp ? elemIndex - 1 : elemIndex + 1;
// 2. 边界检查:如果是第一个往上或最后一个往下,则返回
if (targetTabIndex < 0 || targetTabIndex >= tabList.Count) return;
if (targetElemIndex < 0 || targetElemIndex >= elementList.Count) return;
// 获取目标对象
var targetTab = tabList[targetTabIndex];
var targetElem = elementList[targetElemIndex];
// 3. 交换逻辑数据顺序List 内部排序)
tabList[tabIndex] = targetTab;
tabList[targetTabIndex] = this;
elementList[elemIndex] = targetElem;
elementList[targetElemIndex] = connectedGameElement;
// 4. 处理 UI Tab 的 Sibling Index解决偏移问题的核心
List<Transform> thisSubTree = GetTabSubTreeTransforms(this);
List<Transform> targetSubTree = GetTabSubTreeTransforms(targetTab);
if (thisSubTree.Count > 0 && targetSubTree.Count > 0)
{
if (isUp)
{
// 向上移:正序,插到目标分支的最上方
int insertIndex = targetSubTree[0].GetSiblingIndex();
for (int i = 0; i < thisSubTree.Count; i++)
{
thisSubTree[i].SetSiblingIndex(insertIndex + i);
}
}
else
{
// 向下移:逆序,依次插到目标分支最末尾的索引位置
// 这样先移过去的会被后移过去的顶到后面,保持原有父子相对顺序
int targetLastIndex = targetSubTree[targetSubTree.Count - 1].GetSiblingIndex();
for (int i = thisSubTree.Count - 1; i >= 0; i--)
{
thisSubTree[i].SetSiblingIndex(targetLastIndex);
}
}
}
// 5. 处理场景 GameElement 的 Sibling Index同理
List<Transform> thisElemSubTree = GetElementSubTreeTransforms(connectedGameElement);
List<Transform> targetElemSubTree = GetElementSubTreeTransforms(targetElem);
if (thisElemSubTree.Count > 0 && targetElemSubTree.Count > 0)
{
if (isUp)
{
int insertIndex = targetElemSubTree[0].GetSiblingIndex();
for (int i = 0; i < thisElemSubTree.Count; i++)
{
thisElemSubTree[i].SetSiblingIndex(insertIndex + i);
}
}
else
{
int targetLastIndex = targetElemSubTree[targetElemSubTree.Count - 1].GetSiblingIndex();
for (int i = thisElemSubTree.Count - 1; i >= 0; i--)
{
thisElemSubTree[i].SetSiblingIndex(targetLastIndex);
}
}
}
//奇妙的特判阶段
foreach (var i in parentTab.childTabList)
{
i.connectedGameElement.Refresh();
}
parentTab.connectedGameElement.Refresh();
if (parentTab.connectedGameElement is Track track)
{
track.trackPathSubmodule.pathNodeList =
track.childElementList.OfType<PathNode>().ToList();
track.trackPathSubmodule.SetPathPoints();
}
}
// 获取tab及其所有子tab的transform前序遍历
private List<Transform> GetTabSubTreeTransforms(HierarchyTab tab)
{
List<Transform> list = new List<Transform>();
list.Add(tab.transform);
foreach (var child in tab.childTabList)
list.AddRange(GetTabSubTreeTransforms(child));
return list;
}
// 获取GameElement及其所有子元素的transform前序遍历
private List<Transform> GetElementSubTreeTransforms(GameElement element)
{
List<Transform> list = new List<Transform>();
if (element.transform != null)
list.Add(element.transform);
foreach (var child in element.childElementList)
list.AddRange(GetElementSubTreeTransforms(child));
return list;
}
public void ExpandOrFold()
{
ExpandOrFold(false);

View File

@@ -78,7 +78,7 @@ namespace Ichni.Editor
}
public DynamicUIToggle GenerateToggle(IBaseElement baseElement, DynamicUISubcontainer subcontainer, string title,
string parameterName)
string parameterName = "")
{
DynamicUIToggle toggle = Object.Instantiate(EditorManager.instance.basePrefabs.toggle, subcontainer.rect)
.GetComponent<DynamicUIToggle>();

View File

@@ -556,7 +556,8 @@ namespace ichni.RhythmGame // [修复] 修正命名空间首字母大写,符
}
public static FastNoteTracker GenerateElement(string elementName, Guid id, List<string> tags,
bool isFirstGenerated, GameElement parentElement)
bool isFirstGenerated, GameElement parentElement,
bool isEnabled = true, bool showNotePreview = false, float horizonWidth = 5f, int beatDiver = 1)
{
FastNoteTracker fastNoteTracker = Instantiate(EditorManager.instance.basePrefabs.emptyObject, parentElement.transform)
.AddComponent<FastNoteTracker>();
@@ -566,6 +567,10 @@ namespace ichni.RhythmGame // [修复] 修正命名空间首字母大写,符
return null;
}
fastNoteTracker.Initialize(elementName, id, tags, isFirstGenerated, parentElement);
fastNoteTracker.IsEnabled = isEnabled;
fastNoteTracker.showNotePreview = showNotePreview;
fastNoteTracker.horizonWidth = horizonWidth;
fastNoteTracker.BeatDiver = beatDiver;
return fastNoteTracker;
}
@@ -574,10 +579,23 @@ namespace ichni.RhythmGame // [修复] 修正命名空间首字母大写,符
base.Initialize(elementName, id, tags, isFirstGenerated, parentElement);
//parentElement.refreshAction += AdjustBeatLine;
}
public override void AfterInitialize()
{
Observable.NextFrame().Subscribe(_ => Refresh());
}
public override void SaveBM()
{
matchedBM = new FastNoteTracker_BM(elementName, elementGuid, tags, parentElement.elementGuid);
matchedBM = new FastNoteTracker_BM(
elementName,
elementGuid,
tags,
parentElement.elementGuid,
IsEnabled,
showNotePreview,
horizonWidth,
BeatDiver
);
}
}
}
@@ -586,12 +604,21 @@ namespace Ichni.RhythmGame.Beatmap
{
public class FastNoteTracker_BM : GameElement_BM
{
public FastNoteTracker_BM(string elementName, Guid id, List<string> tags, Guid attachedElementGuid)
public bool IsEnabled { get; set; }
public bool showNotePreview { get; set; }
public float horizonWidth { get; set; }
public int beatDiver { get; set; }
public FastNoteTracker_BM(string elementName, Guid id, List<string> tags, Guid attachedElementGuid, bool IsEnabled, bool showNotePreview, float horizonWidth, int beatDiver)
{
this.elementName = elementName;
this.elementGuid = id;
this.tags = tags;
this.attachedElementGuid = attachedElementGuid;
this.IsEnabled = IsEnabled;
this.showNotePreview = showNotePreview;
this.horizonWidth = horizonWidth;
this.beatDiver = beatDiver;
}
public override GameElement DuplicateBM(GameElement attached)

View File

@@ -95,15 +95,10 @@ public class PanelDrawer : MonoBehaviour //暂时支持xz
public Canvas DisplayCanvas;
private IEnumerator Pressing()
{
GameObject gobj = new GameObject("PanelDrawerPreview");
RawImage rawImage = gobj.AddComponent<RawImage>();
GameObject gobj2 = new GameObject("PanelDrawerText");
gobj2.transform.SetParent(gobj.transform, false);
gobj2.transform.SetParent(DisplayCanvas.transform, false);
TextMeshProUGUI Text = gobj2.AddComponent<TextMeshProUGUI>();
Text.rectTransform.position = new Vector3(50, 50, 0);
rawImage.texture = ingTexture;
rawImage.rectTransform.SetParent(DisplayCanvas.transform);
rawImage.rectTransform.sizeDelta = new Vector2(100, 100);
Vector3 hitPoint = new Vector3(0, 0, 0);
while (Mouse.current.leftButton.isPressed)
{
@@ -117,21 +112,23 @@ public class PanelDrawer : MonoBehaviour //暂时支持xz
if (Keyboard.current.leftAltKey.isPressed)
{
hitPoint = new Vector3(Mathf.Round(hitPoint.x / 0.5f) * 0.5f, hitPoint.y, Mathf.Round(hitPoint.z / 0.5f) * 0.5f);
rawImage.rectTransform.position =
new Vector3(Mathf.Round(Mouse.current.position.ReadValue().x / 10) * 10, Mathf.Round(Mouse.current.position.ReadValue().y / 10) * 10, 0);
}
else
{
rawImage.rectTransform.position = Mouse.current.position.ReadValue();
}
Text.text = $"X: {hitPoint.x:F2}\nY: {hitPoint.y:F2}\nZ: {hitPoint.z:F2}";
Text.rectTransform.position = Mouse.current.position.ReadValue() + new Vector2(20, -20);
Text.text = $"X: {hitPoint.x:F2}\nZ: {hitPoint.z:F2}\n\n";
Text.fontSize = 45;
Text.color = Color.yellow;
}
yield return null;
}
Destroy(rawImage.gameObject);
Destroy(Text.gameObject);
var i = PathNode.GenerateElement("new pathnode", Guid.NewGuid(), new List<string>(), true, (Track)connectedGameElement, true);
i.transformSubmodule.originalPosition = hitPoint;
i.transform.position = hitPoint;
i.transformSubmodule.originalPosition = i.transform.localPosition;
i.Refresh();
connectedGameElement.Refresh();
}

View File

@@ -61,6 +61,8 @@ namespace Ichni.Editor
if (isMoving) return;
transform.eulerAngles = targetGameElement.parentElement?.transform.eulerAngles ?? Vector3.zero;
transform.position = targetGameElement.transform.position;
transform.localScale = //相机距离的函数,保持在一定大小范围内
Vector3.one * Mathf.Clamp(Vector3.Distance(editorCamera.transform.position, transform.position) * 0.05f, 0.1f, 1000f);
if (Mouse.current.leftButton.wasPressedThisFrame)
{
Vector2 mousePosition = Mouse.current.position.ReadValue();

View File

@@ -105,12 +105,10 @@ namespace Ichni.RhythmGame
public abstract partial class GameElement //存档,删除,复制,粘贴
{
public Action refreshAction = new Action(() => { });
public virtual void Refresh()
{
if (connectedTab != null) connectedTab.tabButtonText.text = this.elementName;
gameObject.name = elementName;
refreshAction?.Invoke();
}
/// <summary>

View File

@@ -1,7 +1,10 @@
using System.Collections;
using System.Collections.Generic;
using DG.Tweening;
using Ichni;
using TMPro;
using UnityEngine;
using UnityEngine.InputSystem;
[RequireComponent(typeof(MeshFilter), typeof(MeshRenderer))]
public class SimpleGridController : MonoBehaviour
@@ -39,6 +42,84 @@ public class SimpleGridController : MonoBehaviour
}
public Color MainColor = new Color(0.8f, 0.8f, 0.8f, 0.5f);
public Color SubColor = new Color(0.5f, 0.5f, 0.5f, 0.5f);
void Update()
{
if (Mouse.current.middleButton.wasPressedThisFrame)
{
StartCoroutine(Pressing());
}
}
GameObject coordTextObj = null;
IEnumerator Pressing()
{
if (coordTextObj != null)
{
coordTextObj.transform.DOComplete();
}
TextMeshPro coordText = null;
float targetScale = 1f;
Tweener scaleTween = null;
while (Mouse.current.middleButton.isPressed)
{
Ray ray = targetCamera.ScreenPointToRay(Mouse.current.position.ReadValue());
Plane plane = new Plane(Vector3.up, Vector3.zero);
if (plane.Raycast(ray, out float enter))
{
Vector3 hitPoint = ray.GetPoint(enter);
Vector2 xz = new Vector2(hitPoint.x, hitPoint.z);
// 创建或复用文字对象
if (coordTextObj == null)
{
coordTextObj = Instantiate(textPrefab, textParent);
coordText = coordTextObj.GetComponent<TextMeshPro>();
coordTextObj.transform.localScale = Vector3.zero;
targetScale = Mathf.Clamp(Vector3.Distance(targetCamera.transform.position, hitPoint) * 0.05f, 0.1f, 1000f);
scaleTween = coordTextObj.transform.DOScale(Vector3.one * targetScale, 0.2f).SetEase(Ease.OutBack);
}
else
{
// 动态融合用DOTween的ChangeEndValue实时调整目标scale
targetScale = Mathf.Clamp(Vector3.Distance(targetCamera.transform.position, hitPoint) * 0.05f, 0.1f, 1000f);
if (scaleTween != null && scaleTween.IsActive())
{
scaleTween.ChangeEndValue(Vector3.one * targetScale, true);
}
else
{
coordTextObj.transform.localScale = Vector3.one * targetScale;
}
}
coordTextObj.transform.position = hitPoint + Vector3.up * targetScale;
coordTextObj.transform.rotation = Quaternion.LookRotation(coordTextObj.transform.position - targetCamera.transform.position, Vector3.up);
if (coordText == null) coordText = coordTextObj.GetComponent<TextMeshPro>();
coordText.text = $"({xz.x:F2}, {xz.y:F2})\n({Mathf.Round(xz.x):F2}, {Mathf.Round(xz.y):F2})";
coordText.fontSize = 8;
coordText.enableWordWrapping = false;
coordText.color = Color.yellow;
}
yield return null;
}
// 释放文字对象
if (coordTextObj != null)
{
coordTextObj.transform.DOScale(0, 0.3f).SetEase(Ease.OutExpo).OnComplete(() =>
{
Destroy(coordTextObj);
coordTextObj = null;
});
}
}
void LateUpdate()
{
if (!targetCamera) return;
@@ -49,28 +130,50 @@ public class SimpleGridController : MonoBehaviour
transform.position = new Vector3(camPos.x, 0, camPos.z);
// 2. 计算层级 (1m / 10m / 100m)
CalculateGridLevel(camPos.y);
float absH = Mathf.Abs(camPos.y);
float step = baseGridSize;
Color Mcolor = MainColor;
Color Scolor = SubColor;
// 简单的自适应逻辑:根据高度决定网格密度
if (absH > 150f)//>150
{
step *= 10f;
Scolor = Lerp(SubColor, Color.clear, (absH - 150f) / 150f);
}
else if (absH > 15f)//15-150
{
step *= 10f;
Mcolor = Lerp(MainColor, SubColor, (absH - 15f) / (150f - 15f));
Scolor = Lerp(SubColor, Color.clear, (absH - 15f) / (150f - 15f));
}
else//0-15
{
Mcolor = Lerp(MainColor, SubColor, absH / 15f);
Scolor = Lerp(SubColor, Color.clear, absH / 15f);
}
_currentSpacing = step;
// 3. 更新 Shader
if (_instancedMat)
{
_instancedMat.SetColor("_MainColor", Scolor);
_instancedMat.SetColor("_AxisColor", Mcolor);
_instancedMat.SetFloat("_GridSpacing", _currentSpacing);
_instancedMat.SetFloat("_FadeDist", drawDistance);
}
}
private void CalculateGridLevel(float camHeight)
private Color Lerp(Color a, Color b, float t)
{
float absH = Mathf.Abs(camHeight);
float step = baseGridSize;
// 简单的自适应逻辑:根据高度决定网格密度
if (absH > 15f) step *= 10f;
if (absH > 150f) step *= 10f;
_currentSpacing = step;
return new Color(
Mathf.Lerp(a.r, b.r, t),
Mathf.Lerp(a.g, b.g, t),
Mathf.Lerp(a.b, b.b, t),
Mathf.Lerp(a.a, b.a, t)
);
}

View File

@@ -193,7 +193,11 @@ namespace Ichni
cameraManager.SetUpInspector();
var oo = inspector.GenerateContainer("Grid");
var p = oo.GenerateSubcontainer(3);
var po = inspector.GenerateToggle(this, p, "Enable Grid", nameof(gridController) + "." + nameof(gridController.gameObject) + ".activeSelf");
var po = inspector.GenerateToggle(this, p, "Enable Grid");
po.AddListenerFunction(() =>
{
gridController.gameObject.SetActive(po.toggle.isOn);
});
var o = inspector.GenerateInputField(p, "Grid Size", (gridController.baseGridSize * 10).ToString());
o.AddListenerFunction(() =>
{

View File

@@ -3,6 +3,7 @@ using System.Collections;
using System.Collections.Generic;
using System.Linq;
using Ichni.RhythmGame;
using Sirenix.Utilities;
using TMPro;
using UnityEngine;
using UnityEngine.EventSystems;
@@ -257,6 +258,25 @@ namespace Ichni.Editor
EditorManager.instance.operationManager.CopyPasteDeleteModule.DeleteElement(EditorManager.instance.operationManager.currentSelectedElements[0]);
}
}
if (Keyboard.current.leftAltKey.isPressed)
{
if (Keyboard.current.upArrowKey.wasPressedThisFrame)
{
if (EditorManager.instance.operationManager.currentSelectedElements.Count == 1)
{
EditorManager.instance.operationManager.currentSelectedElements[0]?.connectedTab?.MoveTab(true);
}
}
else if (Keyboard.current.downArrowKey.wasPressedThisFrame)
{
if (EditorManager.instance.operationManager.currentSelectedElements.Count == 1)
{
EditorManager.instance.operationManager.currentSelectedElements[0]?.connectedTab?.MoveTab(false);
}
}
}
}
private void ResolutionHintsOperation()