using System; using System.Collections; using System.Collections.Generic; using System.Linq; using UnityEngine; using UnityEngine.UI; using WI; namespace CHN { public class MachineKPIManager : MonoBehaviour, ISingle { public UI_MachineKPI prefab_MachineKPI; private OrbitalController cam; private Machine[] machines; private List matchingMachines = new(); private Dictionary machineKPIs = new(); private Dictionary kpiToMachines = new(); private List kpis = new List(); public float defaultNameHeight; public Vector3 originScale; public Action onClickKPIToMachine; public Action onClickKPIToData; [Range(0.1f, 0.8f)] public float minScale; [Range(0.5f, 1.5f)] public float maxScale; [Range(0.1f, 2f)] public float scaleClamp; public void Clear() { uiElements.Clear(); matchingMachines.Clear(); kpiToMachines.Clear(); } public void SetMachineKPI(List machineData) { cam = FindSingle(); prefab_MachineKPI = Resources.Load("Prefabs/UI/PRF_UI_MachineKPI"); Clear(); foreach (var data in machineData) { if (machineKPIs.ContainsKey(data.machineName)) { machineKPIs[data.machineName].SetData(data); uiElements.Add(machineKPIs[data.machineName].rectTransform); continue; } var machineKPI = Instantiate(prefab_MachineKPI, transform); machineKPI.SetData(data); machineKPI.onClickKPI += OnClickMachineKPI; machineKPI.name = data.machineName; machineKPI.SetActive(false); uiElements.Add(machineKPI.rectTransform); kpis.Add(machineKPI); machineKPIs.Add(data.machineName, machineKPI); } var building = FindSingle(); machines = building.floors.SelectMany(f => f.machines).ToArray(); foreach (var machine in machines) { if (machineKPIs.ContainsKey(machine.code)) { machine.machineKPI = machineKPIs[machine.code]; matchingMachines.Add(machine); kpiToMachines.Add(machine.machineKPI, machine); machine.SetAnimationSpeed(); } } } private void OnClickMachineKPI(UI_MachineKPI machineKPI) { machineKPI.transform.SetAsLastSibling(); var currentMachine = kpiToMachines[machineKPI]; onClickKPIToMachine?.Invoke(currentMachine); onClickKPIToData?.Invoke(machineKPI.data, currentMachine); } private void Update() { GroupOverlappingUIElements(); RangeDetection(); } public List uiElements = new List(); public List> groupedElements = new List>(); void GroupOverlappingUIElements() { foreach(var matchingMachine in matchingMachines) { var machinePos = matchingMachine.centerPos; var screenPos = cam.camera.WorldToScreenPoint(new Vector3(machinePos.x, machinePos.y + defaultNameHeight, machinePos.z)); matchingMachine.machineKPI.transform.position = screenPos; } // ±×·ìÈ­µÈ UI ¿ä¼ÒµéÀ» ÃʱâÈ­ groupedElements.Clear(); // UI ¿ä¼ÒµéÀ» °ãħ ¿©ºÎ¿¡ µû¶ó ±×·ìÈ­ var uncheckedElements = new HashSet(uiElements); // °ãħ ¿©ºÎ üũ ¾ÈµÈ UI ¿ä¼Òµé // °ãħÀ» È®ÀÎÇÒ UI ¿ä¼ÒµéÀ» ¼øÂ÷ÀûÀ¸·Î ±×·ìÈ­ while (uncheckedElements.Count > 0) { var currentElement = uncheckedElements.First(); uncheckedElements.Remove(currentElement); var group = new List { currentElement }; // ±×·ìÈ­µÈ UI ¿ä¼ÒµéÀ» Ãß°¡ var overlappingElements = uncheckedElements.Where(element => AreRectanglesOverlapping(currentElement, element)).ToList(); foreach (var overlappingElement in overlappingElements) { uncheckedElements.Remove(overlappingElement); group.Add(overlappingElement); } groupedElements.Add(group); } foreach (var group in groupedElements) { var centerPos = GroupCenterCalculate(group); for (int i = 0; i < group.Count; i++) { var kpi = group[i]; var newPos = new Vector3(centerPos.x, centerPos.y + kpi.rect.height * i * kpi.transform.localScale.y, centerPos.z); kpi.transform.localPosition = newPos; } } } private bool AreRectanglesOverlapping(RectTransform rectA, RectTransform rectB) { if (!rectB.gameObject.activeSelf) return false; if (!rectA.gameObject.activeSelf) return false; Rect rectAWorld = GetWorldRect(rectA); Rect rectBWorld = GetWorldRect(rectB); return rectAWorld.Overlaps(rectBWorld); } private Rect GetWorldRect(RectTransform rectTransform) { Vector3[] worldCorners = new Vector3[4]; rectTransform.GetWorldCorners(worldCorners); Vector2 min = new Vector2(worldCorners[0].x, worldCorners[0].y); Vector2 max = new Vector2(worldCorners[2].x, worldCorners[2].y); return new Rect(min, max - min); } private Vector3 GroupCenterCalculate(List group) { var centerPos = Vector3.zero; group.Sort((a, b) => a.transform.localPosition.y.CompareTo(b.transform.localPosition.y)); foreach (var kpi in group) { centerPos += kpi.transform.localPosition; } centerPos /= group.Count; return centerPos; } void RangeDetection() { var layerMask = LayerMask.GetMask("Camera", "Floor Wall"); var currentFloor = FindSingle().currentFloor; float t = Mathf.InverseLerp(cam.option.maxDistance, 0f, cam.option.currentDistance); float scale = Mathf.Lerp(minScale, maxScale, t); var newScale = new Vector3(scale, scale, scale); foreach (var machine in matchingMachines) { if (machine.GetComponentInParent() != currentFloor) { machine.machineKPI.Deactive(); continue; } MachineKPIsActive(machine, layerMask); var machineKPI = machine.machineKPI; machineKPI.transform.localScale = newScale; } } bool IsScreenRange(Machine machine) { Vector3 viewPos = cam.camera.WorldToViewportPoint(machine.centerPos); if (viewPos.x >= 0 && viewPos.x <= 1 && viewPos.y >= 0 && viewPos.y <= 1 && viewPos.z > 0) { return true; } return false; } void MachineKPIsActive(Machine machine, LayerMask layerMask) { var dir = cam.camera.transform.position - machine.centerPos; var hit = new RaycastHit(); if (Physics.Raycast(machine.centerPos, dir, out hit, Mathf.Infinity, layerMask)) { var hitCameraLayer = hit.collider.gameObject.layer.Equals(LayerMask.NameToLayer("Camera")); if (hitCameraLayer) { if (!IsScreenRange(machine)) { machine.machineKPI.Deactive(); } else { machine.machineKPI.Active(); } } else { machine.machineKPI.Deactive(); } } } } }