@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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>();
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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(() =>
|
||||
{
|
||||
|
||||
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user