Signed-off-by: TRAfoer <lhf190@outlook.com>

This commit is contained in:
2026-02-11 01:26:10 +08:00
parent a76f650998
commit c21dc74576
17 changed files with 18014 additions and 17002 deletions

View File

@@ -12,7 +12,11 @@ namespace Ichni.Editor
{
public Button button;
public TMP_Text buttonText;
public override void Initialize(IBaseElement baseElement, string title, string parameterName)
{
base.Initialize(baseElement, title, parameterName);
}
public void SetText(string buttonText)
{
this.buttonText.text = buttonText;
@@ -26,10 +30,11 @@ namespace Ichni.Editor
button.onClick.AddListener(connectedBaseElement.Refresh);
}
}
public override DynamicUIElement AddListenerFunction(UnityAction action)
{
throw new System.NotImplementedException();
button.onClick.AddListener(action);
return this;
}
}
}

View File

@@ -7,6 +7,7 @@ using Ichni.RhythmGame;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.Profiling;
using UnityEngine.UI;
using Object = UnityEngine.Object;
namespace Ichni.Editor
@@ -70,6 +71,8 @@ namespace Ichni.Editor
button.SetText(title);
button.Initialize(baseElement, title, string.Empty);
button.ApplyFunction(function);
var nav = new Navigation { mode = Navigation.Mode.None };
button.button.navigation = nav;
subcontainer.dynamicUIElements.Add(button);
return button;
}
@@ -80,6 +83,8 @@ namespace Ichni.Editor
DynamicUIToggle toggle = Object.Instantiate(EditorManager.instance.basePrefabs.toggle, subcontainer.rect)
.GetComponent<DynamicUIToggle>();
toggle.Initialize(baseElement, title, parameterName);
var nav = new Navigation { mode = Navigation.Mode.None };
toggle.toggle.navigation = nav;
subcontainer.dynamicUIElements.Add(toggle);
return toggle;
}
@@ -92,6 +97,8 @@ namespace Ichni.Editor
.GetComponent<DynamicUIInputField>();
inputField.Initialize(null, title, string.Empty);
inputField.SetDefaultValue(defaultText);
var nav = new Navigation { mode = Navigation.Mode.None };
inputField.inputField.navigation = nav;
subcontainer.dynamicUIElements.Add(inputField);
return inputField;
}
@@ -103,6 +110,8 @@ namespace Ichni.Editor
.Instantiate(EditorManager.instance.basePrefabs.inputField, subcontainer.rect)
.GetComponent<DynamicUIInputField>();
inputField.Initialize(baseElement, title, parameterName);
var nav = new Navigation { mode = Navigation.Mode.None };
inputField.inputField.navigation = nav;
subcontainer.dynamicUIElements.Add(inputField);
return inputField;
}
@@ -115,6 +124,10 @@ namespace Ichni.Editor
.GetComponent<DynamicUIVector3InputField>();
vector3InputField.Initialize(null, title, string.Empty);
vector3InputField.SetDefaultValue(defaultVector3);
var nav = new Navigation { mode = Navigation.Mode.None };
vector3InputField.inputFieldX.navigation = nav;
vector3InputField.inputFieldY.navigation = nav;
vector3InputField.inputFieldZ.navigation = nav;
subcontainer.dynamicUIElements.Add(vector3InputField);
return vector3InputField;
}
@@ -127,6 +140,10 @@ namespace Ichni.Editor
.GetComponent<DynamicUIVector3InputField>();
vector3InputField.Initialize(baseElement, title, parameterName);
vector3InputField.SetAutoUpdate(isAutoUpdate);
var nav = new Navigation { mode = Navigation.Mode.None };
vector3InputField.inputFieldX.navigation = nav;
vector3InputField.inputFieldY.navigation = nav;
vector3InputField.inputFieldZ.navigation = nav;
subcontainer.dynamicUIElements.Add(vector3InputField);
return vector3InputField;
}
@@ -139,6 +156,9 @@ namespace Ichni.Editor
.GetComponent<DynamicUIVector2InputField>();
vector2InputField.Initialize(baseElement, title, parameterName);
vector2InputField.SetAutoUpdate(isAutoUpdate);
var nav = new Navigation { mode = Navigation.Mode.None };
vector2InputField.inputFieldX.navigation = nav;
vector2InputField.inputFieldY.navigation = nav;
subcontainer.dynamicUIElements.Add(vector2InputField);
return vector2InputField;
}
@@ -150,6 +170,11 @@ namespace Ichni.Editor
.Instantiate(EditorManager.instance.basePrefabs.baseColorPicker, subcontainer.rect)
.GetComponent<DynamicUIBaseColorPicker>();
colorPicker.Initialize(baseElement, title, parameterName);
var nav = new Navigation { mode = Navigation.Mode.None };
colorPicker.inputFieldBaseR.navigation = nav;
colorPicker.inputFieldBaseG.navigation = nav;
colorPicker.inputFieldBaseB.navigation = nav;
colorPicker.inputFieldBaseA.navigation = nav;
subcontainer.dynamicUIElements.Add(colorPicker);
return colorPicker;
}
@@ -162,6 +187,11 @@ namespace Ichni.Editor
.Instantiate(EditorManager.instance.basePrefabs.emissionColorPicker, subcontainer.rect)
.GetComponent<DynamicUIEmissionColorPicker>();
colorPicker.Initialize(baseElement, title, emissionEnabledName, emissionColorName, emissionIntensityName);
var nav = new Navigation { mode = Navigation.Mode.None };
colorPicker.inputFieldEmissionR.navigation = nav;
colorPicker.inputFieldEmissionG.navigation = nav;
colorPicker.inputFieldEmissionB.navigation = nav;
colorPicker.inputFieldEmissionI.navigation = nav;
subcontainer.dynamicUIElements.Add(colorPicker);
return colorPicker;
}
@@ -207,6 +237,8 @@ namespace Ichni.Editor
.GetComponent<DynamicUIEnumDropdown>();
enumDropdown.SetUpEnum(enumType);
enumDropdown.Initialize(baseElement, title, parameterName);
var nav = new Navigation { mode = Navigation.Mode.None };
enumDropdown.dropdown.navigation = nav;
subcontainer.dynamicUIElements.Add(enumDropdown);
return enumDropdown;
}
@@ -219,6 +251,8 @@ namespace Ichni.Editor
.GetComponent<DynamicUIStringListDropdown>();
stringListDropdown.SetUpStringList(stringList);
stringListDropdown.Initialize(baseElement, title, parameterName);
var nav = new Navigation { mode = Navigation.Mode.None };
stringListDropdown.dropdown.navigation = nav;
subcontainer.dynamicUIElements.Add(stringListDropdown);
return stringListDropdown;
}

View File

@@ -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,

View File

@@ -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();
}
}

View File

@@ -35,17 +35,12 @@ namespace Ichni.RhythmGame
public override void Initialize(string name, Guid elementGuid, List<string> tags, bool isFirstGenerated, GameElement parentElement)
{
base.Initialize(name, elementGuid, tags, isFirstGenerated, parentElement);
Update();
}
public override void AfterInitialize()
{
base.AfterInitialize();
AddinNoteManager();
}
public void AddinNoteManager(bool isNewOne = true)
{
@@ -147,13 +142,13 @@ namespace Ichni.RhythmGame
if (isFirstJudged && songTime < exactJudgeTime)
{
isFirstJudged = false;
noteVisual.GetComponent<Collider>().enabled = !isFirstJudged;
if (noteVisual != null) noteVisual.GetComponent<Collider>().enabled = !isFirstJudged;
}
else if (!isFirstJudged && songTime >= exactJudgeTime)
{
noteAudioSubmodule?.PlayNoteJudgeAudios(editor.currentJudgeType);
isFirstJudged = true;
noteVisual.GetComponent<Collider>().enabled = !isFirstJudged;
if (noteVisual != null) noteVisual.GetComponent<Collider>().enabled = !isFirstJudged;
}
// 判定单元更新

View File

@@ -17,7 +17,7 @@ namespace Ichni.Editor
public float sceneCameraMoveSpeed;
public float sceneCameraRotateSpeed;
public PanelDrawer panelDrawer = new();
public PanelDrawer panelDrawer => EditorManager.instance.panelDrawer;
public GameCamera gameCamera;
public bool haveGameCamera => gameCamera != null;
@@ -54,20 +54,17 @@ namespace Ichni.Editor
var switchCameraButton = inspector.GenerateButton(this, cameraSettings, "Switch Camera", SwitchCamera);
var cameraMoveSpeedField = inspector.GenerateInputField(this, cameraSettings, "Scene Camera Move Speed", nameof(sceneCameraMoveSpeed));
var camMethods = container.GenerateSubcontainer(1);
if (!panelDrawer.isEditing)
{
var butt1 = inspector.GenerateButton(this, camMethods, "startEdit", () =>
var butt1 = inspector.GenerateButton(this, camMethods, panelDrawer.isEditing ? "End Edit" : "Start Edit", () =>
{ });
butt1.AddListenerFunction(() =>
{
panelDrawer.startEdit();
});
}
else
{
var butt2 = inspector.GenerateButton(this, camMethods, "endEdit", () =>
{
panelDrawer.endEdit();
panelDrawer.SwitchEditing();
butt1.buttonText.text = panelDrawer.isEditing ? "End Edit" : "Start Edit";
});
}
sceneCamera.SetUpInspector();
}

View File

@@ -39,6 +39,7 @@ namespace Ichni
public BeatmapContainer beatmapContainer;
public CommandScripts commandScripts;
public PanelDrawer panelDrawer;
public NoteBase.NoteJudgeType currentJudgeType;
public bool useClickSelect;
public bool useNotePrefab;
@@ -121,6 +122,7 @@ namespace Ichni
private void Update()
{
if (isLoaded) projectManager.autoSaveManager.UpdateAutoSave();
}
public void LoadProject(string projectName)

View File

@@ -88,12 +88,24 @@ namespace Ichni.Editor
Vector3 moveDir = Vector3.zero;
// 计算合成位移向量
if (kb.wKey.isPressed) moveDir += sceneCameraTransform.forward;
if (kb.sKey.isPressed) moveDir -= sceneCameraTransform.forward;
if (kb.dKey.isPressed) moveDir += sceneCameraTransform.right;
if (kb.aKey.isPressed) moveDir -= sceneCameraTransform.right;
if (kb.eKey.isPressed) moveDir += sceneCameraTransform.up;
if (kb.qKey.isPressed) moveDir -= sceneCameraTransform.up;
if (EditorManager.instance.cameraManager.sceneCamera.CanBeFreeRotate)
{
if (kb.wKey.isPressed) moveDir += sceneCameraTransform.forward;
if (kb.sKey.isPressed) moveDir -= sceneCameraTransform.forward;
if (kb.dKey.isPressed) moveDir += sceneCameraTransform.right;
if (kb.aKey.isPressed) moveDir -= sceneCameraTransform.right;
if (kb.eKey.isPressed) moveDir += sceneCameraTransform.up;
if (kb.qKey.isPressed) moveDir -= sceneCameraTransform.up;
}
else
{
if (kb.dKey.isPressed) moveDir += sceneCameraTransform.right;
if (kb.aKey.isPressed) moveDir -= sceneCameraTransform.right;
if (kb.wKey.isPressed) moveDir += sceneCameraTransform.up;
if (kb.sKey.isPressed) moveDir -= sceneCameraTransform.up;
if (kb.qKey.isPressed) moveDir += sceneCameraTransform.forward;
if (kb.eKey.isPressed) moveDir -= sceneCameraTransform.forward;
}
// 一次性应用位移(如果存在移动)
if (moveDir != Vector3.zero)

View File

@@ -36,7 +36,7 @@ namespace Ichni.Editor
sceneCamera.transform.eulerAngles = value;
}
}
public bool CanBeFreeRotate => EditorManager.instance.cameraManager.panelDrawer.isEditing;
public bool CanBeFreeRotate => !EditorManager.instance.cameraManager.panelDrawer.isEditing;
public void SetUpInspector()