优化hierarchyab和consolemethod,然后是粒子追踪

Signed-off-by: TRADER_FOER <lhf190@outlook.com>
This commit is contained in:
2026-07-29 22:27:21 +08:00
parent d662f2fa95
commit ab5465bab0
14 changed files with 21084 additions and 18871 deletions

View File

@@ -48,21 +48,14 @@ namespace Ichni.Editor
}
}
/// <summary>
/// 重命名选中元素
/// </summary>
public static void ReName(string message)
{
inspector.connectedGameElement.elementName = message;
inspector.connectedGameElement.Refresh();
}
#endregion
#region PathNode Generation ()
/// <summary>
/// 直线路径节点生成 (Line PathNode Generator)
/// </summary>
public static void Lgp(int loop, Vector3 start, Vector3 end, bool Clear = false, bool offsetOrigin = false)
public static void Lgp(int loop, Vector3 start, Vector3 end, bool Clear = false)
{
if (inspector.connectedGameElement == null || inspector.connectedGameElement.GetType() != typeof(Track))
{
@@ -80,17 +73,10 @@ namespace Ichni.Editor
// 如果 Clear 且有旧节点,迁移变换
if (Clear)
{
if (offsetOrigin && oldNodes.Count > 0 && newNodes.Count > 0)
{
AdjustPathNodesToNearest(track, newNodes, oldNodes);
}
// 清除之前的PathNode
foreach (var node in oldNodes)
{
EditorManager.instance.operationManager.CopyPasteDeleteModule.DeleteElement(node);
}
}
for (int i = 0; i < loop; i++)
{
float t = (float)i / (loop - 1); // 修正插值
@@ -186,33 +172,7 @@ namespace Ichni.Editor
#region PathNode/Track Utilities (/)
/// <summary>
/// 将原有 PathNode 的变换(位置、旋转、缩放)迁移到新生成的最近 PathNode 上
/// </summary>
public static bool AdjustPathNodesToNearest(Track track, List<PathNode> newNodes, List<PathNode> oldNodes)
{
foreach (var oldNode in oldNodes)
{
// 找到距离 oldNode 最近的新节点
PathNode nearest = newNodes
.OrderBy(n => Vector3.Distance(n.transformSubmodule.originalPosition, oldNode.transformSubmodule.originalPosition))
.FirstOrDefault();
if (nearest != null)
{
// 计算 oldNode 的变换(直接用欧拉角,不用四元数)
Vector3 deltaPos = oldNode.transformSubmodule.originalPosition - oldNode.transformSubmodule.originalPosition;
Vector3 deltaEuler = oldNode.transformSubmodule.originalEulerAngles - oldNode.transformSubmodule.originalEulerAngles;
Vector3 deltaScale = oldNode.transformSubmodule.originalScale - oldNode.transformSubmodule.originalScale;
// 将变换应用到新节点
nearest.transformSubmodule.originalPosition += deltaPos;
nearest.transformSubmodule.originalEulerAngles += deltaEuler;
nearest.transformSubmodule.originalScale += deltaScale;
}
}
return true;
}
/// <summary>
/// 删除父元素下所有与当前选中元素类型相同的其他元素,最后删除当前选中元素

View File

@@ -57,7 +57,7 @@ namespace Ichni.Editor
this.isSelected = false;
if (BgImage != null)
{
BgImage.color = new Color(0.5f, 0.5f, 0.5f, 0);
BgImage.color = new Color(0, 0, 0, 0);
}
this.childTabList = new List<HierarchyTab>();
@@ -157,17 +157,17 @@ namespace Ichni.Editor
}
}
public void SelectGameElement()
{
if (Keyboard.current.shiftKey.isPressed)
{
EditorManager.instance.operationManager.AddSelectRangeToElement(connectedGameElement);
}
else if (Keyboard.current.leftCtrlKey.isPressed)
{
if (!isSelected)
{
EditorManager.instance.operationManager.AddSelectElement(connectedGameElement);
public void SelectGameElement()
{
if (Keyboard.current.shiftKey.isPressed)
{
EditorManager.instance.operationManager.AddSelectRangeToElement(connectedGameElement);
}
else if (Keyboard.current.leftCtrlKey.isPressed)
{
if (!isSelected)
{
EditorManager.instance.operationManager.AddSelectElement(connectedGameElement);
}
else
{

View File

@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using Dreamteck.Splines;
using UnityEngine;
namespace Ichni.RhythmGame.Beatmap
@@ -22,6 +23,7 @@ namespace Ichni.RhythmGame.Beatmap
public string materialThemeBundleName = string.Empty;
public string materialName = string.Empty;
public ParticleController.PathNodeSizeMode pathNodeSizeMode = ParticleController.PathNodeSizeMode.LegacyOffset;
public ParticleTracker_BM()
{
@@ -33,7 +35,8 @@ namespace Ichni.RhythmGame.Beatmap
bool is3D, float width, Vector3 extendDirection,
float density, float lifeTime,
bool isAutoOrient, Vector3 particleRotation,
string materialThemeBundleName, string materialName) : base(elementName, elementGuid, tags, attachedElement)
string materialThemeBundleName, string materialName,
ParticleController.PathNodeSizeMode pathNodeSizeMode = ParticleController.PathNodeSizeMode.LegacyOffset) : base(elementName, elementGuid, tags, attachedElement)
{
this.prewarm = prewarm;
this.playTime = playTime;
@@ -48,6 +51,7 @@ namespace Ichni.RhythmGame.Beatmap
this.materialThemeBundleName = materialThemeBundleName;
this.materialName = materialName;
this.pathNodeSizeMode = pathNodeSizeMode;
}
public override void ExecuteBM()
@@ -55,7 +59,8 @@ namespace Ichni.RhythmGame.Beatmap
matchedElement = ParticleTracker.GenerateElement(
elementName, elementGuid, tags, false,
GetElement(attachedElementGuid) as Track, materialThemeBundleName, materialName,
prewarm, playTime, stopTime, is3D, width, extendDirection, density, lifeTime, isAutoOrient, particleRotation);
prewarm, playTime, stopTime, is3D, width, extendDirection, density, lifeTime, isAutoOrient, particleRotation,
pathNodeSizeMode);
}
#region [Editor ] Editor Interfaces
@@ -64,7 +69,8 @@ namespace Ichni.RhythmGame.Beatmap
return ParticleTracker.GenerateElement(
elementName, Guid.NewGuid(), tags, false,
attached as Track, materialThemeBundleName, materialName,
prewarm, playTime, stopTime, is3D, width, extendDirection, density, lifeTime, isAutoOrient, particleRotation);
prewarm, playTime, stopTime, is3D, width, extendDirection, density, lifeTime, isAutoOrient, particleRotation,
pathNodeSizeMode);
}
#endregion
}

View File

@@ -110,7 +110,8 @@ namespace Ichni.RhythmGame
.Button("Particle Tracker", () =>
{
CommandManager.ExecuteCreate(() => ParticleTracker.GenerateElement("New Particle Tracker", Guid.NewGuid(), new List<string>(), true, this,
string.Empty, string.Empty, false, 0, 1, false, 10, Vector3.right, 10, 5, true, Vector3.zero));
string.Empty, string.Empty, false, 0, 1, false, 10, Vector3.right, 10, 5, true, Vector3.zero,
Dreamteck.Splines.ParticleController.PathNodeSizeMode.ApplyPathNodeSize));
})
.Button("Object Tracker", () =>
{

View File

@@ -13,7 +13,7 @@ namespace Ichni.RhythmGame
matchedBM = new ParticleTracker_BM(elementName, elementGuid, tags,
parentElement.matchedBM as GameElement_BM,
prewarm, playTime, stopTime, is3D, width, extendDirection, density, lifeTime, isAutoOrient, particleRotation,
themeBundleName, materialName);
themeBundleName, materialName, pathNodeSizeMode);
}
#endregion
@@ -45,6 +45,8 @@ namespace Ichni.RhythmGame
.OnChanged(SetShape)
.Vector3Field(nameof(extendDirection), "Extend Direction")
.OnChanged(SetShape)
.Dropdown(nameof(pathNodeSizeMode), typeof(Dreamteck.Splines.ParticleController.PathNodeSizeMode), "PathNode Size Mode")
.OnChanged(SetShape)
.Section("Emission")
.InputField(nameof(density), "Density")
.OnChanged(() => particlesContainer.SetDensity(density))

View File

@@ -35,6 +35,7 @@ namespace Ichni.RhythmGame
public bool isAutoOrient;
public Vector3 particleRotation;
public ParticleController.PathNodeSizeMode pathNodeSizeMode;
#endregion
#region [] Generation & Initialization
@@ -43,7 +44,8 @@ namespace Ichni.RhythmGame
bool prewarm, float playTime, float stopTime,
bool is3D, float width, Vector3 extendDirection,
float density, float lifeTime,
bool isAutoOrient, Vector3 particleRotation)
bool isAutoOrient, Vector3 particleRotation,
ParticleController.PathNodeSizeMode pathNodeSizeMode = ParticleController.PathNodeSizeMode.LegacyOffset)
{
ParticleTracker particleTracker = Instantiate(EditorManager.instance.basePrefabs.particleTracker, track.transform)
.GetComponent<ParticleTracker>();
@@ -57,6 +59,7 @@ namespace Ichni.RhythmGame
particleTracker.materialNameList = new List<string>();
particleTracker.themeBundleName = themeBundleName;
particleTracker.materialName = materialName;
particleTracker.pathNodeSizeMode = pathNodeSizeMode;
particleTracker.particlesContainer.SetParticleMaterial(themeBundleName, materialName);
particleTracker.SetParticleSettings(prewarm, is3D, width, extendDirection, density, lifeTime, isAutoOrient, particleRotation);
@@ -88,16 +91,17 @@ namespace Ichni.RhythmGame
this.prewarm = prewarm;
this.isAutoOrient = isAutoOrient;
this.particleRotation = particleRotation;
particlesContainer.SetParticleSettings(prewarm, ParticleSystemSimulationSpace.Local, density,
particlesContainer.SetParticleSettings(prewarm, ParticleSystemSimulationSpace.Local, density,
lifeTime, 0, 1, isAutoOrient, particleRotation);
SetShape();
}
private void SetShape()
{
particleController.is3D = is3D;
particleController.width = width;
particleController.extendDirection = extendDirection;
particleController.pathNodeSizeMode = pathNodeSizeMode;
particleController.Rebuild();
}
#endregion

View File

@@ -2,40 +2,41 @@ using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using DG.Tweening;
using Ichni.Editor.Commands;
using Ichni.RhythmGame;
using Ichni.RhythmGame.Beatmap;
using UnityEngine;
using UnityEngine.InputSystem;
namespace Ichni.Editor
{
public class OperationManager
{
public List<GameElement> currentSelectedElements { get; private set; }
public TempOutlineModule TempOutlineModule = new TempOutlineModule();
public CopyPasteDeleteModule CopyPasteDeleteModule;
public FindingModule FindingModule;
public OperationManager()
{
currentSelectedElements = new List<GameElement>();
CopyPasteDeleteModule = new CopyPasteDeleteModule();
FindingModule = new FindingModule();
}
using UnityEngine;
using UnityEngine.InputSystem;
namespace Ichni.Editor
{
public class OperationManager
{
public List<GameElement> currentSelectedElements { get; private set; }
public TempOutlineModule TempOutlineModule = new TempOutlineModule();
public CopyPasteDeleteModule CopyPasteDeleteModule;
public FindingModule FindingModule;
public OperationManager()
{
currentSelectedElements = new List<GameElement>();
CopyPasteDeleteModule = new CopyPasteDeleteModule();
FindingModule = new FindingModule();
}
public void AddSelectElement(GameElement gameElement)
{
if (gameElement == null || gameElement.connectedTab == null) return;
if (currentSelectedElements.Contains(gameElement)) return;
currentSelectedElements.Add(gameElement);
gameElement.connectedTab.isSelected = true;
if (gameElement.connectedTab.BgImage != null)
{
gameElement.connectedTab.BgImage.color = new Color(0f, 0f, 0f, 0.8f);
currentSelectedElements.Add(gameElement);
gameElement.connectedTab.isSelected = true;
if (gameElement.connectedTab.BgImage != null)
{
gameElement.connectedTab.BgImage.DOColor(new Color(0f, 0f, 0f, 0.8f), 0.2f);
}
}
@@ -93,75 +94,78 @@ namespace Ichni.Editor
public void RemoveSelectElement(GameElement gameElement)
{
if (gameElement == null) return;
currentSelectedElements.Remove(gameElement);
if (gameElement.connectedTab == null) return;
gameElement.connectedTab.isSelected = false;
if (gameElement.connectedTab.BgImage != null)
{
gameElement.connectedTab.BgImage.color = new Color(0.5f, 0.5f, 0.5f, 0);
}
}
public void ClearSelectedElements()
{
currentSelectedElements.RemoveAll(gameElement => gameElement == null);
currentSelectedElements.ForEach(gameElement =>
{
if (gameElement == null || gameElement.connectedTab == null) return;
gameElement.connectedTab.isSelected = false;
if (gameElement.connectedTab.BgImage != null)
{
gameElement.connectedTab.BgImage.color = new Color(0.5f, 0.5f, 0.5f, 0);
}
});
currentSelectedElements.Clear();
}
}
public class TempOutlineModule
{
public List<GameElement> outlinedElements;
public Material outlineMaterial => EditorManager.instance.basePrefabs.outlineShaderMaterial;
public TempOutlineModule()
{
outlinedElements = new List<GameElement>();
}
public void AddOutline(GameElement gameElement)
{
outlinedElements.Add(gameElement);
}
public void RemoveOutline(GameElement gameElement)
{
outlinedElements.Remove(gameElement);
}
public void ClearOutline()
{
outlinedElements.Clear();
}
}
public class FindingModule
{
public GameElement FindGameElementByName(string elementName)
{
foreach (GameElement element in EditorManager.instance.beatmapContainer.gameElementList)
{
if (element.elementName == elementName)
{
return element;
}
}
LogWindow.Log("Element not found: " + elementName, Color.red);
return null;
}
}
currentSelectedElements.Remove(gameElement);
if (gameElement.connectedTab == null) return;
gameElement.connectedTab.isSelected = false;
if (gameElement.connectedTab.BgImage != null)
{
gameElement.connectedTab.BgImage.DOColor(new Color(0, 0, 0, 0), 0.2f);
}
}
public void ClearSelectedElements()
{
currentSelectedElements.RemoveAll(gameElement => gameElement == null);
currentSelectedElements.ForEach(gameElement =>
{
if (gameElement == null || gameElement.connectedTab == null) return;
gameElement.connectedTab.isSelected = false;
if (gameElement.connectedTab.BgImage != null)
{
gameElement.connectedTab.BgImage.DOColor(new Color(0, 0, 0, 0), 0.2f);
}
});
currentSelectedElements.Clear();
}
}
public class TempOutlineModule
{
public List<GameElement> outlinedElements;
public Material outlineMaterial => EditorManager.instance.basePrefabs.outlineShaderMaterial;
public TempOutlineModule()
{
outlinedElements = new List<GameElement>();
}
public void AddOutline(GameElement gameElement)
{
outlinedElements.Add(gameElement);
}
public void RemoveOutline(GameElement gameElement)
{
outlinedElements.Remove(gameElement);
}
public void ClearOutline()
{
outlinedElements.Clear();
}
}
public class FindingModule
{
public GameElement FindGameElementByName(string elementName)
{
foreach (GameElement element in EditorManager.instance.beatmapContainer.gameElementList)
{
if (element.elementName == elementName)
{
return element;
}
}
LogWindow.Log("Element not found: " + elementName, Color.red);
return null;
}
}
public class CopyPasteDeleteModule
{
public GameElement copiedElement;
@@ -220,8 +224,8 @@ namespace Ichni.Editor
public void PasteElement(GameElement parentElement)
{
if (parentElement == null)
{
LogWindow.Log("No parent element selected to paste.", Color.red);
{
LogWindow.Log("No parent element selected to paste.", Color.red);
return;
}