Files
Studio/Assets/Scripts/XED/Building/ObjectDistanceLine.cs
2025-02-19 17:43:14 +09:00

214 lines
7.3 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEditor;
using WI;
using XED.UI;
namespace XED.UI
{
//TODO::areabox가 아니라 TwinObject를 대상으로 동작하도록 수정
public class ObjectDistanceLine : MonoBehaviour, ISingle
{
private Vector3 centerPoint = new();
private Vector3[] edges = new Vector3[4];
private LineRenderer[] lineRenderers = new LineRenderer[4];
private int[] distances = new int[4];
private Vector3[] hits = new Vector3[4];
[SerializeField]
private float thickness;
private int rot;
private Dictionary<Vector3, Vector3> dirTable = new();
TwinObject target;
HashSet<TwinObject> targets = new();
public event Action onTargetEvent;
public event Action onTargetMissing;
public event Action<Vector3, int, int> onLineUpdate;
public void Initialize()
{
var lineMat = Resources.Load<Material>("Materials/Mat_LineRender");
for (int i = 0; i < lineRenderers.Length; i++)
{
lineRenderers[i] = new GameObject("DistanceLine").AddComponent<LineRenderer>();
lineRenderers[i].transform.SetParent(transform, true);
lineRenderers[i].material = lineMat;
lineRenderers[i].positionCount = 2;
lineRenderers[i].startWidth = thickness;
lineRenderers[i].endWidth = thickness;
}
}
public void SetTarget(TwinObject target)
{
if (target is Wall || target is WallGroup)
return;
this.target = target;
targets.Add(target);
gameObject.SetActive(true);
onTargetEvent?.Invoke();
}
private void EdgePoints()
{
var size = target.physics.areabox.bounds.size;
edges[0] = centerPoint + EdgePointCalculate(-size.x, -size.z, centerPoint);
edges[1] = centerPoint + EdgePointCalculate(size.x, -size.z, centerPoint);
edges[2] = centerPoint + EdgePointCalculate(size.x, size.z, centerPoint);
edges[3] = centerPoint + EdgePointCalculate(-size.x, size.z, centerPoint);
}
private void CenterPoints()
{
var size = target.physics.areabox.bounds.size;
edges[0] = centerPoint + EdgePointCalculate(-size.x, 0f, centerPoint);
edges[1] = centerPoint + EdgePointCalculate(size.x, 0f, centerPoint);
edges[2] = centerPoint + EdgePointCalculate(0f, -size.z, centerPoint);
edges[3] = centerPoint + EdgePointCalculate(0f, size.z, centerPoint);
}
private Vector3 EdgePointCalculate(float xValue, float zValue, Vector3 center)
{
//대각선의 길이,
//중점길이
var point = center + (new Vector3(xValue, 0, zValue) * 0.5f);
var length = Vector3.Distance(point, center);
var dir = (point - center).normalized * length;
//var pos = RotateVector3(dir, rot);
var pos = Quaternion.AngleAxis(rot, Vector3.up) * dir;
return pos;
}
public void Off()
{
targets.Clear();
onTargetMissing?.Invoke();
}
void LineRenderSetActive(bool isActive)
{
foreach(var line in lineRenderers)
{
line.enabled = isActive;
}
}
private void Update()
{
if (target == null || targets.Count >1)
{
Off();
LineRenderSetActive(false);
return;
}
//if (areabox == null)
// return;
//Distance가 4개 모두 0이면 return?
LineRenderSetActive(true);
rot = Mathf.RoundToInt(target.physics.areabox.transform.localEulerAngles.y);
centerPoint = new Vector3(target.physics.areabox.bounds.center.x, 0, target.physics.areabox.bounds.center.z);
var angle = rot % 90f;
if (Mathf.Approximately(angle, 0f))
CenterPoints();
else
EdgePoints();
var xOrder = edges.OrderBy(l => l.x);
var zOrder = edges.OrderBy(l => l.z);
dirTable.Clear();
dirTable.TryAdd(xOrder.ElementAt(0), Vector3.left);
dirTable.TryAdd(xOrder.ElementAt(3), Vector3.right);
dirTable.TryAdd(zOrder.ElementAt(0), Vector3.back);
dirTable.TryAdd(zOrder.ElementAt(3), Vector3.forward);
for (int i = 0; i < edges.Length; i++)
{
//var dir = Dir(dirTable[edges[i]]);
var dir = dirTable[edges[i]];
if (Physics.Raycast(edges[i], dir, out RaycastHit hit, Mathf.Infinity))
{
hits[i] = hit.point;
}
else
{
hits[i] = edges[i];
}
distances[i] = Mathf.RoundToInt(Vector3.Distance(hits[i], edges[i]) * 1000f);
lineRenderers[i].SetPosition(0, edges[i]);
lineRenderers[i].SetPosition(1, hits[i]);
//3방향중 점중 가장 짧은거
var pos = DistanceTextPos(edges[i], hits[i]);
if (Mathf.Abs(pos.y) == Mathf.Infinity || float.IsNaN(pos.y))
continue;
onLineUpdate?.Invoke(pos, i, distances[i]);
}
}
private Vector3 DistanceTextPos(Vector3 edge, Vector3 hit)
{
var x = Screen.width;
var y = Screen.height;
var hitpoint = Camera.main.WorldToScreenPoint(hit);
var edgePoint = Camera.main.WorldToScreenPoint(edge);
//카메라 끝점 edge와 hit 사이;
//기울기 가져온다
var m = (hitpoint.y - edgePoint.y) / (hitpoint.x - edgePoint.x);
if (hitpoint.y > y)
{
var yPos = y;
var xPos = ((yPos - edgePoint.y) / m) + edgePoint.x;
hitpoint = new Vector3(xPos, yPos);
}
else if (hitpoint.y < 0)
{
var yPos = 0;
var xPos = ((yPos - edgePoint.y) / m) + edgePoint.x;
hitpoint = new Vector3(xPos, yPos);
}
if (hitpoint.x > x)
{
var xPos = x;
var yPos = (m * (xPos - edgePoint.x)) + edgePoint.y;
hitpoint = new Vector3(xPos, yPos);
}
else if (hitpoint.x < 0)
{
var xPos = 0;
var yPos = (m * (xPos - edgePoint.x)) + edgePoint.y;
hitpoint = new Vector3(xPos, yPos);
}
var center = (hitpoint + edgePoint) * 0.5f;
return center;
}
void Gizmo()
{
#if UNITY_EDITOR
// for (int i = 0; i < edges.Length; ++i)
// {
// Gizmos.DrawSphere(edges[i].pos, 0.1f);
// }
// for (int i = 0; i < hits.Length; ++i)
// {
// var dir = Dir(dirTable[edges[i]]);
// Handles.color = Color.yellow;
// Handles.DrawLine(edges[i].pos, hits[i], 1);
// Gizmos.DrawRay(edges[i].pos, dir * 10f);
// }
#endif
}
}
}