using System; using System.Collections; using System.Collections.Generic; using Ichni.Editor; using Ichni.RhythmGame; using Ichni.RhythmGame.Beatmap; using Sirenix.OdinInspector; using SLSUtilities.General; using UnityEngine; namespace Ichni { /// /// 谱面数据容器。 /// 继承自 GameElement,作为编辑器中"游戏世界"的根节点, /// 持有 projectInformation / songInformation / beatmapContainer / commandScripts /// 等所有与谱面/游戏内容直接相关的数据。 /// 由 EditorManager 在场景中持有引用,并通过 EditorManager 的转发属性对外暴露, /// 以实现零调用点改动的架构迁移。 /// public class ProjectContainer : GameElement { #region [单例] Singleton private static ProjectContainer _instance; public static ProjectContainer instance => _instance != null ? _instance : _instance = FindFirstObjectByType(); #endregion #region [谱面数据] Project Data public ProjectInformation projectInformation; public SongInformation songInformation; public BeatmapContainer beatmapContainer; public CommandScripts commandScripts; #endregion #region [游戏全局元素] Global Game Elements [Title("Runtime Global Elements")] public VariablesContainer variablesContainer; public BackgroundSetter backgroundSetter; #endregion #region [编辑器首选项] Editor Preferences // 这些首选项字段放在 ProjectContainer 中,以便 SetUpInspector 将 this 作为 IBaseElement owner 传给 GenerateToggle public NoteBase.NoteJudgeType currentJudgeType; public bool useClickSelect = true; public bool useNotePrefab = true; public bool useQuickMove = false; #endregion #region [生成与初始化] Generation & Initialization private void Awake() { _instance = this; } #endregion #region [编辑器界面] Inspector public override void SetUpInspector() { InspectorBuilder.For(this) .Section("Editor Manager") .Dropdown(nameof(currentJudgeType), typeof(NoteBase.NoteJudgeType), "Judge Type") .OnChanged(() => { foreach (GameElement gameElement in beatmapContainer.gameElementList) { if (gameElement is NoteVisualBase noteVisual) noteVisual.Recover(); } }) .Toggle(nameof(useNotePrefab), "Use Note Prefab") .Toggle(nameof(useClickSelect), "Use Click Select") .Toggle(nameof(useQuickMove), "Use Quick Move") .Button("Generate Folder", () => ElementFolder.GenerateElement("Folder", Guid.NewGuid(), new List(), true, null)) .Button("Generate Background Setter", () => BackgroundSetter.GenerateElement("Background Setter", Guid.NewGuid(), new List(), true, null, false, "basic", "Skybox", "Background")) .Button("Generate Variables Container", () => VariablesContainer.GenerateElement("Variables Container", Guid.NewGuid(), new List(), true, null, new Dictionary())) .Build(); projectInformation.SetUpInspector(); songInformation.SetUpInspector(); EditorManager.instance.cameraManager.SetUpInspector(); // Grid 设置使用 RawSection 处理无绑定 Toggle/InputField 交互 var gridToggleRef = new ElementRef(); var gridSizeRef = new ElementRef(); var showCoordsRef = new ElementRef(); InspectorBuilder.For(this) .RawSection("Grid", int.MaxValue, (insp, container) => { var p = container.GenerateSubcontainer(3); var gridToggle = insp.GenerateToggle(this, p, "Enable Grid"); gridToggle.AddListenerFunction(() => { EditorManager.instance.gridController.gameObject.SetActive(gridToggle.toggle.isOn); }); var gridSizeInput = insp.GenerateInputField(p, "Grid Size", (EditorManager.instance.gridController.baseGridSize * 10).ToString()); gridSizeInput.AddListenerFunction(() => { EditorManager.instance.gridController.baseGridSize = float.Parse(gridSizeInput.inputField.text) / 10; }); var showCoords = insp.GenerateToggle(this, p, "Show Coordinates"); showCoords.toggle.isOn = EditorManager.instance.gridController.showCoordinates; showCoords.AddListenerFunction(() => { EditorManager.instance.gridController.showCoordinates = showCoords.toggle.isOn; }); }) .Build(); } #endregion } }