2025-09-25 19:04:05 +08:00
|
|
|
using Convention;
|
|
|
|
|
using Convention.WindowsUI;
|
|
|
|
|
using Convention.WindowsUI.Variant;
|
|
|
|
|
using Demo.Game;
|
2025-11-28 17:35:24 +08:00
|
|
|
using Dreamteck.Splines;
|
2025-11-27 17:18:30 +08:00
|
|
|
using System;
|
|
|
|
|
using System.Collections;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.IO;
|
2025-09-25 19:04:05 +08:00
|
|
|
using UnityEngine;
|
|
|
|
|
using UnityEngine.InputSystem;
|
|
|
|
|
using UnityEngine.SceneManagement;
|
|
|
|
|
|
|
|
|
|
namespace Demo.Editor
|
|
|
|
|
{
|
2025-11-28 17:35:24 +08:00
|
|
|
[DefaultExecutionOrder(-9999)]
|
2025-09-25 19:04:05 +08:00
|
|
|
public class EditorController : MonoSingleton<EditorController>
|
|
|
|
|
{
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 必须是-1至0
|
|
|
|
|
/// </summary>
|
|
|
|
|
public const float TimelineUVOffset = -0.1f;
|
|
|
|
|
public override bool IsDontDestroyOnLoad => false;
|
|
|
|
|
|
|
|
|
|
[Resources] public WindowManager ToolsWindow;
|
|
|
|
|
[Resources] public ModernUIFillBar TotalTimelineBar;
|
|
|
|
|
[Resources] public Text CurrentTimeText;
|
|
|
|
|
[Resources] public Text CurrentFPS;
|
|
|
|
|
[Setting] public const string SceneName = "GameScene";
|
|
|
|
|
[Setting] public bool IsLowPerformance = false;
|
|
|
|
|
|
|
|
|
|
[Content] public string LastLoadProjectName = "";
|
|
|
|
|
|
|
|
|
|
public GameController MainGameController;
|
|
|
|
|
|
|
|
|
|
[Header("Spectrum")]
|
|
|
|
|
[Resources] public UnityEngine.UI.RawImage SpectrumRawImage;
|
|
|
|
|
[Resources] public ModernUIFillBar SpectrumSeeline;
|
|
|
|
|
[Setting] public float SpectrumRenderTP = 0.1f;
|
|
|
|
|
[Setting] public ModernUIFillBar SongPlaySpeedBar;
|
|
|
|
|
private float[][] spectrums = null;
|
|
|
|
|
private float spectrumDataMax;
|
|
|
|
|
private Color[] blank = null;
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// TODO: 替换为Dict<Tex>纹理组,这将用来支持更高的精度
|
|
|
|
|
/// </summary>
|
|
|
|
|
private Texture2D SpectrumRenderTexture;
|
2025-11-28 17:35:24 +08:00
|
|
|
private bool IsEnableUpdateSpectrumRenderTexture = false;
|
2025-09-25 19:04:05 +08:00
|
|
|
|
|
|
|
|
private Color backgroundColor = new(0, 0, 0, 0);
|
|
|
|
|
private Color waveformColor = new(1, 1, 1, 1);
|
|
|
|
|
private Color waveformTpColor = new(1, 0, 0, 1);
|
|
|
|
|
|
|
|
|
|
[Resources, SerializeField] private PropertiesWindow TimelineScriptParentWindow;
|
|
|
|
|
public static Dictionary<string, Type> GetDefaultScriptableObjectTypen()
|
|
|
|
|
{
|
|
|
|
|
Dictionary<string, Type> result = new();
|
|
|
|
|
foreach (var asm in AppDomain.CurrentDomain.GetAssemblies())
|
|
|
|
|
{
|
|
|
|
|
foreach (var type in asm.GetTypes())
|
|
|
|
|
{
|
|
|
|
|
if (typeof(ScriptableObject).IsAssignableFrom(type) && type.IsAbstract == false)
|
|
|
|
|
{
|
|
|
|
|
result.Add(type.Name, type);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Header("BPM")]
|
|
|
|
|
[Resources] public BPMLine BPMLinePrefab;
|
|
|
|
|
[Resources] public Transform BPMLineParentTransform;
|
|
|
|
|
|
|
|
|
|
[Content, SerializeField] private List<BPMLine> BPMLineEntries = new();
|
|
|
|
|
[Content] public int BPMFraction = 4;
|
2025-10-04 23:09:46 +08:00
|
|
|
[Content] public float SongOffset = 0;
|
2025-09-25 19:04:05 +08:00
|
|
|
[Content] public float BPM = 60;
|
|
|
|
|
|
2025-10-04 23:09:46 +08:00
|
|
|
private float onebarDeltaTime;
|
|
|
|
|
|
2025-09-25 19:04:05 +08:00
|
|
|
private void InjectSetSongCurrentTime(float time)
|
|
|
|
|
{
|
|
|
|
|
// 秒,可见长度,以下相同,注意并非百分比
|
|
|
|
|
var duration = SpectrumSeeline.value / TotalTimelineBar.maxValue;
|
|
|
|
|
// 这是负数
|
|
|
|
|
var leftClipFrom = SpectrumSeeline.value * TimelineUVOffset;
|
|
|
|
|
// 这是正数
|
|
|
|
|
var rightClipTo = SpectrumSeeline.value * (1 + TimelineUVOffset);
|
|
|
|
|
|
|
|
|
|
// 简单UI变更
|
|
|
|
|
void UpdateUI()
|
|
|
|
|
{
|
|
|
|
|
TotalTimelineBar.SetValue(time);
|
|
|
|
|
CurrentTimeText.text = time.ToString();
|
|
|
|
|
SpectrumRawImage.uvRect = new(TotalTimelineBar.currentPercent + TimelineUVOffset * duration, 0, duration, 1);
|
|
|
|
|
// 传递时间切片
|
|
|
|
|
UI.TimelineItem.clip = new(leftClipFrom + time, rightClipTo + time);
|
|
|
|
|
}
|
2025-11-28 01:16:00 +08:00
|
|
|
using (Profiler.BeginZone(nameof(UpdateUI)))
|
|
|
|
|
UpdateUI();
|
2025-09-25 19:04:05 +08:00
|
|
|
|
|
|
|
|
if (IsLowPerformance)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (MainGameController == null || MainGameController.MainAudio == null)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
// 绘制时频
|
|
|
|
|
void UpdateSpectrumRenderTexture()
|
|
|
|
|
{
|
|
|
|
|
if (spectrums != null &&
|
|
|
|
|
MainGameController.MainAudio.IsPlaying() &&
|
|
|
|
|
MainGameController.MainAudio is Convention.AudioSystem audio)
|
|
|
|
|
{
|
|
|
|
|
if (Mathf.Approximately(1, SongPlaySpeedBar.Value) == false)
|
|
|
|
|
return;
|
|
|
|
|
// Render Current Data
|
|
|
|
|
int x = (int)(TotalTimelineBar.currentPercent * SpectrumRenderTexture.width);
|
|
|
|
|
spectrums[x] = new float[SpectrumRenderTexture.height];
|
|
|
|
|
audio.Source.GetSpectrumData(spectrums[x], 0, FFTWindow.Rectangular);
|
|
|
|
|
var max = Mathf.Max(spectrums[x]);
|
|
|
|
|
for (int y = 0; y < spectrums[x].Length - 1; y++)
|
|
|
|
|
{
|
|
|
|
|
var et = Mathf.Pow(spectrums[x][y] / max, 0.5f);
|
|
|
|
|
if (SpectrumRenderTP < 1 - et)
|
|
|
|
|
{
|
|
|
|
|
SpectrumRenderTexture.SetPixel(x, y, Color.Lerp(backgroundColor, waveformColor, et / (1 - SpectrumRenderTP)));
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
SpectrumRenderTexture.SetPixel(x, y, Color.Lerp(waveformColor, waveformTpColor, (et - SpectrumRenderTP) / (SpectrumRenderTP)));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// Render Forward Null
|
|
|
|
|
int ie = x - 1;
|
|
|
|
|
while (ie > 0 && (spectrums[ie] == null || spectrums[ie].Length == 0))
|
|
|
|
|
{
|
|
|
|
|
ie--;
|
|
|
|
|
}
|
|
|
|
|
if (ie != 0 && ie != x - 1)
|
|
|
|
|
{
|
|
|
|
|
for (int ix = x; ix > ie; ix--)
|
|
|
|
|
{
|
|
|
|
|
for (int y = 0; y < spectrums[x].Length - 1; y++)
|
|
|
|
|
{
|
|
|
|
|
SpectrumRenderTexture.SetPixel(ix, y,
|
|
|
|
|
Color.Lerp(SpectrumRenderTexture.GetPixel(ie, y), SpectrumRenderTexture.GetPixel(x, y), (ix - ie) / (float)(x - ie)));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
SpectrumRenderTexture.Apply();
|
|
|
|
|
SpectrumRawImage.texture = SpectrumRenderTexture;
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-11-28 17:35:24 +08:00
|
|
|
|
|
|
|
|
if (IsEnableUpdateSpectrumRenderTexture)
|
|
|
|
|
using (Profiler.BeginZone(nameof(UpdateSpectrumRenderTexture)))
|
|
|
|
|
UpdateSpectrumRenderTexture();
|
2025-09-25 19:04:05 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void InjectSongLoadOverCallback(BasicAudioSystem audio)
|
|
|
|
|
{
|
|
|
|
|
// Speed
|
|
|
|
|
SongPlaySpeedBar.OnTransValueChange.AddListener(x =>
|
|
|
|
|
{
|
|
|
|
|
audio.SetSpeed(x);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (IsLowPerformance)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
// BPM
|
|
|
|
|
BPM = (float)MainGameController.MainConfig.FindItem(nameof(BPM), BPM);
|
2025-10-04 23:09:46 +08:00
|
|
|
onebarDeltaTime = 60.0f / (BPM * BPMFraction);
|
2025-09-25 19:04:05 +08:00
|
|
|
BPMFraction = (int)MainGameController.MainConfig.FindItem(nameof(BPMFraction), BPMFraction);
|
2025-10-04 23:09:46 +08:00
|
|
|
SongOffset = (float)MainGameController.MainConfig.FindItem(nameof(SongOffset), SongOffset);
|
2025-09-25 19:04:05 +08:00
|
|
|
|
|
|
|
|
// 绘制时频
|
|
|
|
|
var texturePath = (string)MainGameController.MainConfig.FindItem(nameof(SpectrumRenderTexture), null);
|
|
|
|
|
|
|
|
|
|
if (audio is Convention.AudioSystem caudioSystem)
|
|
|
|
|
{
|
|
|
|
|
int width = 16384;
|
|
|
|
|
int height = 64;
|
|
|
|
|
blank = new Color[width * height];
|
|
|
|
|
Texture2D texture = new(width, height);
|
|
|
|
|
for (int i = 0; i < blank.Length; ++i)
|
|
|
|
|
{
|
|
|
|
|
blank[i] = backgroundColor;
|
|
|
|
|
}
|
|
|
|
|
texture.SetPixels(blank, 0);
|
|
|
|
|
texture.Apply();
|
|
|
|
|
SpectrumRenderTexture = texture;
|
|
|
|
|
spectrums = new float[width][];
|
|
|
|
|
SpectrumRawImage.texture = texture;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
IEnumerator Foo()
|
|
|
|
|
{
|
|
|
|
|
int width = 4096 * 4;
|
|
|
|
|
int height = 1024;
|
|
|
|
|
var _clip = audio.CurrentClip;
|
|
|
|
|
int resolution = 240; // 这个值可以控制频谱的密度
|
|
|
|
|
|
|
|
|
|
resolution = _clip.frequency / resolution;
|
|
|
|
|
|
|
|
|
|
float[] samples = new float[_clip.samples * _clip.channels];
|
|
|
|
|
_clip.GetData(samples, 0);
|
|
|
|
|
|
|
|
|
|
float[] waveForm = new float[(samples.Length / resolution)];
|
|
|
|
|
|
|
|
|
|
float min = 0;
|
|
|
|
|
float max = 0;
|
|
|
|
|
bool inited = false;
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < waveForm.Length; i++)
|
|
|
|
|
{
|
|
|
|
|
waveForm[i] = 0;
|
|
|
|
|
|
|
|
|
|
for (int j = 0; j < resolution; j++)
|
|
|
|
|
{
|
|
|
|
|
waveForm[i] += Mathf.Abs(samples[(i * resolution) + j]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!inited)
|
|
|
|
|
{
|
|
|
|
|
min = waveForm[i];
|
|
|
|
|
max = waveForm[i];
|
|
|
|
|
inited = true;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (waveForm[i] < min)
|
|
|
|
|
{
|
|
|
|
|
min = waveForm[i];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (waveForm[i] > max)
|
|
|
|
|
{
|
|
|
|
|
max = waveForm[i];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
//waveForm[i] /= resolution;
|
|
|
|
|
if (i % 100 == 0)
|
|
|
|
|
yield return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
spectrumDataMax = max;
|
|
|
|
|
|
|
|
|
|
PriorityQueue<float> tpQueue = new();
|
|
|
|
|
var maxQueueSize = SpectrumRenderTP * waveForm.Length;
|
|
|
|
|
|
|
|
|
|
foreach (var value in waveForm)
|
|
|
|
|
{
|
|
|
|
|
tpQueue.Enqueue(value);
|
|
|
|
|
}
|
|
|
|
|
var thValue = tpQueue.GetTP(SpectrumRenderTP);
|
|
|
|
|
|
|
|
|
|
blank = new Color[width * height];
|
|
|
|
|
Texture2D texture = new Texture2D(width, height);
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < blank.Length; ++i)
|
|
|
|
|
{
|
|
|
|
|
blank[i] = backgroundColor;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
texture.SetPixels(blank, 0);
|
|
|
|
|
|
|
|
|
|
float xScale = (float)width / (float)waveForm.Length;
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < waveForm.Length; ++i)
|
|
|
|
|
{
|
|
|
|
|
int x = (int)(i * xScale);
|
|
|
|
|
int yOffset = (int)(waveForm[i] / max * height);
|
|
|
|
|
int startY = 0;
|
|
|
|
|
int endY = yOffset;
|
|
|
|
|
|
|
|
|
|
for (int y = startY; y <= endY; ++y)
|
|
|
|
|
{
|
|
|
|
|
if (waveForm[i] > thValue)
|
|
|
|
|
texture.SetPixel(x, y, waveformTpColor);
|
|
|
|
|
else
|
|
|
|
|
texture.SetPixel(x, y, waveformColor);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (i % 100 == 0)
|
|
|
|
|
yield return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
texture.Apply();
|
|
|
|
|
SpectrumRenderTexture = texture;
|
|
|
|
|
SpectrumRawImage.texture = texture;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
StartCoroutine(Foo());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (spectrumDataMax == 0)
|
|
|
|
|
{
|
|
|
|
|
var buffer = new float[SpectrumRenderTexture.width];
|
|
|
|
|
audio.CurrentClip.GetData(buffer, 0);
|
|
|
|
|
spectrumDataMax = Mathf.Max(buffer);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-02 11:11:42 +08:00
|
|
|
//[Header("PersistentDataPath")]
|
|
|
|
|
public string PersistentDataPath => PlatformIndicator.PersistentDataPath;
|
|
|
|
|
public string PersistentProjectPath => Path.Combine(PersistentDataPath, "Projects/");
|
|
|
|
|
public string PersistentHelperPath => Path.Combine(PersistentDataPath, "Helper/");
|
|
|
|
|
|
2025-09-25 19:04:05 +08:00
|
|
|
private void OpenGameScene(string ProjectName, bool IsCreateNewProject)
|
|
|
|
|
{
|
|
|
|
|
if (GameContent.instance == null)
|
|
|
|
|
{
|
|
|
|
|
new GameObject("GameContent").AddComponent<GameContent>();
|
|
|
|
|
}
|
|
|
|
|
var content = GameContent.instance;
|
2025-10-02 11:11:42 +08:00
|
|
|
content.RootSourceDir = Path.Combine(PersistentProjectPath, ProjectName) + "/";
|
2025-09-25 19:04:05 +08:00
|
|
|
content.IsCreateNewProject = IsCreateNewProject;
|
|
|
|
|
content.ScriptableObjectTypen = GetDefaultScriptableObjectTypen();
|
|
|
|
|
content.IsAutoPlay = true;
|
|
|
|
|
content.SetupSongDuration = (x, y) =>
|
|
|
|
|
{
|
|
|
|
|
TotalTimelineBar.minValue = x;
|
|
|
|
|
TotalTimelineBar.maxValue = y;
|
|
|
|
|
if (MainGameController != null && MainGameController.MainAudio.CurrentClip != null)
|
|
|
|
|
MainGameController.ForceScriptUpdate();
|
|
|
|
|
};
|
|
|
|
|
content.SetSongCurrentTime = InjectSetSongCurrentTime;
|
|
|
|
|
content.SongLoadOverCallback = InjectSongLoadOverCallback;
|
|
|
|
|
SceneManager.LoadSceneAsync(SceneName, LoadSceneMode.Additive).completed += x =>
|
|
|
|
|
{
|
|
|
|
|
LastLoadProjectName = ProjectName;
|
|
|
|
|
StopRefreshFlag = false;
|
2025-11-25 17:04:19 +08:00
|
|
|
MainGameController = FindFirstObjectByType<GameController>();
|
2025-09-25 19:04:05 +08:00
|
|
|
MainGameController.IsMain = true;
|
|
|
|
|
StartCoroutine(MainGameController.GameInit());
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private IEnumerator CloseGameScene()
|
|
|
|
|
{
|
|
|
|
|
// InjectSongLoadOverCallback中注册了一个回调
|
|
|
|
|
SongPlaySpeedBar.OnTransValueChange.RemoveAllListeners();
|
|
|
|
|
// 清理一下图片,这总是合理的
|
|
|
|
|
SpectrumRenderTexture = null;
|
|
|
|
|
// 判断一下有没有
|
|
|
|
|
if (MainGameController == null)
|
|
|
|
|
yield break;
|
|
|
|
|
yield return MainGameController.GameExit();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private IEnumerator WaitForSharedModule()
|
|
|
|
|
{
|
|
|
|
|
while (SharedModule.instance == null)
|
|
|
|
|
yield return null;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
SharedModule.instance.OpenCustomMenu(Architecture.Get<HierarchyWindow>().transform as RectTransform,
|
|
|
|
|
new SharedModule.CallbackData("Open Project", _ =>
|
|
|
|
|
{
|
|
|
|
|
SharedModule.instance.SingleEditString("Open Project", "", (ProjectName) =>
|
|
|
|
|
{
|
|
|
|
|
OpenGameScene(ProjectName, false);
|
|
|
|
|
});
|
|
|
|
|
}),
|
|
|
|
|
new SharedModule.CallbackData("Create New Project", _ =>
|
|
|
|
|
{
|
|
|
|
|
SharedModule.instance.SingleEditString("Create New Project", "", (ProjectName) =>
|
|
|
|
|
{
|
|
|
|
|
OpenGameScene(ProjectName, true);
|
|
|
|
|
});
|
|
|
|
|
}));
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-28 17:35:24 +08:00
|
|
|
private void RegisterVariableGenerater()
|
2025-09-25 19:04:05 +08:00
|
|
|
{
|
2025-10-30 15:39:33 +08:00
|
|
|
// Generate Framework
|
2025-11-28 17:35:24 +08:00
|
|
|
var generaters = DefaultInstantiate.GetScriptableObjectInstantiate();
|
|
|
|
|
foreach (var asm in AppDomain.CurrentDomain.GetAssemblies())
|
2025-10-30 15:39:33 +08:00
|
|
|
{
|
2025-11-28 17:35:24 +08:00
|
|
|
foreach (var type in asm.GetTypes())
|
2025-10-30 15:39:33 +08:00
|
|
|
{
|
2025-11-28 17:35:24 +08:00
|
|
|
string filename = Convention.RScript.Variable.CStyle.CScriptRScriptVariableGenerater.GetTypename(type);
|
|
|
|
|
if (Convention.RScript.Variable.RScriptInjectVariableGenerater.AllRScriptInjectVariables.ContainsKey(filename))
|
|
|
|
|
continue;
|
|
|
|
|
if (generaters.TryGetValue(filename, out var generater))
|
2025-10-30 15:39:33 +08:00
|
|
|
{
|
2025-11-28 17:35:24 +08:00
|
|
|
new Convention.RScript.Variable.CStyle.CScriptRScriptVariableGenerater(type, () => generater(), null, filename).Register();
|
|
|
|
|
}
|
|
|
|
|
else if (typeof(ScriptableObject).IsAssignableFrom(type))
|
|
|
|
|
{
|
|
|
|
|
new Convention.RScript.Variable.CStyle.CScriptRScriptVariableGenerater(type, null, null, filename).Register();
|
2025-10-30 15:39:33 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-11-28 17:35:24 +08:00
|
|
|
new Convention.RScript.Variable.CStyle.CScriptRScriptVariableGenerater(typeof(MathExtension.EaseCurveType), null, null,
|
|
|
|
|
Convention.RScript.Variable.CStyle.CScriptRScriptVariableGenerater.GetTypename(typeof(MathExtension.EaseCurveType))).Register();
|
|
|
|
|
new Convention.RScript.Variable.CStyle.CScriptRScriptVariableGenerater(typeof(SplineComputer.SampleMode), null, null,
|
|
|
|
|
Convention.RScript.Variable.CStyle.CScriptRScriptVariableGenerater.GetTypename(typeof(SplineComputer.SampleMode))).Register();
|
|
|
|
|
new Convention.RScript.Variable.CStyle.CScriptRScriptVariableGenerater(typeof(Spline.Type), null, null,
|
|
|
|
|
Convention.RScript.Variable.CStyle.CScriptRScriptVariableGenerater.GetTypename(typeof(Spline.Type))).Register();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void Start()
|
|
|
|
|
{
|
|
|
|
|
Profiler.AppInfo(Application.productName);
|
|
|
|
|
|
|
|
|
|
GlobalConfig.ConstConfigFile = "config.easysave";
|
|
|
|
|
RegisterVariableGenerater();
|
2025-10-30 15:39:33 +08:00
|
|
|
|
2025-09-25 19:04:05 +08:00
|
|
|
// Helper Files
|
2025-10-02 11:11:42 +08:00
|
|
|
ToolFile helperHeaderDir = new ToolFile(PersistentHelperPath);
|
2025-11-12 10:09:26 +08:00
|
|
|
helperHeaderDir.MustExistsPath();
|
|
|
|
|
foreach (var (name, variable) in Convention.RScript.Variable.RScriptInjectVariableGenerater.AllRScriptInjectVariables)
|
2025-09-25 19:04:05 +08:00
|
|
|
{
|
2025-11-12 10:09:26 +08:00
|
|
|
(helperHeaderDir | name).SaveAsText(variable.scriptIndicator);
|
2025-09-25 19:04:05 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Reset
|
|
|
|
|
TimelineScriptObject.TimelineWindow = this.TimelineScriptParentWindow;
|
|
|
|
|
|
|
|
|
|
string ProjectName = "";
|
|
|
|
|
foreach (var arg in Environment.GetCommandLineArgs())
|
|
|
|
|
{
|
|
|
|
|
if (arg.StartsWith("-p="))
|
|
|
|
|
{
|
|
|
|
|
ProjectName = arg["-p=".Length..];
|
|
|
|
|
}
|
|
|
|
|
else if (arg.StartsWith("-project="))
|
|
|
|
|
{
|
|
|
|
|
ProjectName = arg["-project=".Length..];
|
|
|
|
|
}
|
|
|
|
|
else if (arg == "-nfs")
|
|
|
|
|
{
|
|
|
|
|
Screen.fullScreen = false;
|
|
|
|
|
}
|
2025-11-12 10:09:26 +08:00
|
|
|
else if (arg == "-fs")
|
2025-09-25 19:04:05 +08:00
|
|
|
{
|
|
|
|
|
Screen.fullScreen = true;
|
|
|
|
|
}
|
|
|
|
|
else if (arg.StartsWith("-scale="))
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
var item = arg["-scale=".Length..].Split(',');
|
|
|
|
|
int x = int.Parse(item[0]);
|
|
|
|
|
int y = int.Parse(item[1]);
|
|
|
|
|
Screen.SetResolution(x, y, false);
|
|
|
|
|
}
|
|
|
|
|
catch
|
|
|
|
|
{
|
|
|
|
|
Screen.fullScreen = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-11-12 10:09:26 +08:00
|
|
|
else if (arg == "-lp")
|
2025-09-25 19:04:05 +08:00
|
|
|
{
|
|
|
|
|
IsLowPerformance = true;
|
|
|
|
|
}
|
2025-11-28 17:35:24 +08:00
|
|
|
else if (arg == "-UpdateSpectrumRenderTexture")
|
|
|
|
|
{
|
|
|
|
|
IsEnableUpdateSpectrumRenderTexture = true;
|
|
|
|
|
}
|
2025-09-25 19:04:05 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private bool StopRefreshFlag = true;
|
|
|
|
|
|
|
|
|
|
public void ReloadCurrentProject()
|
|
|
|
|
{
|
|
|
|
|
StopRefreshFlag = true;
|
|
|
|
|
|
|
|
|
|
IEnumerator Foo()
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
yield return CloseGameScene();
|
|
|
|
|
}
|
|
|
|
|
finally
|
|
|
|
|
{
|
|
|
|
|
OpenGameScene(LastLoadProjectName, false);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
StartCoroutine(Foo());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void CloseCurrentProject()
|
|
|
|
|
{
|
|
|
|
|
StopRefreshFlag = true;
|
|
|
|
|
LastLoadProjectName = "";
|
|
|
|
|
|
|
|
|
|
IEnumerator Foo()
|
|
|
|
|
{
|
|
|
|
|
yield return CloseGameScene();
|
|
|
|
|
yield return WaitForSharedModule();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
StartCoroutine(Foo());
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-27 17:18:30 +08:00
|
|
|
#region Performance Monitoring
|
|
|
|
|
|
|
|
|
|
private void MonitorFrameRate()
|
|
|
|
|
{
|
|
|
|
|
float fps = 1.0f / Time.unscaledDeltaTime;
|
|
|
|
|
float frameTime = Time.unscaledDeltaTime * 1000.0f;
|
|
|
|
|
|
|
|
|
|
Profiler.Plot("FPS", fps);
|
|
|
|
|
Profiler.Plot("Frame Time (ms)", frameTime);
|
|
|
|
|
Profiler.Plot("Time Scale", Time.timeScale);
|
|
|
|
|
}
|
|
|
|
|
private void MonitorMemory()
|
|
|
|
|
{
|
|
|
|
|
// GC 内存
|
|
|
|
|
long totalMemory = System.GC.GetTotalMemory(false);
|
|
|
|
|
Profiler.Plot("GC Memory (MB)", totalMemory / (1024.0 * 1024.0));
|
|
|
|
|
|
|
|
|
|
// Unity Profiler 内存统计
|
|
|
|
|
long usedHeap = UnityEngine.Profiling.Profiler.usedHeapSizeLong;
|
|
|
|
|
long totalAllocated = UnityEngine.Profiling.Profiler.GetTotalAllocatedMemoryLong();
|
|
|
|
|
long totalReserved = UnityEngine.Profiling.Profiler.GetTotalReservedMemoryLong();
|
|
|
|
|
|
|
|
|
|
Profiler.Plot("Used Heap (MB)", usedHeap / (1024.0 * 1024.0));
|
|
|
|
|
Profiler.Plot("Total Allocated (MB)", totalAllocated / (1024.0 * 1024.0));
|
|
|
|
|
Profiler.Plot("Total Reserved (MB)", totalReserved / (1024.0 * 1024.0));
|
|
|
|
|
|
|
|
|
|
// 纹理内存
|
|
|
|
|
Profiler.Plot("Texture Memory (MB)", UnityEngine.Profiling.Profiler.GetAllocatedMemoryForGraphicsDriver() / (1024.0 * 1024.0));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
2025-09-25 19:04:05 +08:00
|
|
|
private void Update()
|
|
|
|
|
{
|
2025-11-27 17:18:30 +08:00
|
|
|
MonitorMemory();
|
|
|
|
|
MonitorFrameRate();
|
2025-09-25 19:04:05 +08:00
|
|
|
|
2025-11-27 17:18:30 +08:00
|
|
|
using (Profiler.BeginZone("EditorController.Update"))
|
2025-09-25 19:04:05 +08:00
|
|
|
{
|
2025-11-27 17:18:30 +08:00
|
|
|
CurrentFPS.text = $"{1 / Time.smoothDeltaTime}";
|
|
|
|
|
|
2025-09-25 19:04:05 +08:00
|
|
|
if (string.IsNullOrEmpty(LastLoadProjectName))
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (MainGameController == null ||
|
|
|
|
|
MainGameController.MainAudio == null ||
|
|
|
|
|
MainGameController.MainAudio.CurrentClip == null)
|
|
|
|
|
{
|
|
|
|
|
foreach (var item in BPMLineEntries)
|
|
|
|
|
{
|
|
|
|
|
item.gameObject.SetActive(false);
|
|
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
// 秒,可见长度,以下相同,注意并非百分比
|
|
|
|
|
var duration = SpectrumSeeline.value / MainGameController.MainAudio.CurrentClip.length;
|
|
|
|
|
// 这是负数
|
|
|
|
|
var leftClipFrom = SpectrumSeeline.value * TimelineUVOffset;
|
|
|
|
|
// 这是正数
|
|
|
|
|
var rightClipTo = SpectrumSeeline.value * (1 + TimelineUVOffset);
|
|
|
|
|
|
|
|
|
|
var time = MainGameController.CurrentTime;
|
|
|
|
|
|
|
|
|
|
int BPMLineEntriesIndex = 0;
|
|
|
|
|
float farAplha = (1 - SpectrumSeeline.currentPercent) * 0.5f + 0.5f;
|
|
|
|
|
float foucs = Mathf.CeilToInt((time + leftClipFrom ) / onebarDeltaTime) * onebarDeltaTime;
|
|
|
|
|
for (float end = time + rightClipTo; foucs + BPMLineEntriesIndex * onebarDeltaTime < end; BPMLineEntriesIndex++)
|
|
|
|
|
{
|
|
|
|
|
if (BPMLineEntries.Count <= BPMLineEntriesIndex)
|
|
|
|
|
{
|
|
|
|
|
var newLine = Instantiate(BPMLinePrefab, BPMLineParentTransform);
|
|
|
|
|
BPMLineEntries.Add(newLine);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
BPMLineEntries[BPMLineEntriesIndex].Setup(foucs + BPMLineEntriesIndex * onebarDeltaTime,
|
|
|
|
|
time,
|
|
|
|
|
onebarDeltaTime,
|
|
|
|
|
farAplha,
|
|
|
|
|
new Vector2(time + leftClipFrom, time + rightClipTo));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
for (; BPMLineEntriesIndex < BPMLineEntries.Count; BPMLineEntriesIndex++)
|
|
|
|
|
{
|
|
|
|
|
BPMLineEntries[BPMLineEntriesIndex].gameObject.SetActive(false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (Keyboard.current[Key.LeftCtrl].isPressed)
|
|
|
|
|
{
|
|
|
|
|
if (Keyboard.current[Key.R].wasPressedThisFrame && StopRefreshFlag == false)
|
|
|
|
|
{
|
|
|
|
|
ReloadCurrentProject();
|
|
|
|
|
}
|
|
|
|
|
else if (Keyboard.current[Key.Tab].wasPressedThisFrame)
|
|
|
|
|
{
|
|
|
|
|
CloseCurrentProject();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-11-27 17:18:30 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void LateUpdate()
|
|
|
|
|
{
|
|
|
|
|
Profiler.EmitFrameMark();
|
2025-09-25 19:04:05 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ToolTable
|
|
|
|
|
|
|
|
|
|
public void ToolIOpenProject()
|
|
|
|
|
{
|
|
|
|
|
SharedModule.instance.SingleEditString("Open Project", "Project", x =>
|
|
|
|
|
{
|
|
|
|
|
IEnumerator Foo()
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
yield return CloseGameScene();
|
|
|
|
|
}
|
|
|
|
|
finally
|
|
|
|
|
{
|
|
|
|
|
OpenGameScene(x, false);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
StartCoroutine(Foo());
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void ToolCreateProject()
|
|
|
|
|
{
|
|
|
|
|
SharedModule.instance.SingleEditString("Create New Project", "New Project", x =>
|
|
|
|
|
{
|
|
|
|
|
IEnumerator Foo()
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
yield return CloseGameScene();
|
|
|
|
|
}
|
|
|
|
|
finally
|
|
|
|
|
{
|
|
|
|
|
OpenGameScene(x, true);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
StartCoroutine(Foo());
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Header("Tools Button")]
|
|
|
|
|
[Resources] public UnityEngine.UI.Button ToolSongTimeButton;
|
|
|
|
|
|
|
|
|
|
public void ToolCreateSongModule()
|
|
|
|
|
{
|
|
|
|
|
if (MainGameController == null)
|
|
|
|
|
return;
|
|
|
|
|
if (MainGameController.MainAudio == null)
|
|
|
|
|
return;
|
|
|
|
|
if (MainGameController.MainAudio.CurrentClip == null)
|
|
|
|
|
return;
|
|
|
|
|
SharedModule.instance.OpenCustomMenu(ToolSongTimeButton.transform as RectTransform,
|
|
|
|
|
new SharedModule.CallbackData("Play(P)", _ => MainGameController.Play()),
|
|
|
|
|
new SharedModule.CallbackData("Pause(P)", _ => MainGameController.Pause()),
|
|
|
|
|
new SharedModule.CallbackData("Stop(S)", _ => MainGameController.Stop())
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Resources] public UnityEngine.UI.Button CameraPositionButton;
|
|
|
|
|
[Resources] public UnityEngine.UI.Button CameraRotationButton;
|
|
|
|
|
[Resources] public FreeSceneCamera SceneVirtualCamera;
|
|
|
|
|
|
|
|
|
|
public void ToolCreateInitCameraModule()
|
|
|
|
|
{
|
|
|
|
|
if (MainGameController != null)
|
|
|
|
|
{
|
|
|
|
|
if (MainGameController.MainAudio.IsPlaying())
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
SharedModule.instance.OpenCustomMenu(ToolSongTimeButton.transform as RectTransform,
|
|
|
|
|
new SharedModule.CallbackData("Reset Rotation(LeftShift)", _ => SceneVirtualCamera.transform.rotation = Quaternion.identity),
|
|
|
|
|
new SharedModule.CallbackData("Reset(LeftShift+Z)", _ => SceneVirtualCamera.transform.SetLocalPositionAndRotation(
|
|
|
|
|
Vector3.zero, Quaternion.identity)));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|