QuickMove!

Signed-off-by: TRAfoer <lhf190@outlook.com>
This commit is contained in:
2025-08-10 16:00:46 +08:00
parent 7b152a2ecd
commit 19ff2f4862
55 changed files with 343288 additions and 364537 deletions

View File

@@ -5,6 +5,7 @@ using Ichni.RhythmGame;
using Michsky.MUIP;
using Sirenix.Utilities;
using TMPro;
using Unity.VisualScripting;
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.Serialization;
@@ -132,6 +133,24 @@ namespace Ichni.Editor
EditorManager.instance.operationManager.ClearSelectedElements();
EditorManager.instance.operationManager.AddSelectElement(connectedGameElement);
}
if (EditorManager.instance.useQuickMove)
{
if (connectedGameElement is IHaveTransformSubmodule haveTransformSubmodule)
{
QuickMover quickMover = Instantiate(EditorManager.instance.basePrefabs.QuickMoveObj).GetComponent<QuickMover>();
quickMover.Initialize(haveTransformSubmodule);
}
else if (connectedGameElement is NoteBase noteBase && noteBase.noteVisual != null)
{
QuickMover quickMover = Instantiate(EditorManager.instance.basePrefabs.QuickMoveObj).GetComponent<QuickMover>();
quickMover.Initialize(noteBase.noteVisual);
}
else if (QuickMover.instance != null)
{
Destroy(QuickMover.instance.gameObject);
QuickMover.instance = null;
}
}
EditorManager.instance.uiManager.inspector.SetInspector(connectedGameElement);
@@ -198,7 +217,7 @@ namespace Ichni.Editor
for (var index = 0; index < FixedList.Count; index++)
{
int hasYield = 0;
while (Time.realtimeSinceStartup - startTime > 1f / StrandTimeWhileStartUp * 3f && hasYield <= 2)
while (Time.realtimeSinceStartup - startTime > 1f / StrandTimeWhileStartUp * 3f && hasYield <= 1)
{
yield return null;
hasYield += 1;

View File

@@ -103,11 +103,11 @@ namespace Ichni.RhythmGame
var container = inspector.GenerateContainer("Transform");
var subcontainer = container.GenerateSubcontainer(1);
var originalPosInputField =
inspector.GenerateVector3InputField(this, subcontainer, "Start Position", nameof(originalPosition));
inspector.GenerateVector3InputField(this, subcontainer, "Start Position", nameof(originalPosition), true);
var originalRotInputField =
inspector.GenerateVector3InputField(this, subcontainer, "Start Rotation", nameof(originalEulerAngles));
inspector.GenerateVector3InputField(this, subcontainer, "Start Rotation", nameof(originalEulerAngles), true);
var originalScaleInputField =
inspector.GenerateVector3InputField(this, subcontainer, "Start Scale", nameof(originalScale));
inspector.GenerateVector3InputField(this, subcontainer, "Start Scale", nameof(originalScale), true);
var currentPosText =
inspector.GenerateParameterText(this, subcontainer, "Current Position", nameof(currentPosition), true);
var currentRotText =

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: bd71e53392300e547bd7aae27850df3a
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,191 @@
using System.Collections;
using UnityEngine;
using Ichni.RhythmGame;
using UnityEngine.InputSystem;
using DG.Tweening;
namespace Ichni.Editor
{
public class QuickMover : MonoBehaviour
{
public static QuickMover instance;
[Header("Settings")]
public float moveSensitivity = 0.5f; // 移动灵敏度
[Header("References")]
public GameObject prefab;
public BoxCollider colliderX;
public BoxCollider colliderY;
public BoxCollider colliderZ;
// 运行时状态
private IHaveTransformSubmodule targetElement;
private Camera editorCamera => EditorManager.instance.cameraManager.currentCamera;
private bool isMoving;
private int activeMoveCode = -1;
private Vector3 initialTargetPosition;
private Vector3 initialMouseScreenPos;
public GameElement targetGameElement =>
targetElement?.transformSubmodule?.attachedGameElement;
public void Initialize(IHaveTransformSubmodule targetElement)
{
this.targetElement = targetElement;
if (instance != null)
{
Destroy(instance.gameObject);
}
instance = this;
if (targetGameElement != null)
transform.position = targetGameElement.transform.position;
transform.localScale = Vector3.zero;
StartCoroutine(WindowAnim.ShowPanelOnScale(gameObject));
}
void Update()
{
if (isMoving) return;
transform.position = targetGameElement.transform.position;
if (Mouse.current.leftButton.wasPressedThisFrame)
{
Vector2 mousePosition = Mouse.current.position.ReadValue();
Ray ray = editorCamera.ScreenPointToRay(mousePosition);
if (Physics.Raycast(ray, out RaycastHit hit, float.MaxValue, LayerMask.GetMask("Default")))
{
if (hit.collider == colliderX)
{
colliderX.gameObject.transform.DOScale(colliderX.gameObject.transform.localScale * 1.2f, 0.2f);
StartMovement(0);
}
else if (hit.collider == colliderY)
{
colliderY.gameObject.transform.DOScale(colliderY.gameObject.transform.localScale * 1.2f, 0.2f);
StartMovement(1);
}
else if (hit.collider == colliderZ)
{
colliderZ.gameObject.transform.DOScale(colliderZ.gameObject.transform.localScale * 1.2f, 0.2f);
StartMovement(2);
}
}
}
}
private Vector3 objPosOffset;
private void StartMovement(int moveCode)
{
Debug.Log($"StartMovement: {moveCode}");
if (targetGameElement == null) return;
activeMoveCode = moveCode;
initialTargetPosition = targetGameElement.transform.position;
initialMouseScreenPos = Mouse.current.position.ReadValue();
objPosOffset = targetElement.transformSubmodule.currentPosition - targetElement.transformSubmodule.originalPosition;
StartCoroutine(MovementRoutine());
}
private IEnumerator MovementRoutine()
{
isMoving = true;
while (Mouse.current.leftButton.isPressed)
{
UpdateTargetPosition();
targetElement.transformSubmodule.originalPosition = targetElement.transformSubmodule.currentPosition - objPosOffset;
yield return null;
}
// 当鼠标释放时,结束移动
EndMovement();
isMoving = false;
activeMoveCode = -1;
}
private void EndMovement()
{
targetGameElement.transform.position = transform.position;
targetElement.transformSubmodule.currentPosition = targetGameElement.transform.localPosition;
targetElement.transformSubmodule.originalPosition = targetElement.transformSubmodule.currentPosition - objPosOffset;
targetGameElement.Refresh();
if (activeMoveCode == 0)
{
colliderX.gameObject.transform.DOScale(colliderX.gameObject.transform.localScale / 1.2f, 0.2f);
// Do something specific for X axis
}
else if (activeMoveCode == 1)
{
colliderY.gameObject.transform.DOScale(colliderY.gameObject.transform.localScale / 1.2f, 0.2f);
// Do something specific for Y axis
}
else if (activeMoveCode == 2)
{
colliderZ.gameObject.transform.DOScale(colliderZ.gameObject.transform.localScale / 1.2f, 0.2f);
// Do something specific for Z axis
}
}
private void UpdateTargetPosition()
{
if (targetGameElement == null) return;
// 获取当前鼠标位置
Vector3 currentMousePos = Mouse.current.position.ReadValue();
Vector3 mouseDelta = currentMousePos - initialMouseScreenPos;
// 计算移动轴的世界方向
Vector3 worldAxis = GetWorldAxis(activeMoveCode);
// 计算屏幕移动向量对应的世界移动量
float worldDelta = CalculateWorldDelta(worldAxis, mouseDelta);
// 应用新位置
Vector3 newPosition = initialTargetPosition + worldAxis * worldDelta;
Vector3 newPositionFormatted = new Vector3(
Mathf.Round(newPosition.x * 100f) / 100f,
Mathf.Round(newPosition.y * 100f) / 100f,
Mathf.Round(newPosition.z * 100f) / 100f
);
targetGameElement.transform.position = newPositionFormatted;
transform.position = newPositionFormatted;
}
private Vector3 GetWorldAxis(int moveCode)
{
return moveCode switch
{
0 => transform.right, // X轴
1 => transform.up, // Y轴
2 => transform.forward, // Z轴
_ => Vector3.zero
};
}
private float CalculateWorldDelta(Vector3 worldAxis, Vector3 mouseDelta)
{
// 将世界轴转换为屏幕方向
Vector3 screenRefPoint = editorCamera.WorldToScreenPoint(initialTargetPosition);
Vector3 screenAxisEnd = editorCamera.WorldToScreenPoint(initialTargetPosition + worldAxis);
// 计算屏幕方向向量
Vector3 screenAxis = (screenAxisEnd - screenRefPoint).normalized;
// 防止摄像机与轴平行时出现零向量
if (screenAxis.sqrMagnitude < 0.01f) return 0;
// 计算鼠标移动在轴方向上的投影
Vector2 screenAxis2D = new Vector2(screenAxis.x, screenAxis.y);
Vector2 mouseDelta2D = new Vector2(mouseDelta.x, mouseDelta.y);
float projection = Vector2.Dot(mouseDelta2D, screenAxis2D.normalized);
// 应用灵敏度并返回
return projection * moveSensitivity * 0.01f;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: cde63278924ce8944b8accfbf000a7e5
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -232,7 +232,7 @@ public class SampleWindow : MovableWindow//该window高度为300横的要XWid
far += 1f / Xdevide;
}
far -= 1f / Xdevide;
float time = far * timePerBeat;
float time = Mathf.Round(far * timePerBeat * 1000f) / 1000f;
switch (NoteCode)

View File

@@ -73,9 +73,11 @@ namespace Ichni.RhythmGame
public GameObject stringIntPairUnit;
[Title("字体")] public Dictionary<string, TMP_FontAsset> fonts;
[Title("图形化动画编辑器")] public GameObject graphicalFlexibleFloatWindow;
[Title("图形化动画编辑器")]
public GameObject graphicalFlexibleFloatWindow;
public GameObject QuickMoveObj;
//采音器
[Title("Background相关")] public Sprite defaultBackground;

View File

@@ -39,9 +39,10 @@ namespace Ichni
public CommandScripts commandScripts;
public NoteBase.NoteJudgeType currentJudgeType;
public bool useNotePrefab = true;
public bool ExpandWhileClick = true;
public bool useClickSelect;
public bool useNotePrefab;
public bool ExpandWhileClick;
public bool useQuickMove;
public BasePrefabsCollection basePrefabs;
public Dictionary<string, CustomPrefabsCollection> customPrefabs;
@@ -55,6 +56,10 @@ namespace Ichni
private void Awake()
{
instance = this;
useNotePrefab = true;
ExpandWhileClick = true;
useQuickMove = true;
useClickSelect = true;
isLoaded = false;
projectManager = new ProjectManager();
operationManager = new OperationManager();
@@ -121,7 +126,15 @@ namespace Ichni
public void LoadProject(string projectName)
{
projectManager.loadManager.Load(projectName);
if (!InformationTransistor.instance.isRecovery)
{
projectManager.loadManager.Load(projectName);
}
else
{
projectManager.loadManager.LoadExport(projectName);
}
musicPlayer.audioSource.clip = songInformation.song;
beatmapContainer.gameElementList.ForEach(gameElement =>
{
@@ -150,10 +163,14 @@ namespace Ichni
});
var useNotePrefabToggle =
inspector.GenerateToggle(this, inGameSettings, "Use Note Prefab", nameof(useNotePrefab));
var useClickSelectToggle =
inspector.GenerateToggle(this, inGameSettings, "Use Click Select", nameof(useClickSelect));
var ExpandWhileClickToggle =
inspector.GenerateToggle(this, inGameSettings, "Expand Tab While Click", nameof(ExpandWhileClick));
var useQuickMoveToggle =
inspector.GenerateToggle(this, inGameSettings, "Use Quick Move", nameof(useQuickMove));
var generation = container.GenerateSubcontainer(3);
var generateFolderButton =
inspector.GenerateButton(this, generation, "Generate Folder",

View File

@@ -42,7 +42,7 @@ namespace Ichni.Editor
ResolutionHintsOperation();
UIOperation();
SwitchCameraOperation();
ClickSelectElement();
if (EditorManager.instance.useClickSelect) ClickSelectElement();

View File

@@ -183,6 +183,41 @@ namespace Ichni
ES3.Load<CommandScripts_BM>("CommandScripts", EditorManager.instance.projectInformation.CommandScriptsPath,
ProjectManager.SaveSettings).ExecuteBM();
}
public void LoadExport(string projectName)
{
LoadProjectInfoExport(projectName);
LoadSongInfoExport(projectName);
LoadCommandScriptsExport(projectName);
LoadBeatMapExport(projectName);
LogWindow.Log("Load Export Complete,", new Color(0.5f, 0f, 1f));
LogWindow.Log("Please Save Your Project As You Can", new Color(0.5f, 0f, 1f));
}
private void LoadProjectInfoExport(string projectName)
{
string projectInfoPath = Application.streamingAssetsPath + "/Export/" + projectName + "/ProjectInfo.bytes";
ES3.Load<ProjectInformation_BM>("ProjectInformation", projectInfoPath, ProjectManager.ExportSettings).ExecuteBM();
}
private void LoadSongInfoExport(string projectName)
{
string songInfoPath = Application.streamingAssetsPath + "/Export/" + projectName + "/SongInfo.bytes";
ES3.Load<SongInformation_BM>("SongInformation", songInfoPath, ProjectManager.ExportSettings).ExecuteBM();
}
private void LoadBeatMapExport(string projectName)
{
string beatMapPath = Application.streamingAssetsPath + "/Export/" + projectName + "/Beatmap.bytes";
ES3.Load<BeatmapContainer_BM>("Beatmap", beatMapPath, ProjectManager.ExportSettings).ExecuteBM();
}
private void LoadCommandScriptsExport(string projectName)
{
string commandScriptsPath = Application.streamingAssetsPath + "/Export/" + projectName + "/CommandScripts.bytes";
ES3.Load<CommandScripts_BM>("CommandScripts", commandScriptsPath, ProjectManager.ExportSettings).ExecuteBM();
}
}
public class BeatmapClipManager

View File

@@ -12,10 +12,10 @@ namespace Ichni
public bool isLoadedProject;
public string loadedProjectName;
public bool isRecovery = false;
public ProjectInformation_BM projectInfo_BM;
public SongInformation_BM songInfo_BM;
private void Awake()
{
if (instance == null)

View File

@@ -15,6 +15,7 @@ namespace Ichni.StartMenu
public TMP_Text projectNameText, lastSavedTimeText, songNameText;
public Button loadButton;
public Image MemuMask;
public bool isRecovery = false;
public void SetUpTab(string projectName, string lastSavedTime, string songName)
{
this.projectName = projectName;
@@ -31,6 +32,7 @@ namespace Ichni.StartMenu
MemuMask.gameObject.SetActive(true);
InformationTransistor.instance.isLoadedProject = true;
InformationTransistor.instance.loadedProjectName = projectName;
InformationTransistor.instance.isRecovery = isRecovery;
string projectPath = Application.streamingAssetsPath + "/Projects/" + projectName;
InformationTransistor.instance.projectInfo_BM = ES3.Load<ProjectInformation_BM>("ProjectInformation", projectPath + "/ProjectInfo.json");