修改track复制步进和撤销模式

Signed-off-by: TRADER_FOER <lhf190@outlook.com>
This commit is contained in:
2026-07-10 14:05:45 +08:00
parent fdc20cd66f
commit a835e8321b
30 changed files with 1501 additions and 367 deletions

View File

@@ -230,9 +230,15 @@ namespace Ichni
public class BeatmapClipManager
{
private string clipFolder => Application.streamingAssetsPath + "/Clips";
private string GetClipPath(string clipName) => clipFolder + "/" + clipName + ".json";
public void SaveClip(string clipName)
{
LogWindow.Log("Start Saving Clip...");
clipName = clipName?.Trim();
if (!ValidateClipName(clipName)) return;
if (EditorManager.instance.operationManager.currentSelectedElements.Count != 1) // TODO: 后续适应多选
{
@@ -256,8 +262,11 @@ namespace Ichni
public void LoadClip(string clipName)
{
LogWindow.Log("Start Loading Clip...");
clipName = clipName?.Trim();
if (!ES3.FileExists(Application.streamingAssetsPath + "/Clips/" + clipName + ".json"))
if (!ValidateClipName(clipName)) return;
if (!ES3.FileExists(GetClipPath(clipName)))
{
LogWindow.Log("Clip not found", Color.red);
return;
@@ -278,9 +287,10 @@ namespace Ichni
}
Debug.Log(selectedElement.elementName + " " + selectedElement.elementGuid);
_LoadClip(selectedElement, clipName);
LogWindow.Log("Load Clip Complete", Color.green);
if (_LoadClip(selectedElement, clipName))
{
LogWindow.Log("Load Clip Complete", Color.green);
}
}
private void _SaveClip(GameElement element, string clipName)
@@ -290,7 +300,8 @@ namespace Ichni
element.GetAllGameElementsFromThis().ForEach(e =>
{
e.SaveBM();
clip.Add(e.matchedBM);
if (e.matchedBM != null)
clip.Add(e.matchedBM);
e.submoduleList.ForEach(s =>
{
s.SaveBM();
@@ -299,47 +310,59 @@ namespace Ichni
});
});
string filePath = Application.streamingAssetsPath + "/Clips/" + clipName + ".json";
Directory.CreateDirectory(clipFolder);
string filePath = GetClipPath(clipName);
ES3.Save("Clip", clip, filePath, ProjectManager.SaveSettings);
}
private void _LoadClip(GameElement target, string clipName)
private bool _LoadClip(GameElement target, string clipName)
{
try
{
string filePath = Application.streamingAssetsPath + "/Clips/" + clipName + ".json";
string filePath = GetClipPath(clipName);
List<BaseElement_BM> clip = ES3.Load<List<BaseElement_BM>>("Clip", filePath, ProjectManager.SaveSettings);
if (clip == null || clip.Count == 0)
{
Debug.LogError("Clip is empty or null");
return;
LogWindow.Log("Clip is empty or null", Color.red);
return false;
}
// 验证第一个元素
if (!(clip[0] is GameElement_BM first))
{
Debug.LogError("First element is not a GameElement_BM");
return;
LogWindow.Log("First element is not a GameElement_BM", Color.red);
return false;
}
// 处理目标对象 - 添加清理机制
target.SaveBM();
if (target.matchedBM is not GameElement_BM targetBM)
{
LogWindow.Log("Load Clip failed: target did not create a valid BM.", Color.red);
return false;
}
bool targetAdded = GameElement_BM.identifier.TryAdd(target.elementGuid, target.matchedBM as GameElement_BM);
if (!targetAdded)
{
Debug.LogWarning("Target element already exists in identifier dictionary");
}
targetBM.matchedElement = target;
List<GameElement> loadedElements = new List<GameElement>();
// 处理第一个元素及其附加元素
ProcessGameElement(first, clip, target.elementGuid);
if (!ProcessGameElement(first, clip, target.elementGuid, loadedElements)) return false;
// 处理后续元素
for (var index = 1; index < clip.Count; index++)
{
if (clip[index] is GameElement_BM gameElement)
{
ProcessGameElement(gameElement, clip, null);
if (!ProcessGameElement(gameElement, clip, null, loadedElements)) return false;
}
else
{
@@ -348,14 +371,28 @@ namespace Ichni
}
}
EditorManager.instance.beatmapContainer.ExecuteLowPriorityActions();
loadedElements.ForEach(element =>
{
if (element == null) return;
element.AfterInitialize();
element.Refresh();
});
return true;
}
catch (Exception ex)
{
Debug.LogError($"Failed to load clip: {ex.Message}");
LogWindow.Log("Failed to load clip: " + ex.Message, Color.red);
return false;
}
}
private void ProcessGameElement(GameElement_BM gameElement, List<BaseElement_BM> clip, Guid? attachToGuid)
private bool ProcessGameElement(
GameElement_BM gameElement,
List<BaseElement_BM> clip,
Guid? attachToGuid,
List<GameElement> loadedElements)
{
List<BaseElement_BM> attachedElements = GameElement_BM.GetAllAttachedBaseElements(gameElement, clip);
@@ -365,7 +402,8 @@ namespace Ichni
if (!elementAdded)
{
Debug.LogError($"Failed to add element with GUID: {gameElement.elementGuid}");
return;
LogWindow.Log("Failed to add clip element: " + gameElement.elementName, Color.red);
return false;
}
// 更新附加元素
@@ -381,7 +419,31 @@ namespace Ichni
}
gameElement.ExecuteBM();
if (gameElement.matchedElement == null)
{
LogWindow.Log("Failed to generate clip element: " + gameElement.elementName, Color.red);
return false;
}
loadedElements.Add(gameElement.matchedElement);
return true;
}
private bool ValidateClipName(string clipName)
{
if (string.IsNullOrWhiteSpace(clipName))
{
LogWindow.Log("Clip name cannot be empty.", Color.red);
return false;
}
if (clipName.IndexOfAny(Path.GetInvalidFileNameChars()) >= 0)
{
LogWindow.Log("Clip name contains invalid characters.", Color.red);
return false;
}
return true;
}
}