Signed-off-by: TRAfoer <lhf190@outlook.com>
This commit is contained in:
@@ -36,18 +36,139 @@ namespace ichni.RhythmGame // [修复] 修正命名空间首字母大写,符
|
||||
set
|
||||
{
|
||||
_isEnabled = value;
|
||||
foreach (var preview in NotePreviews.Values)
|
||||
{
|
||||
if (preview != null) preview.gameObject.SetActive(showNotePreview);
|
||||
}
|
||||
|
||||
Refresh();
|
||||
}
|
||||
}
|
||||
|
||||
public bool showNotePreview
|
||||
{
|
||||
get => _showNotePreview;
|
||||
set
|
||||
{
|
||||
_showNotePreview = value;
|
||||
foreach (var preview in NotePreviews.Values)
|
||||
{
|
||||
if (preview != null) preview.gameObject.SetActive(showNotePreview);
|
||||
}
|
||||
Refresh();
|
||||
}
|
||||
}
|
||||
private bool _showNotePreview = false;
|
||||
BaseElement_BM IBeChangeInExport.MatchingExportElement { get => null; set { } }
|
||||
|
||||
// NotePreview和TextHint的容器
|
||||
private Transform _previewRoot;
|
||||
private Transform PreviewRoot
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_previewRoot == null)
|
||||
{
|
||||
var obj = GameObject.Find("NotePreviewRoot");
|
||||
if (obj == null)
|
||||
{
|
||||
obj = new GameObject("NotePreviewRoot");
|
||||
obj.transform.SetParent(this.transform, false);
|
||||
}
|
||||
_previewRoot = obj.transform;
|
||||
}
|
||||
return _previewRoot;
|
||||
}
|
||||
}
|
||||
|
||||
private class NotePreviewData : MonoBehaviour
|
||||
{
|
||||
public float noteTime;
|
||||
public NoteBase noteBase1;
|
||||
public LineRenderer lineRenderer;
|
||||
|
||||
public void Initialize(float time, NoteBase noteBase)
|
||||
{
|
||||
noteTime = time;
|
||||
noteBase1 = noteBase;
|
||||
lineRenderer = this.gameObject.AddComponent<LineRenderer>();
|
||||
lineRenderer.useWorldSpace = false;
|
||||
lineRenderer.positionCount = 2;
|
||||
lineRenderer.startWidth = 1f;
|
||||
lineRenderer.endWidth = 0f;
|
||||
lineRenderer.material = EditorManager.instance.basePrefabs.defaultTrackMaterial;
|
||||
Color color = new Color(1f, 0.5f, 0f, 0.8f);
|
||||
DOTween.ToAlpha(() => color, c => color = c, 1f, 1f).SetEase(Ease.InOutQuad).OnUpdate(() =>
|
||||
{
|
||||
lineRenderer.startColor = lineRenderer.endColor = color;
|
||||
});
|
||||
|
||||
}
|
||||
public void Refresh(SplineComputer spline, TrackTimeSubmoduleMovable trackTime, float horizonWidth)
|
||||
{
|
||||
float trackPercent = Mathf.Clamp01(trackTime.GetTrackPercentRaw(noteTime));
|
||||
SplineSample sample = spline.Evaluate(trackPercent);
|
||||
Vector3 sideOffset = sample.rotation * (noteBase1.noteVisual != null ? noteBase1.noteVisual.transformSubmodule.originalPosition : Vector3.zero);
|
||||
Vector3 worldPos = sample.position;
|
||||
|
||||
Vector3 localPos = this.transform.parent.InverseTransformPoint(worldPos);
|
||||
Vector3 localOffset = this.transform.parent.InverseTransformVector(Vector3.up);
|
||||
lineRenderer.SetPosition(0, localPos + sideOffset);
|
||||
lineRenderer.SetPosition(1, localPos + localOffset + sideOffset);
|
||||
}
|
||||
void OnDestroy() { }
|
||||
}
|
||||
// NotePreviews字典改为以时间为key
|
||||
private Dictionary<float, NotePreviewData> NotePreviews = new Dictionary<float, NotePreviewData>();
|
||||
public List<LineRenderer> BeatLines = new List<LineRenderer>();
|
||||
public List<float> Beats = new List<float>();
|
||||
public override void Refresh()
|
||||
{
|
||||
base.Refresh();
|
||||
UpdateBeatLine();
|
||||
RefreshNotePreviews();
|
||||
}
|
||||
|
||||
// 刷新NotePreviews,按时间生成预览
|
||||
private void RefreshNotePreviews()
|
||||
{
|
||||
if (TrackedTrack == null) return;
|
||||
if (!_isEnabled) return;
|
||||
if (!showNotePreview) return;
|
||||
var notes = TrackedTrack.childElementList.OfType<NoteBase>().ToList();
|
||||
var times = notes.Select(n => n.exactJudgeTime).ToList();
|
||||
// 移除已不存在的note时间
|
||||
var toRemove = NotePreviews.Keys.Except(times).ToList();
|
||||
foreach (var t in toRemove)
|
||||
{
|
||||
if (NotePreviews[t] != null) Destroy(NotePreviews[t].gameObject);
|
||||
NotePreviews.Remove(t);
|
||||
}
|
||||
// 添加新note时间
|
||||
foreach (var n in notes)
|
||||
{
|
||||
float time = n.exactJudgeTime;
|
||||
if (!NotePreviews.ContainsKey(time))
|
||||
{
|
||||
var obj = new GameObject($"NotePreview_{n.elementName}_{time}");
|
||||
obj.transform.SetParent(PreviewRoot, false);
|
||||
var preview = obj.AddComponent<NotePreviewData>();
|
||||
preview.Initialize(time, n);
|
||||
NotePreviews[time] = preview;
|
||||
}
|
||||
}
|
||||
// 刷新所有预览
|
||||
SplineComputer spline = TrackedTrack.trackPathSubmodule.path;
|
||||
var trackTime = TrackedTrack.trackTimeSubmodule as TrackTimeSubmoduleMovable;
|
||||
foreach (var preview in NotePreviews.Values)
|
||||
{
|
||||
if (preview != null)
|
||||
{
|
||||
Observable.NextFrame().Subscribe(_ =>
|
||||
{
|
||||
preview.Refresh(spline, trackTime, horizonWidth);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int BeatDiver = 1; // [建议] 变量名修正为 BeatDiver (原本是 Beatdiver)
|
||||
@@ -219,6 +340,7 @@ namespace ichni.RhythmGame // [修复] 修正命名空间首字母大写,符
|
||||
if (ForceRefresh)
|
||||
{
|
||||
AdjustBeatLine();
|
||||
RefreshNotePreviews();
|
||||
}
|
||||
|
||||
if (InputListener.instance.isPointerOverUI) return;
|
||||
@@ -229,6 +351,7 @@ namespace ichni.RhythmGame // [修复] 修正命名空间首字母大写,符
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void CastRay()
|
||||
{
|
||||
if (EditorManager.instance.cameraManager.currentCamera == null) return;
|
||||
@@ -372,12 +495,9 @@ namespace ichni.RhythmGame // [修复] 修正命名空间首字母大写,符
|
||||
{
|
||||
// 1. 创建一个新的 GameObject
|
||||
string content = noteBase.elementName;
|
||||
Vector3 worldPos = noteBase.noteVisual.transform.position;
|
||||
GameObject hintObj = new GameObject("NoteHint_" + content);
|
||||
hintObj.transform.position = worldPos;
|
||||
|
||||
hintObj.transform.SetParent(PreviewRoot, false); // 统一放到PreviewRoot下
|
||||
// 2. 添加 TextMeshPro 组件 (注意是 TextMeshPro,不是 TextMeshProUGUI)
|
||||
// 因为我们在 3D 空间生成,所以使用非 UI 版本
|
||||
TextMeshPro text = hintObj.AddComponent<TextMeshPro>();
|
||||
// 3. 配置文字属性
|
||||
text.text = content;
|
||||
@@ -391,13 +511,25 @@ namespace ichni.RhythmGame // [修复] 修正命名空间首字母大写,符
|
||||
// 5. 动画效果:向上移动 1 个单位,并同时淡出
|
||||
// 1 秒后执行
|
||||
hintObj.transform.DOScale(Vector3.one * 1.2f, 1f).SetEase(Ease.OutBack).From(Vector3.zero);
|
||||
hintObj.transform.DOMoveY(worldPos.y + 1f, 1f);
|
||||
// 位置同步协程
|
||||
StartCoroutine(SyncHintPosition(hintObj.transform, noteBase));
|
||||
text.DOFade(0, 1f).OnComplete(() => Destroy(hintObj));
|
||||
// 7. 让文字始终面向相机 (Billboard 效果)
|
||||
// 如果你的相机视角是固定的,可以直接设置旋转
|
||||
// 让文字始终面向相机 (Billboard 效果)
|
||||
hintObj.transform.LookAt(hintObj.transform.position + Camera.main.transform.rotation * Vector3.forward,
|
||||
EditorManager.instance.cameraManager.currentCamera.transform.rotation * Vector3.up);
|
||||
}
|
||||
|
||||
private IEnumerator SyncHintPosition(Transform hint, NoteBase noteBase)
|
||||
{
|
||||
float t = 0f;
|
||||
Vector3 offset = Vector3.up;
|
||||
while (t < 1f && noteBase != null && noteBase.noteVisual != null)
|
||||
{
|
||||
hint.position = noteBase.noteVisual.transform.position + offset;
|
||||
t += Time.deltaTime;
|
||||
yield return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 后面的 Inspector 和 Export 代码逻辑暂时不需要大改,只要注意变量名 Beatdiver -> BeatDiver
|
||||
@@ -419,6 +551,8 @@ namespace ichni.RhythmGame // [修复] 修正命名空间首字母大写,符
|
||||
inspector.GenerateInputField(this, sub, "Beat Diver", nameof(BeatDiver));
|
||||
inspector.GenerateToggle(this, sub, "Force refresh (cost++)", nameof(ForceRefresh));
|
||||
inspector.GenerateInputField(this, sub, "Horizon Width", nameof(horizonWidth));
|
||||
inspector.GenerateToggle(this, sub, "Show Note Preview", nameof(showNotePreview));
|
||||
|
||||
}
|
||||
|
||||
public static FastNoteTracker GenerateElement(string elementName, Guid id, List<string> tags,
|
||||
|
||||
@@ -1,16 +1,21 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using DG.Tweening;
|
||||
using Ichni;
|
||||
using Ichni.Editor;
|
||||
using Ichni.RhythmGame;
|
||||
using TMPro;
|
||||
using Unity.VisualScripting;
|
||||
using UnityEngine;
|
||||
using UnityEngine.InputSystem;
|
||||
using UnityEngine.UI;
|
||||
|
||||
public class PanelDrawer//暂时支持xz
|
||||
public class PanelDrawer : MonoBehaviour //暂时支持xz
|
||||
{
|
||||
public bool isEditing = false;
|
||||
public CameraManager cameraManager => EditorManager.instance.cameraManager;
|
||||
public GameElement connectedGameElement => EditorManager.instance.uiManager.inspector.connectedGameElement;
|
||||
public SceneCamera sceneCamera => cameraManager.sceneCamera;
|
||||
|
||||
public float height
|
||||
@@ -37,6 +42,7 @@ public class PanelDrawer//暂时支持xz
|
||||
}
|
||||
}
|
||||
private float _baseHeight = 0f;
|
||||
private Vector3 tempRot = new Vector3(30, 0, 0);
|
||||
public void startEdit()
|
||||
{
|
||||
isEditing = true;
|
||||
@@ -47,16 +53,86 @@ public class PanelDrawer//暂时支持xz
|
||||
cameraManager.sceneCamera.transform.rotation = cameraManager.gameCamera.transform.rotation;
|
||||
cameraManager.SwitchCamera();
|
||||
cameraManager.sceneCamera.transform.DOMove(cameraManager.gameCamera.transform.position + new Vector3(0, 0, _baseHeight + _height), 0.5f).SetEase(Ease.InOutQuad);
|
||||
tempRot = cameraManager.sceneCamera.transform.eulerAngles;
|
||||
cameraManager.sceneCamera.transform.DORotate(new Vector3(90, 0, 0), 0.5f).SetEase(Ease.InOutQuad);
|
||||
}
|
||||
else
|
||||
{
|
||||
cameraManager.sceneCamera.transform.DOMove(cameraManager.gameCamera.transform.position + new Vector3(0, 0, _baseHeight + _height), 0.5f).SetEase(Ease.InOutQuad);
|
||||
tempRot = cameraManager.sceneCamera.transform.eulerAngles;
|
||||
cameraManager.sceneCamera.transform.DORotate(new Vector3(90, 0, 0), 0.5f).SetEase(Ease.InOutQuad);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
public void endEdit()
|
||||
{
|
||||
isEditing = false;
|
||||
cameraManager.sceneCamera.transform.DOMove(cameraManager.gameCamera.transform.position + new Vector3(0, 0, _baseHeight + _height), 0.5f).SetEase(Ease.InOutQuad);
|
||||
cameraManager.sceneCamera.transform.DORotate(tempRot, 0.5f).SetEase(Ease.InOutQuad);
|
||||
}
|
||||
public void SwitchEditing()
|
||||
{
|
||||
if (isEditing)
|
||||
{
|
||||
endEdit();
|
||||
}
|
||||
else
|
||||
{
|
||||
startEdit();
|
||||
}
|
||||
}
|
||||
void Update()
|
||||
{
|
||||
if (isEditing && connectedGameElement is Track &&
|
||||
Keyboard.current.leftCtrlKey.isPressed &&
|
||||
Mouse.current.leftButton.wasPressedThisFrame)
|
||||
{
|
||||
StartCoroutine(Pressing());
|
||||
}
|
||||
}
|
||||
public Texture ingTexture;
|
||||
public Canvas DisplayCanvas;
|
||||
private IEnumerator Pressing()
|
||||
{
|
||||
GameObject gobj = new GameObject("PanelDrawerPreview");
|
||||
RawImage rawImage = gobj.AddComponent<RawImage>();
|
||||
GameObject gobj2 = new GameObject("PanelDrawerText");
|
||||
gobj2.transform.SetParent(gobj.transform, false);
|
||||
TextMeshProUGUI Text = gobj2.AddComponent<TextMeshProUGUI>();
|
||||
Text.rectTransform.position = new Vector3(50, 50, 0);
|
||||
rawImage.texture = ingTexture;
|
||||
rawImage.rectTransform.SetParent(DisplayCanvas.transform);
|
||||
rawImage.rectTransform.sizeDelta = new Vector2(100, 100);
|
||||
Vector3 hitPoint = new Vector3(0, 0, 0);
|
||||
while (Mouse.current.leftButton.isPressed)
|
||||
{
|
||||
|
||||
Ray ray = cameraManager.currentCamera.ScreenPointToRay(Mouse.current.position.ReadValue());
|
||||
Plane plane = new Plane(Vector3.up, new Vector3(0, _baseHeight, 0));
|
||||
if (plane.Raycast(ray, out float enter))
|
||||
{
|
||||
|
||||
hitPoint = ray.GetPoint(enter);
|
||||
if (Keyboard.current.leftAltKey.isPressed)
|
||||
{
|
||||
hitPoint = new Vector3(Mathf.Round(hitPoint.x / 0.5f) * 0.5f, hitPoint.y, Mathf.Round(hitPoint.z / 0.5f) * 0.5f);
|
||||
rawImage.rectTransform.position =
|
||||
new Vector3(Mathf.Round(Mouse.current.position.ReadValue().x / 10) * 10, Mathf.Round(Mouse.current.position.ReadValue().y / 10) * 10, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
rawImage.rectTransform.position = Mouse.current.position.ReadValue();
|
||||
}
|
||||
Text.text = $"X: {hitPoint.x:F2}\nY: {hitPoint.y:F2}\nZ: {hitPoint.z:F2}";
|
||||
|
||||
}
|
||||
yield return null;
|
||||
}
|
||||
Destroy(rawImage.gameObject);
|
||||
var i = PathNode.GenerateElement("new pathnode", Guid.NewGuid(), new List<string>(), true, (Track)connectedGameElement, true);
|
||||
i.transformSubmodule.originalPosition = hitPoint;
|
||||
i.Refresh();
|
||||
connectedGameElement.Refresh();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user