Files
ichni_Creator_Studio/Assets/Scripts/Manager/EditorManager.cs

294 lines
11 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System;
using System.Collections;
using System.Collections.Generic;
using Ichni.Editor;
using Ichni.RhythmGame;
using SLSUtilities.General;
using TMPro;
using UnityEngine;
namespace Ichni
{
/// <summary>
/// 编辑器全局管理器。
/// 继承自 Singleton<EditorManager>,只持有编辑器基础设施引用:
/// 子管理器、音频播放器、UI、相机、设置、首选项等。
/// 游戏/谱面数据由 ProjectContainer 持有;
/// 此处的属性均为转发属性,保持所有外部调用点零改动。
/// </summary>
public class EditorManager : Singleton<EditorManager>
{
#region [] Singleton Alias
/// <summary>小写别名,兼容现有调用点</summary>
public new static EditorManager instance => Instance;
#endregion
#region [] Load State
public bool isLoaded;
#endregion
#region [] Editor Infrastructure Managers
public ProjectManager projectManager;
public AudioManager audioManager;
public MusicPlayer musicPlayer;
public EditorUIManager uiManager;
public EditorSettings editorSettings;
public OperationManager operationManager;
public BackgroundController backgroundController;
public SimpleGridController gridController;
public CameraManager cameraManager;
public NoteManager noteManager;
public TrackManager trackManager;
public AnimationManager animationManager;
public Canvas judgeHintCanvas;
public Canvas inspectorCanvas;
public Timeline timeline;
public PanelDrawer panelDrawer;
#endregion
#region [] Editor Preferences Forwarding Properties
// 实际字段在 ProjectContainer 中定义,此处为转发属性以保持所有调用点不变
public NoteBase.NoteJudgeType currentJudgeType
{
get => ProjectContainer.instance.currentJudgeType;
set => ProjectContainer.instance.currentJudgeType = value;
}
public bool useClickSelect
{
get => ProjectContainer.instance.useClickSelect;
set => ProjectContainer.instance.useClickSelect = value;
}
public bool useNotePrefab
{
get => ProjectContainer.instance.useNotePrefab;
set => ProjectContainer.instance.useNotePrefab = value;
}
public bool useQuickMove
{
get => ProjectContainer.instance.useQuickMove;
set => ProjectContainer.instance.useQuickMove = value;
}
#endregion
#region [] Prefab Asset Collections
public BasePrefabsCollection basePrefabs;
public Dictionary<string, CustomPrefabsCollection> customPrefabs;
public NoteAudioCollection noteAudioCollection;
#endregion
#region [ProjectContainer ] ProjectContainer Forwarding Properties
// 以下属性保持与原 EditorManager 完全相同的访问路径,
// 内部转发至 ProjectContainer无需修改任何调用点。
public ProjectInformation projectInformation
{
get => ProjectContainer.instance.projectInformation;
set => ProjectContainer.instance.projectInformation = value;
}
public SongInformation songInformation
{
get => ProjectContainer.instance.songInformation;
set => ProjectContainer.instance.songInformation = value;
}
public BeatmapContainer beatmapContainer
{
get => ProjectContainer.instance.beatmapContainer;
set => ProjectContainer.instance.beatmapContainer = value;
}
public CommandScripts commandScripts
{
get => ProjectContainer.instance.commandScripts;
set => ProjectContainer.instance.commandScripts = value;
}
public VariablesContainer variablesContainer
{
get => ProjectContainer.instance.variablesContainer;
set => ProjectContainer.instance.variablesContainer = value;
}
public BackgroundSetter backgroundSetter
{
get => ProjectContainer.instance.backgroundSetter;
set => ProjectContainer.instance.backgroundSetter = value;
}
#endregion
#region [] Lifecycle
protected override void Awake()
{
base.Awake(); // Singleton<T>.Initialize(false)
isLoaded = false;
projectManager = new ProjectManager();
operationManager = new OperationManager();
// 注册时间提供者:让编辑器的所有时间相关逻辑通过 CoreServices.TimeProvider 访问,
// 不再直接依赖 EditorManager.instance.musicPlayer
CoreServices.TimeProvider = musicPlayer;
if (!ES3.FileExists(Application.streamingAssetsPath + "/EditorSettings.es3"))
{
editorSettings = new EditorSettings(300, 3, 100, 100, 60);
EditorSettings.SaveSettings(editorSettings);
Application.targetFrameRate = editorSettings.frameRate;
}
else
{
EditorSettings.LoadSettings(ref editorSettings);
Application.targetFrameRate = editorSettings.frameRate;
}
}
private void Start()
{
StartCoroutine(StartFrameRate());
Debug.Log("EditorManager Start: Initializing UI and Loading Project...");
// ProjectContainer 自身作为根节点注册到层级视图
ProjectContainer.instance.elementName = "EditorManager";
ProjectContainer.instance.elementGuid = Guid.Empty;
uiManager.hierarchy.GenerateTab(ProjectContainer.instance, null);
ProjectContainer.instance.connectedTab.deleteButton.gameObject.SetActive(false);
if (InformationTransistor.instance.isLoadedProject)
{
LoadProject(InformationTransistor.instance.loadedProjectName);
Debug.Log("Loaded");
}
else
{
projectManager.GenerateEmptyProject(
InformationTransistor.instance.projectInfo_BM,
InformationTransistor.instance.songInfo_BM);
projectManager.saveManager.Save();
musicPlayer.audioSource.clip = songInformation.song;
Debug.Log("Generated");
}
StartCoroutine(beatmapContainer.AfterLoadSet());
isLoaded = true;
songInformation.songTime = musicPlayer.audioSource.time - songInformation.offset;
}
private void Update()
{
if (!isLoaded) return;
projectManager.autoSaveManager.UpdateAutoSave();
// 统一调度: Animation → Submodules → Track → Note
float songTime = CoreServices.TimeProvider.SongTime;
animationManager.ManualTick(songTime);
// 手动执行原本属于 UniRx 的每帧调度,消灭不可控的时序错乱
for (int i = 0; i < beatmapContainer.gameElementList.Count; i++)
{
var element = beatmapContainer.gameElementList[i];
if (element == null) continue;
if (element is IHaveTimeDurationSubmodule timeHost && !(element is NoteBase))
{
timeHost.timeDurationSubmodule?.UpdateTimeDuration(songTime);
}
if (element is IHaveDirtyMarkSubmodule dirtyHost)
{
dirtyHost.dirtyMarkSubmodule?.ExecuteDeferredRefresh();
}
if (element.gameObject.activeSelf)
{
if (element is IHaveTransformSubmodule transformHost)
{
transformHost.UpdateTransform();
}
if (element is IHaveColorSubmodule colorHost)
{
colorHost.UpdateColor();
}
}
}
trackManager.ManualTick(songTime);
noteManager.ManualTick(songTime);
}
#endregion
#region [FPS ] FPS Monitor
public float CurrentFrameRate;
public TMP_Text FPStext;
public TMP_Text UIText;
private IEnumerator StartFrameRate()
{
int frameCount = 0;
while (true)
{
CurrentFrameRate = 1f / Time.deltaTime;
if (frameCount == 2)
{
frameCount = 0;
FPStext.text = string.Format("FPS: {0:N2}", CurrentFrameRate);
}
frameCount++;
yield return null;
}
}
#endregion
#region [] Project Loading
public void LoadProject(string projectName)
{
if (!InformationTransistor.instance.isRecovery)
{
projectManager.loadManager.Load(projectName);
Debug.Log("Loaded");
}
else
{
projectManager.loadManager.LoadExport(projectName);
}
musicPlayer.audioSource.clip = songInformation.song;
beatmapContainer.gameElementList.ForEach(gameElement =>
{
gameElement.AfterInitialize();
gameElement.Refresh();
});
}
#endregion
#region [退] Application Quit
private bool isQuit = false;
[Obsolete]
public void OnApplicationQuit()
{
if (isQuit) return;
Application.CancelQuit(); // 退出拦截
GeneralSecondaryWindow QuitWindow =
Instantiate(instance.basePrefabs.generalSecondaryWindow,
instance.uiManager.mainPage.mainCanvas.GetComponent<RectTransform>())
.GetComponent<GeneralSecondaryWindow>();
QuitWindow.Initialize("Do You Want To Save?");
var container = QuitWindow.GenerateContainer("Save confirm");
var beatmapToolsSettings = container.GenerateSubcontainer(3);
QuitWindow.GenerateButton(beatmapToolsSettings, "Yes", () => SaveAndQuit());
QuitWindow.GenerateButton(beatmapToolsSettings, "No", () =>
{
isQuit = true;
Application.Quit();
});
}
async void SaveAndQuit()
{
isQuit = true;
LogWindow.Log("Start Saving...", Color.yellow);
await projectManager.saveManager.SaveAllCoroutine();
Application.Quit();
}
#endregion
}
}