using System; using System.Collections; using System.Collections.Generic; using System.Linq; using DG.Tweening; using Ichni.RhythmGame; using Sirenix.Utilities; using TMPro; using Unity.VisualScripting; using UnityEngine; using UnityEngine.InputSystem; using UnityEngine.Serialization; using UnityEngine.UI; namespace Ichni.Editor { public partial class Timeline : StaticWindow { public float songTime => EditorManager.instance.songInformation.songTime; public float songBeat => EditorManager.instance.songInformation.songBeat; public float beatmapStartTime => -EditorManager.instance.songInformation.delay; public float timePerBeat => 60f / EditorManager.instance.songInformation.bpm; public GameObject timelineTabRect; public TimePointerModule timePointerModule; public MusicPlayModule musicPlayModule; private TimePointer MarkedPointer; public TMP_InputField TimeField; public TMP_InputField BeatField; public RectTransform GetinputArea; private string BeatText; private string TimeText; public void Update() { if (EditorManager.instance.musicPlayer.isPlaying) UpdateTime(); if (RectTransformUtility.RectangleContainsScreenPoint(GetinputArea, Mouse.current.position.ReadValue())) { DetectSetRange(); DetectPointer(); } if (BeatField.text != BeatText) { BeatField.text = BeatText; } if (TimeField.text != TimeText) { TimeField.text = TimeText; } } private void DetectSetRange() { if (Keyboard.current.spaceKey.wasPressedThisFrame) { EditorManager.instance.musicPlayer.PlayMusic(); } if (Mouse.current.scroll.ReadValue().y != 0) { if (Keyboard.current.leftCtrlKey.isPressed || Keyboard.current.rightCtrlKey.isPressed) { float scrollValue = Mouse.current.scroll.ReadValue().y / 12; if (timePointerModule.timePointerInterval + scrollValue >= 30) { timePointerModule.timePointerInterval += scrollValue; timePointerModule.SortPos(); SetTimeLine(EditorManager.instance.uiManager.inspector.connectedGameElement); } } else { if (Mouse.current.scroll.ReadValue().y >= 0) { SetTime((EditorManager.instance.songInformation.songTime + timePerBeat).ToString()); } else { if (EditorManager.instance.songInformation.songTime - timePerBeat >= 0) SetTime((EditorManager.instance.songInformation.songTime - timePerBeat).ToString()); else SetTime("0"); } UpdateTime(); } } } private void DetectPointer() { if (Mouse.current.leftButton.wasPressedThisFrame) { foreach (var pointer in timePointerModule.ActivePointer) { if (RectTransformUtility.RectangleContainsScreenPoint(pointer.intervalUnitText.GetComponent(), Mouse.current.position.ReadValue())) { GUIUtility.systemCopyBuffer = pointer.intervalUnitText.text; LogWindow.Log("Copied Time: " + pointer.intervalUnitText.text + " Marked Pointer"); if (MarkedPointer != null) MarkedPointer.intervalUnitText.color = Color.white; pointer.intervalUnitText.color = Color.yellow; pointer.PlayAnim(); MarkedPointer = pointer; } } } if (Mouse.current.rightButton.wasPressedThisFrame) { foreach (var pointer in timePointerModule.ActivePointer) { if (RectTransformUtility.RectangleContainsScreenPoint(pointer.intervalUnitText.GetComponent(), Mouse.current.position.ReadValue())) { GUIUtility.systemCopyBuffer = Mathf.Abs(pointer.time - MarkedPointer.time).ToString("F3"); LogWindow.Log("Total Time: " + Mathf.Abs(pointer.time - MarkedPointer.time).ToString("F3")); pointer.PlayAnim(); pointer.intervalUnitText.color = Color.yellow; pointer.intervalUnitText.DOColor(Color.white, 0.5f); // 灰色矩形动画效果 if (MarkedPointer != null && pointer != MarkedPointer) { var rt1 = pointer.intervalUnitText.GetComponent(); var rt2 = MarkedPointer.intervalUnitText.GetComponent(); var parentRect = timePointerModule.timePointerContainer; // 世界坐标转父级本地坐标 Vector3 localPos1 = parentRect.InverseTransformPoint(rt1.position); Vector3 localPos2 = parentRect.InverseTransformPoint(rt2.position); float minX = Mathf.Min(localPos1.x, localPos2.x); float width = Mathf.Abs(localPos1.x - localPos2.x); float minY = Mathf.Min(localPos1.y, localPos2.y); float height = Mathf.Max(rt1.rect.height, rt2.rect.height); GameObject rectObj = new GameObject("PointerRangeRect", typeof(RectTransform), typeof(Image)); rectObj.transform.SetParent(parentRect, false); var rectTrans = rectObj.GetComponent(); var image = rectObj.GetComponent(); image.color = new Color(1f, 1f, 1f, 0.5f); rectTrans.anchorMin = new Vector2(0, 0); rectTrans.anchorMax = new Vector2(0, 0); rectTrans.pivot = new Vector2(0, 0); rectTrans.localPosition = new Vector3(minX, minY - (height / 2), 0); rectTrans.sizeDelta = new Vector2(width, height); DOTween.ToAlpha(() => image.color, c => image.color = c, 0f, 1f) .OnComplete(() => Destroy(rectObj)); } } } } } private void UpdateTime() { TimeText = songTime.ToString("F2"); BeatText = songBeat.ToString("F2"); } private Tweener _activeTweener = null; // 保存当前动画的引用 public void SetTime(string time) { EditorManager.instance.musicPlayer.PauseMusic(); float parsedTime = float.Parse(time); float offsetedtime = Mathf.Clamp(parsedTime - EditorManager.instance.songInformation.offset, 0, EditorManager.instance.songInformation.songLength); EditorManager.instance.musicPlayer.audioSource.time = offsetedtime; if (_activeTweener != null && _activeTweener.IsActive()) { _activeTweener.Complete(); } // 使用 DOTween 来平滑过渡到新的时间 _activeTweener = DOTween.To(() => EditorManager.instance.songInformation.songTime, x => EditorManager.instance.songInformation.songTime = x, parsedTime, 0.2f) .OnUpdate(UpdateTime) ; // timePointerModule.UpdatePointers(); // timePointerModule.SetRange(songTime); } public void SetBeat(string beat) { EditorManager.instance.musicPlayer.PauseMusic(); float parsedTime = float.Parse(beat) * timePerBeat; float offsetedtime = Mathf.Clamp(parsedTime - EditorManager.instance.songInformation.offset, 0, EditorManager.instance.songInformation.songLength); EditorManager.instance.musicPlayer.audioSource.time = offsetedtime; if (_activeTweener != null && _activeTweener.IsActive()) { _activeTweener.Complete(); } _activeTweener = DOTween.To(() => EditorManager.instance.songInformation.songTime, x => EditorManager.instance.songInformation.songTime = x, parsedTime, 0.2f) .OnUpdate(UpdateTime); // EditorManager.instance.musicPlayer.audioSource.time = Mathf.Clamp((float.Parse(beat) * timePerBeat) - EditorManager.instance.songInformation.offset, 0, EditorManager.instance.songInformation.songLength); // EditorManager.instance.songInformation.songTime = float.Parse(beat) * timePerBeat; // timePointerModule.UpdatePointers(); // timePointerModule.SetRange(songTime); } } public partial class Timeline { public TimelineTab timelineTabPrefab; public Dictionary timelineTabList = new Dictionary(); private int TabIndex = 0; public void SetTimeLine(GameElement element)//暂时好了(别的类型什么的传时间就好了) { TabIndex = 0; for (int i = timePointerModule.moveTabPoint.childCount - 1; i >= 0; i--) { var transform = timePointerModule.moveTabPoint.transform.GetChild(i); Destroy(transform.gameObject); } for (int i = timelineTabList.Count - 1; i >= 0; i--) { Destroy(timelineTabList.ElementAt(i).Value.gameObject); timelineTabList.Remove(timelineTabList.ElementAt(i).Key); } Elementfind(element); } public RectTransform moveTabPoint; public void Elementfind(GameElement element) { if (element == null) return; foreach (var i in element.childElementList) { if (i is NoteBase) { if (!timelineTabList.ContainsKey(i.GetType())) { Add(i); } else { timelineTabList[i.GetType()].AddElement(i); } } } if (element is NoteBase) { if (!timelineTabList.ContainsKey(element.GetType())) { Add(element); } else { timelineTabList[element.GetType()].AddElement(element); } } if (element is Track track) { var trackTimeSubmodule = track.submoduleList.FirstOrDefault(w => w is TrackTimeSubmodule) as TrackTimeSubmodule; if (trackTimeSubmodule != null) { Add(trackTimeSubmodule); } } var timeDurationSubmodule = element.submoduleList.FirstOrDefault(w => w is TimeDurationSubmodule) as TimeDurationSubmodule; if (timeDurationSubmodule != null && timeDurationSubmodule.isOverridingDuration) { Add(timeDurationSubmodule); } } private void Add(IBaseElement obj) { TimelineTab timelineTab = Instantiate(timelineTabPrefab, timelineTabRect.transform); timelineTab.MoveArea = moveTabPoint; timelineTab.TabIndex = TabIndex; timelineTab.SetTab(obj, obj.GetType()); timelineTabList.Add(obj.GetType(), timelineTab); TabIndex++; } } }