点击选中
This commit is contained in:
@@ -0,0 +1,38 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Events;
|
||||
|
||||
namespace Ichni.RhythmGame
|
||||
{
|
||||
public class SelectSubmodule : SubmoduleBase
|
||||
{
|
||||
private GameElement elementToSelect;
|
||||
|
||||
public SelectSubmodule(GameElement attachedGameElement, GameElement elementToSelect = null) : base(attachedGameElement)
|
||||
{
|
||||
(attachedGameElement as IHaveSelectSubmodule).selectSubmodule = this;
|
||||
(attachedGameElement).gameObject.layer = LayerMask.NameToLayer("Selectable");
|
||||
this.elementToSelect = elementToSelect == null ? attachedGameElement : elementToSelect;
|
||||
}
|
||||
|
||||
public void SelectGameElement()
|
||||
{
|
||||
Debug.Log(elementToSelect);
|
||||
EditorManager.instance.operationManager.ClearSelectedElements();
|
||||
EditorManager.instance.operationManager.AddSelectElement(elementToSelect);
|
||||
EditorManager.instance.uiManager.inspector.SetInspector(elementToSelect);
|
||||
EditorManager.instance.timeline.SetTimeLine(elementToSelect);
|
||||
}
|
||||
|
||||
public override void SaveBM()
|
||||
{
|
||||
//这个模块不需要存档
|
||||
}
|
||||
}
|
||||
|
||||
public interface IHaveSelectSubmodule
|
||||
{
|
||||
public SelectSubmodule selectSubmodule { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e91fffff157a0d543850b1ce45419ff8
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -53,7 +53,11 @@ namespace Ichni.RhythmGame
|
||||
this.tags = tags;
|
||||
EditorManager.instance.beatmapContainer.gameElementList.Add(this);
|
||||
submoduleList = new List<SubmoduleBase>();
|
||||
if (isFirstGenerated) SetDefaultSubmodules();
|
||||
if (isFirstGenerated)
|
||||
{
|
||||
SetDefaultSubmodules();
|
||||
}
|
||||
|
||||
SetParent(parentElement);
|
||||
EditorManager.instance.uiManager.hierarchy.GenerateTab(this, parentElement);
|
||||
|
||||
@@ -70,12 +74,17 @@ namespace Ichni.RhythmGame
|
||||
|
||||
}
|
||||
|
||||
public virtual void SetEditorSubmodules()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 在所有物体生成完毕后,执行的初始化方法
|
||||
/// </summary>
|
||||
public virtual void AfterInitialize()
|
||||
{
|
||||
|
||||
SetEditorSubmodules();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -4,7 +4,7 @@ using UnityEngine;
|
||||
|
||||
namespace Ichni.RhythmGame
|
||||
{
|
||||
public abstract class NoteVisualBase : SubstantialObject, IHaveEffectSubmodule
|
||||
public abstract class NoteVisualBase : SubstantialObject, IHaveEffectSubmodule, IHaveSelectSubmodule
|
||||
{
|
||||
public NoteBase note;
|
||||
|
||||
@@ -15,11 +15,17 @@ namespace Ichni.RhythmGame
|
||||
public List<GameObject> effectPartList;
|
||||
|
||||
public EffectSubmodule effectSubmodule { get; set; }
|
||||
public SelectSubmodule selectSubmodule { get; set; }
|
||||
|
||||
public override void SetDefaultSubmodules()
|
||||
{
|
||||
base.SetDefaultSubmodules();
|
||||
effectSubmodule = new EffectSubmodule(this, EffectSubmodule.EffectSubmodulePreset.Note);
|
||||
}
|
||||
|
||||
public override void SetEditorSubmodules()
|
||||
{
|
||||
selectSubmodule = new SelectSubmodule(this, note);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -54,6 +54,7 @@ namespace Ichni.RhythmGame
|
||||
|
||||
public override void AfterInitialize()
|
||||
{
|
||||
base.AfterInitialize();
|
||||
Refresh();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,6 +47,8 @@ namespace Ichni.RhythmGame
|
||||
|
||||
public override void AfterInitialize()
|
||||
{
|
||||
base.AfterInitialize();
|
||||
|
||||
if (trackPathSubmodule != null && trackPathSubmodule.pathNodeList.Count > 3)
|
||||
{
|
||||
trackPathSubmodule.ClosePath();
|
||||
@@ -62,7 +64,6 @@ namespace Ichni.RhythmGame
|
||||
trackPathSubmodule?.Refresh();
|
||||
trackTimeSubmodule?.Refresh();
|
||||
trackRendererSubmodule?.Refresh();
|
||||
|
||||
}
|
||||
|
||||
public override void OnDelete()
|
||||
|
||||
@@ -1,4 +1,10 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Lean.Pool;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
using UnityEngine.InputSystem;
|
||||
using UnityEngine.Serialization;
|
||||
|
||||
namespace Ichni.Editor
|
||||
{
|
||||
@@ -7,6 +13,8 @@ namespace Ichni.Editor
|
||||
{
|
||||
[Tooltip("指定用于计算缩放的摄像机(若为空则使用 Camera.main)")]
|
||||
public Camera sceneCamera;
|
||||
|
||||
public float cameraDistance;
|
||||
|
||||
[Tooltip("指定网格所在的平面:0 = XZ (y=0), 1 = XY (z=0), 2 = YZ (x=0)")]
|
||||
public int gridPlane = 0;
|
||||
@@ -21,8 +29,19 @@ namespace Ichni.Editor
|
||||
// 内部缓存材质
|
||||
private Material gridMaterial;
|
||||
|
||||
public float logScale;
|
||||
public float gridScale; // 1, 4, 16, 64...
|
||||
|
||||
[FormerlySerializedAs("showPositionText")] public bool canShowPositionText;
|
||||
public bool isShowingPositionText;
|
||||
public Transform textContainer;
|
||||
public GameObject positionTextPrefab;
|
||||
public Dictionary<GameObject, Vector3> positionTexts;
|
||||
|
||||
void Start()
|
||||
{
|
||||
positionTexts = new Dictionary<GameObject, Vector3>();
|
||||
|
||||
sceneCamera = EditorManager.instance.cameraManager.sceneCamera.sceneCamera;
|
||||
// 实例化材质,避免修改共享材质
|
||||
gridMaterial = GetComponent<MeshRenderer>().material;
|
||||
@@ -35,27 +54,103 @@ namespace Ichni.Editor
|
||||
sceneCamera = EditorManager.instance.cameraManager.currentCamera;
|
||||
|
||||
// 计算摄像机到网格平面的垂直距离
|
||||
float camDistance = 0f;
|
||||
cameraDistance = 0f;
|
||||
Vector3 camPos = sceneCamera.transform.position;
|
||||
Vector3 gridPos = transform.position;
|
||||
switch (gridPlane)
|
||||
{
|
||||
case 0: // XZ 平面:垂直方向为 Y
|
||||
camDistance = Mathf.Abs(camPos.y - gridPos.y);
|
||||
cameraDistance = Mathf.Abs(camPos.y - gridPos.y);
|
||||
break;
|
||||
case 1: // XY 平面:垂直方向为 Z
|
||||
camDistance = Mathf.Abs(camPos.z - gridPos.z);
|
||||
cameraDistance = Mathf.Abs(camPos.z - gridPos.z);
|
||||
break;
|
||||
case 2: // YZ 平面:垂直方向为 X
|
||||
camDistance = Mathf.Abs(camPos.x - gridPos.x);
|
||||
cameraDistance = Mathf.Abs(camPos.x - gridPos.x);
|
||||
break;
|
||||
}
|
||||
|
||||
// 利用对数函数计算缩放等级:距离越远,网格越大
|
||||
float logScale = Mathf.Floor(Mathf.Log(camDistance / distanceFactor + 1, 4));
|
||||
float gridScale = baseScale * Mathf.Pow(4, logScale) * scaleMultiplier;
|
||||
logScale = Mathf.Floor(Mathf.Log(cameraDistance / distanceFactor + 1, 4));
|
||||
gridScale = baseScale * Mathf.Pow(4, logScale) * scaleMultiplier;
|
||||
|
||||
gridMaterial.SetFloat("_GridScale", 1 / gridScale);
|
||||
gridMaterial.SetFloat("_DisappearEndDistance", 100 * gridScale);
|
||||
|
||||
if (canShowPositionText && isShowingPositionText)
|
||||
{
|
||||
GetPoints();
|
||||
|
||||
foreach (KeyValuePair<GameObject,Vector3> positionText in positionTexts)
|
||||
{
|
||||
positionText.Key.transform.position = positionText.Value + new Vector3(gridScale / 6, 0, gridScale / 12);
|
||||
float scaleFactor = gridScale * 1.5f;
|
||||
positionText.Key.transform.localScale = new Vector3(scaleFactor, scaleFactor, scaleFactor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GetPoints()
|
||||
{
|
||||
Ray sceneCameraRay = sceneCamera.ScreenPointToRay(new Vector2(Screen.width / 2f, Screen.height / 2f));
|
||||
if (Physics.Raycast(sceneCameraRay, out RaycastHit sceneCameraHit, float.MaxValue, LayerMask.GetMask("Grid")))
|
||||
{
|
||||
if (sceneCameraHit.collider.gameObject == gameObject)
|
||||
{
|
||||
Vector3 point = sceneCameraHit.point;
|
||||
float radius = gridScale * 16f;
|
||||
float step = gridScale * 4f;
|
||||
|
||||
float minX = point.x - radius;
|
||||
float maxX = point.x + radius;
|
||||
float minZ = point.z - radius;
|
||||
float maxZ = point.z + radius;
|
||||
|
||||
// 对于 X 与 Z 方向,根据网格间距取整,确保从整点开始
|
||||
minX = Mathf.Floor(minX / step) * step;
|
||||
maxX = Mathf.Ceil(maxX / step) * step;
|
||||
minZ = Mathf.Floor(minZ / step) * step;
|
||||
maxZ = Mathf.Ceil(maxZ / step) * step;
|
||||
|
||||
List<Vector3> newPositions = new List<Vector3>();
|
||||
|
||||
for (float x = minX; x <= maxX; x += step)
|
||||
{
|
||||
for (float z = minZ; z <= maxZ; z += step)
|
||||
{
|
||||
Vector3 position = new Vector3(x, 0, z);
|
||||
|
||||
if (!positionTexts.ContainsValue(position))
|
||||
{
|
||||
GameObject posText = LeanPool.Spawn(positionTextPrefab);
|
||||
posText.transform.position = position + new Vector3(gridScale / 8, 0, gridScale / 16);
|
||||
posText.transform.forward = -transform.up;
|
||||
posText.GetComponent<TMP_Text>().text = $"({Mathf.RoundToInt(position.x)}, {Mathf.RoundToInt(position.z)})";
|
||||
posText.transform.SetParent(textContainer);
|
||||
positionTexts.Add(posText, position);
|
||||
}
|
||||
|
||||
newPositions.Add(new Vector3(x, 0, z));
|
||||
}
|
||||
}
|
||||
|
||||
List<GameObject> toRemove = new List<GameObject>();
|
||||
// 清除不在新范围内的Text
|
||||
foreach (KeyValuePair<GameObject, Vector3> positionText in positionTexts)
|
||||
{
|
||||
if (!newPositions.Contains(positionText.Value))
|
||||
{
|
||||
LeanPool.Despawn(positionText.Key);
|
||||
toRemove.Add(positionText.Key);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (GameObject text in toRemove)
|
||||
{
|
||||
positionTexts.Remove(text);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using Ichni.RhythmGame;
|
||||
using Ichni.RhythmGame.Beatmap;
|
||||
using Lean.Pool;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Ichni.Editor
|
||||
@@ -19,11 +20,14 @@ namespace Ichni.Editor
|
||||
public bool xPlaneEnabled;
|
||||
public bool zPlaneEnabled;
|
||||
|
||||
public bool isYPlaneShowingPositionText;
|
||||
|
||||
private void Start()
|
||||
{
|
||||
yPlaneEnabled = true;
|
||||
xPlaneEnabled = false;
|
||||
zPlaneEnabled = false;
|
||||
isYPlaneShowingPositionText = true;
|
||||
RefreshPlanes();
|
||||
}
|
||||
|
||||
@@ -38,11 +42,15 @@ namespace Ichni.Editor
|
||||
var yPlaneToggle =
|
||||
inspector.GenerateToggle(this, gridSettings, "Y Plane", nameof(yPlaneEnabled))
|
||||
.AddListenerFunction(RefreshPlanes);
|
||||
var xPlaneToggle =
|
||||
inspector.GenerateToggle(this, gridSettings, "X Plane", nameof(xPlaneEnabled))
|
||||
.AddListenerFunction(RefreshPlanes);
|
||||
var zPlaneToggle =
|
||||
inspector.GenerateToggle(this, gridSettings, "Z Plane", nameof(zPlaneEnabled))
|
||||
.AddListenerFunction(RefreshPlanes);
|
||||
var xPlaneToggle =
|
||||
inspector.GenerateToggle(this, gridSettings, "X Plane", nameof(xPlaneEnabled))
|
||||
|
||||
var yPlaneShowPositionToggle =
|
||||
inspector.GenerateToggle(this, gridSettings, "Show Y Plane Position", nameof(isYPlaneShowingPositionText))
|
||||
.AddListenerFunction(RefreshPlanes);
|
||||
}
|
||||
|
||||
@@ -51,6 +59,17 @@ namespace Ichni.Editor
|
||||
yPlaneGrid.gameObject.SetActive(yPlaneEnabled);
|
||||
xPlaneGrid.gameObject.SetActive(xPlaneEnabled);
|
||||
zPlaneGrid.gameObject.SetActive(zPlaneEnabled);
|
||||
yPlaneGrid.isShowingPositionText = isYPlaneShowingPositionText;
|
||||
|
||||
if (!yPlaneGrid.isShowingPositionText)
|
||||
{
|
||||
foreach (KeyValuePair<GameObject, Vector3> positionText in yPlaneGrid.positionTexts)
|
||||
{
|
||||
LeanPool.Despawn(positionText.Key);
|
||||
}
|
||||
|
||||
yPlaneGrid.positionTexts.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -92,6 +92,7 @@ namespace Ichni
|
||||
beatmapContainer.gameElementList.ForEach(gameElement =>
|
||||
{
|
||||
gameElement.AfterInitialize();
|
||||
|
||||
gameElement.Refresh();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -35,6 +35,7 @@ namespace Ichni.Editor
|
||||
ResolutionHintsOperation();
|
||||
UIOperation();
|
||||
SwitchCameraOperation();
|
||||
ClickSelectElement();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -232,6 +233,20 @@ namespace Ichni.Editor
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ClickSelectElement()
|
||||
{
|
||||
if (Mouse.current.leftButton.wasPressedThisFrame && !isPointerOverUI)
|
||||
{
|
||||
Vector2 mousePosition = Mouse.current.position.ReadValue();
|
||||
Ray ray = EditorManager.instance.cameraManager.currentCamera.ScreenPointToRay(mousePosition);
|
||||
if (Physics.Raycast(ray, out RaycastHit hit, float.MaxValue, LayerMask.GetMask("Selectable")))
|
||||
{
|
||||
GameElement clickedElement = hit.collider.GetComponent<GameElement>();//TODO: 对于Hold这种复杂的元素,需要使用连接脚本进行获取
|
||||
(clickedElement as IHaveSelectSubmodule)?.selectSubmodule.SelectGameElement();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public partial class InputListener
|
||||
|
||||
@@ -3,6 +3,7 @@ using System.Collections.Generic;
|
||||
using Ichni.RhythmGame;
|
||||
using Ichni.RhythmGame.Beatmap;
|
||||
using UnityEngine;
|
||||
using UnityEngine.InputSystem;
|
||||
|
||||
namespace Ichni.Editor
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user