diff --git a/.vscode/extensions.json b/.vscode/extensions.json
new file mode 100644
index 0000000..ddb6ff8
--- /dev/null
+++ b/.vscode/extensions.json
@@ -0,0 +1,5 @@
+{
+ "recommendations": [
+ "visualstudiotoolsforunity.vstuc"
+ ]
+}
diff --git a/.vscode/launch.json b/.vscode/launch.json
new file mode 100644
index 0000000..da60e25
--- /dev/null
+++ b/.vscode/launch.json
@@ -0,0 +1,10 @@
+{
+ "version": "0.2.0",
+ "configurations": [
+ {
+ "name": "Attach to Unity",
+ "type": "vstuc",
+ "request": "attach"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/.vscode/settings.json b/.vscode/settings.json
new file mode 100644
index 0000000..95dae1b
--- /dev/null
+++ b/.vscode/settings.json
@@ -0,0 +1,71 @@
+{
+ "files.exclude": {
+ "**/.DS_Store": true,
+ "**/.git": true,
+ "**/.vs": true,
+ "**/.gitmodules": true,
+ "**/.vsconfig": true,
+ "**/*.booproj": true,
+ "**/*.pidb": true,
+ "**/*.suo": true,
+ "**/*.user": true,
+ "**/*.userprefs": true,
+ "**/*.unityproj": true,
+ "**/*.dll": true,
+ "**/*.exe": true,
+ "**/*.pdf": true,
+ "**/*.mid": true,
+ "**/*.midi": true,
+ "**/*.wav": true,
+ "**/*.gif": true,
+ "**/*.ico": true,
+ "**/*.jpg": true,
+ "**/*.jpeg": true,
+ "**/*.png": true,
+ "**/*.psd": true,
+ "**/*.tga": true,
+ "**/*.tif": true,
+ "**/*.tiff": true,
+ "**/*.3ds": true,
+ "**/*.3DS": true,
+ "**/*.fbx": true,
+ "**/*.FBX": true,
+ "**/*.lxo": true,
+ "**/*.LXO": true,
+ "**/*.ma": true,
+ "**/*.MA": true,
+ "**/*.obj": true,
+ "**/*.OBJ": true,
+ "**/*.asset": true,
+ "**/*.cubemap": true,
+ "**/*.flare": true,
+ "**/*.mat": true,
+ "**/*.meta": true,
+ "**/*.prefab": true,
+ "**/*.unity": true,
+ "build/": true,
+ "Build/": true,
+ "Library/": true,
+ "library/": true,
+ "obj/": true,
+ "Obj/": true,
+ "Logs/": true,
+ "logs/": true,
+ "ProjectSettings/": true,
+ "UserSettings/": true,
+ "temp/": true,
+ "Temp/": true
+ },
+ "files.associations": {
+ "*.asset": "yaml",
+ "*.meta": "yaml",
+ "*.prefab": "yaml",
+ "*.unity": "yaml",
+ },
+ "explorer.fileNesting.enabled": true,
+ "explorer.fileNesting.patterns": {
+ "*.sln": "*.csproj",
+ "*.slnx": "*.csproj"
+ },
+ "dotnet.defaultSolution": "AZTECH_WB.slnx"
+}
\ No newline at end of file
diff --git a/AZTECH_WB.slnx b/AZTECH_WB.slnx
new file mode 100644
index 0000000..ea9ecb2
--- /dev/null
+++ b/AZTECH_WB.slnx
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Assets/DownloadAssets/XRLib/Resources/Prefabs/UI/Toolbar/images/ic_floor_active.png b/Assets/DownloadAssets/XRLib/Resources/Prefabs/UI/Toolbar/images/ic_floor_active.png
new file mode 100644
index 0000000..20832f5
Binary files /dev/null and b/Assets/DownloadAssets/XRLib/Resources/Prefabs/UI/Toolbar/images/ic_floor_active.png differ
diff --git a/Assets/DownloadAssets/XRLib/Resources/Prefabs/UI/Toolbar/images/ic_floor_active.png.meta b/Assets/DownloadAssets/XRLib/Resources/Prefabs/UI/Toolbar/images/ic_floor_active.png.meta
new file mode 100644
index 0000000..5ed9031
--- /dev/null
+++ b/Assets/DownloadAssets/XRLib/Resources/Prefabs/UI/Toolbar/images/ic_floor_active.png.meta
@@ -0,0 +1,117 @@
+fileFormatVersion: 2
+guid: e9074fab59981e24ea177f1361c7dd5f
+TextureImporter:
+ internalIDToNameTable: []
+ externalObjects: {}
+ serializedVersion: 13
+ mipmaps:
+ mipMapMode: 0
+ enableMipMap: 0
+ sRGBTexture: 1
+ linearTexture: 0
+ fadeOut: 0
+ borderMipMap: 0
+ mipMapsPreserveCoverage: 0
+ alphaTestReferenceValue: 0.5
+ mipMapFadeDistanceStart: 1
+ mipMapFadeDistanceEnd: 3
+ bumpmap:
+ convertToNormalMap: 0
+ externalNormalMap: 0
+ heightScale: 0.25
+ normalMapFilter: 0
+ flipGreenChannel: 0
+ isReadable: 0
+ streamingMipmaps: 0
+ streamingMipmapsPriority: 0
+ vTOnly: 0
+ ignoreMipmapLimit: 0
+ grayScaleToAlpha: 0
+ generateCubemap: 6
+ cubemapConvolution: 0
+ seamlessCubemap: 0
+ textureFormat: 1
+ maxTextureSize: 2048
+ textureSettings:
+ serializedVersion: 2
+ filterMode: 1
+ aniso: 1
+ mipBias: 0
+ wrapU: 1
+ wrapV: 1
+ wrapW: 0
+ nPOTScale: 0
+ lightmap: 0
+ compressionQuality: 50
+ spriteMode: 1
+ spriteExtrude: 1
+ spriteMeshType: 1
+ alignment: 0
+ spritePivot: {x: 0.5, y: 0.5}
+ spritePixelsToUnits: 100
+ spriteBorder: {x: 0, y: 0, z: 0, w: 0}
+ spriteGenerateFallbackPhysicsShape: 1
+ alphaUsage: 1
+ alphaIsTransparency: 1
+ spriteTessellationDetail: -1
+ textureType: 8
+ textureShape: 1
+ singleChannelComponent: 0
+ flipbookRows: 1
+ flipbookColumns: 1
+ maxTextureSizeSet: 0
+ compressionQualitySet: 0
+ textureFormatSet: 0
+ ignorePngGamma: 0
+ applyGammaDecoding: 0
+ swizzle: 50462976
+ cookieLightType: 0
+ platformSettings:
+ - serializedVersion: 4
+ buildTarget: DefaultTexturePlatform
+ maxTextureSize: 2048
+ resizeAlgorithm: 0
+ textureFormat: -1
+ textureCompression: 1
+ compressionQuality: 50
+ crunchedCompression: 0
+ allowsAlphaSplitting: 0
+ overridden: 0
+ ignorePlatformSupport: 0
+ androidETC2FallbackOverride: 0
+ forceMaximumCompressionQuality_BC6H_BC7: 0
+ - serializedVersion: 4
+ buildTarget: Standalone
+ maxTextureSize: 2048
+ resizeAlgorithm: 0
+ textureFormat: -1
+ textureCompression: 1
+ compressionQuality: 50
+ crunchedCompression: 0
+ allowsAlphaSplitting: 0
+ overridden: 0
+ ignorePlatformSupport: 0
+ androidETC2FallbackOverride: 0
+ forceMaximumCompressionQuality_BC6H_BC7: 0
+ spriteSheet:
+ serializedVersion: 2
+ sprites: []
+ outline: []
+ customData:
+ physicsShape: []
+ bones: []
+ spriteID: 5e97eb03825dee720800000000000000
+ internalID: 0
+ vertices: []
+ indices:
+ edges: []
+ weights: []
+ secondaryTextures: []
+ spriteCustomMetadata:
+ entries: []
+ nameFileIdTable: {}
+ mipmapLimitGroupName:
+ pSDRemoveMatte: 0
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/DownloadAssets/XRLib/Resources/Prefabs/UI/Toolbar/images/ic_floor_deactive.png b/Assets/DownloadAssets/XRLib/Resources/Prefabs/UI/Toolbar/images/ic_floor_deactive.png
new file mode 100644
index 0000000..8e3e886
Binary files /dev/null and b/Assets/DownloadAssets/XRLib/Resources/Prefabs/UI/Toolbar/images/ic_floor_deactive.png differ
diff --git a/Assets/DownloadAssets/XRLib/Resources/Prefabs/UI/Toolbar/images/ic_floor_deactive.png.meta b/Assets/DownloadAssets/XRLib/Resources/Prefabs/UI/Toolbar/images/ic_floor_deactive.png.meta
new file mode 100644
index 0000000..ff3abe1
--- /dev/null
+++ b/Assets/DownloadAssets/XRLib/Resources/Prefabs/UI/Toolbar/images/ic_floor_deactive.png.meta
@@ -0,0 +1,117 @@
+fileFormatVersion: 2
+guid: 9458e5537061e7945a771b442a1d3413
+TextureImporter:
+ internalIDToNameTable: []
+ externalObjects: {}
+ serializedVersion: 13
+ mipmaps:
+ mipMapMode: 0
+ enableMipMap: 0
+ sRGBTexture: 1
+ linearTexture: 0
+ fadeOut: 0
+ borderMipMap: 0
+ mipMapsPreserveCoverage: 0
+ alphaTestReferenceValue: 0.5
+ mipMapFadeDistanceStart: 1
+ mipMapFadeDistanceEnd: 3
+ bumpmap:
+ convertToNormalMap: 0
+ externalNormalMap: 0
+ heightScale: 0.25
+ normalMapFilter: 0
+ flipGreenChannel: 0
+ isReadable: 0
+ streamingMipmaps: 0
+ streamingMipmapsPriority: 0
+ vTOnly: 0
+ ignoreMipmapLimit: 0
+ grayScaleToAlpha: 0
+ generateCubemap: 6
+ cubemapConvolution: 0
+ seamlessCubemap: 0
+ textureFormat: 1
+ maxTextureSize: 2048
+ textureSettings:
+ serializedVersion: 2
+ filterMode: 1
+ aniso: 1
+ mipBias: 0
+ wrapU: 1
+ wrapV: 1
+ wrapW: 0
+ nPOTScale: 0
+ lightmap: 0
+ compressionQuality: 50
+ spriteMode: 1
+ spriteExtrude: 1
+ spriteMeshType: 1
+ alignment: 0
+ spritePivot: {x: 0.5, y: 0.5}
+ spritePixelsToUnits: 100
+ spriteBorder: {x: 0, y: 0, z: 0, w: 0}
+ spriteGenerateFallbackPhysicsShape: 1
+ alphaUsage: 1
+ alphaIsTransparency: 1
+ spriteTessellationDetail: -1
+ textureType: 8
+ textureShape: 1
+ singleChannelComponent: 0
+ flipbookRows: 1
+ flipbookColumns: 1
+ maxTextureSizeSet: 0
+ compressionQualitySet: 0
+ textureFormatSet: 0
+ ignorePngGamma: 0
+ applyGammaDecoding: 0
+ swizzle: 50462976
+ cookieLightType: 0
+ platformSettings:
+ - serializedVersion: 4
+ buildTarget: DefaultTexturePlatform
+ maxTextureSize: 2048
+ resizeAlgorithm: 0
+ textureFormat: -1
+ textureCompression: 1
+ compressionQuality: 50
+ crunchedCompression: 0
+ allowsAlphaSplitting: 0
+ overridden: 0
+ ignorePlatformSupport: 0
+ androidETC2FallbackOverride: 0
+ forceMaximumCompressionQuality_BC6H_BC7: 0
+ - serializedVersion: 4
+ buildTarget: Standalone
+ maxTextureSize: 2048
+ resizeAlgorithm: 0
+ textureFormat: -1
+ textureCompression: 1
+ compressionQuality: 50
+ crunchedCompression: 0
+ allowsAlphaSplitting: 0
+ overridden: 0
+ ignorePlatformSupport: 0
+ androidETC2FallbackOverride: 0
+ forceMaximumCompressionQuality_BC6H_BC7: 0
+ spriteSheet:
+ serializedVersion: 2
+ sprites: []
+ outline: []
+ customData:
+ physicsShape: []
+ bones: []
+ spriteID: 5e97eb03825dee720800000000000000
+ internalID: 0
+ vertices: []
+ indices:
+ edges: []
+ weights: []
+ secondaryTextures: []
+ spriteCustomMetadata:
+ entries: []
+ nameFileIdTable: {}
+ mipmapLimitGroupName:
+ pSDRemoveMatte: 0
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Scenes/Main.unity b/Assets/Scenes/Main.unity
index 236ade6..8e992f8 100644
--- a/Assets/Scenes/Main.unity
+++ b/Assets/Scenes/Main.unity
@@ -496,15 +496,17 @@ MonoBehaviour:
m_Name:
m_EditorClassIdentifier:
isInit: 0
- machineStatusItem: {fileID: 0}
- machines: []
- defaultNameHeight: 1.5
- originScale: {x: 1, y: 1, z: 1}
- activeIconEnable: 0
- minScale: 0.6
- maxScale: 1.2
- scaleClamp: 1
- uiElements: []
+ worldOffset: {x: 0, y: 1.5, z: 0}
+ verticalPadding: 0
+ overlapThreshold: 2
+ smoothSpeed: 50
+ occlusionLayer:
+ serializedVersion: 2
+ m_Bits: 0
+ occlusionCheckInterval: 0.1
+ minScale: 1.2
+ maxScale: 1.4
+ scaleSmoothing: 5
--- !u!114 &69824778
MonoBehaviour:
m_ObjectHideFlags: 0
diff --git a/Assets/Scripts/Camera/OrbitalController.cs b/Assets/Scripts/Camera/OrbitalController.cs
index e7a617c..4b47654 100644
--- a/Assets/Scripts/Camera/OrbitalController.cs
+++ b/Assets/Scripts/Camera/OrbitalController.cs
@@ -175,7 +175,7 @@ namespace AZTECHWB
var moveVector = camera.transform.TransformDirection(x, y, -z);
moveVector.y = 0;
- //0.5 값은 카메라 속도 보정값
+ //0.5 값?? 카메? ?? 보정?
moveVector *= (moveSpeed * 0.5f) * (currentDistance / maxDistance);
nextPosition = cameraPivot.transform.position - moveVector;
}
@@ -303,10 +303,5 @@ namespace AZTECHWB
break;
}
}
-
- internal void SetControllOptionValue()
- {
- throw new NotImplementedException();
- }
}
}
\ No newline at end of file
diff --git a/Assets/Scripts/Command/ScreenHistoryCommand/ScreenResetCommand.cs b/Assets/Scripts/Command/ScreenHistoryCommand/ScreenResetCommand.cs
index 54dee57..5a4138c 100644
--- a/Assets/Scripts/Command/ScreenHistoryCommand/ScreenResetCommand.cs
+++ b/Assets/Scripts/Command/ScreenHistoryCommand/ScreenResetCommand.cs
@@ -15,6 +15,9 @@ namespace AZTECHWB.Command
var raycaster = AZTECHSceneMain.Instance.GetManager();
raycaster.SetInteractable(false);
+ //var building = AZTECHSceneMain.Instance.building;
+ //building.ActiveRoof(true);
+
var machineStatusItemManager = AZTECHSceneMain.Instance.GetManager();
machineStatusItemManager.SetActiveIcons(false);
machineStatusItemManager.SetInteractableIcons(true);
diff --git a/Assets/Scripts/Manager/LibraryManager.cs b/Assets/Scripts/Manager/LibraryManager.cs
index 3f95e2d..7956444 100644
--- a/Assets/Scripts/Manager/LibraryManager.cs
+++ b/Assets/Scripts/Manager/LibraryManager.cs
@@ -105,7 +105,7 @@ namespace AZTECHWB.Management
machines = building.floors.SelectMany(f => f.machines).ToArray();
foreach (var info in standardInfos)
{
- var p = machines.Where(x => x.machineName.Equals(info.code)).FirstOrDefault();
+ var p = machines.FirstOrDefault(x => x.machineName.Equals(info.code));
if (p == null)
continue;
p.typeOptions = info.filterInfo;
diff --git a/Assets/Scripts/Manager/MachineStatusItemManager.cs b/Assets/Scripts/Manager/MachineStatusItemManager.cs
index 191996a..4aaa4cf 100644
--- a/Assets/Scripts/Manager/MachineStatusItemManager.cs
+++ b/Assets/Scripts/Manager/MachineStatusItemManager.cs
@@ -1,7 +1,7 @@
using AZTECHWB.Command;
using AZTECHWB.Constants;
+using AZTECHWB.Core;
using Cysharp.Threading.Tasks;
-using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
@@ -10,111 +10,50 @@ namespace AZTECHWB.Management
{
public class MachineStatusItemManager : Manager
{
- public MachineStatusItem machineStatusItem;
+ [Header("References")]
+ private MachineStatusItem machineStatusItem;
- private OrbitalController controller;
- private Building building;
- public Machine[] machines;
- private List matchingMachines = new();
+ [Header("Position Settings")]
+ public Vector3 worldOffset = new Vector3(0, 1.5f, 0);
+ public float verticalPadding = 5f;
+ public float overlapThreshold = 2f;
+ public float smoothSpeed = 10f;
+ [Header("Visibility Settings")]
+ public LayerMask occlusionLayer;
+ public float occlusionCheckInterval = 0.1f;
+
+ private Dictionary smoothedPositions = new Dictionary();
+ private float occlusionTimer;
+
+ [Header("Scale Settings")]
+ public float minScale = 0.5f; // 가장 멀 때 크기
+ public float maxScale = 1.2f; // 가장 가까울 때 크기
+ public float scaleSmoothing = 5f; // 스케일 변화 스무딩 속도
+ private float maxDistance = 50f; // 스케일 계산 기준 최대 거리
+
+ private Dictionary smoothedScales = new Dictionary();
private Dictionary machineIcons = new();
private Dictionary iconToMachines = new();
-
- public float defaultNameHeight;
- public Vector3 originScale;
-
- public bool activeIconEnable;
-
- [Range(0.1f, 0.8f)]
- public float minScale;
- [Range(0.5f, 1.5f)]
- public float maxScale;
- [Range(0.1f, 2f)]
- public float scaleClamp;
+ private OrbitalController controller;
+ private Machine[] machines;
+ private bool activeIconEnable;
public override async UniTask Init()
{
- controller = FindAnyObjectByType();
machineStatusItem = Resources.Load($"{ResourceURL.uiPrefabFolderPath}{nameof(MachineStatusItem)}");
- building = FindAnyObjectByType();
+ controller = AZTECHAppMain.Instance.cameraController;
+ machines = AZTECHSceneMain.Instance.building.floors.SelectMany(f => f.machines).ToArray();
+
+ maxDistance = controller.maxDistance;
await UniTask.CompletedTask;
}
- public void Clear()
- {
- uiElements.Clear();
- matchingMachines.Clear();
- iconToMachines.Clear();
- }
- public void SetMachineData(List machineData)
- {
- Clear();
-
- if (machines.Length <= 0)
- {
- machines = building.floors.SelectMany(f => f.machines).ToArray();
- }
-
- foreach (var data in machineData)
- {
- var machine = FindMachineWithCode(data.worknm);
-
- if (machine == null)
- continue;
-
- if (machineIcons.ContainsKey(data.worknm))
- {
- machineIcons[data.worknm].SetData(data, machine.machineName);
- uiElements.Add(machineIcons[data.worknm].rectTransform);
- continue;
- }
-
- var machineIcon = Instantiate(machineStatusItem, transform);
- machineIcon.Init(data.worknm);
- machineIcon.SetData(data, machine.machineName);
- machineIcon.onClickIcon += OnClickMachineKPI;
-
- machineIcon.gameObject.SetActive(false);
- uiElements.Add(machineIcon.rectTransform);
-
- machineIcons.Add(data.worknm, machineIcon);
- }
-
- foreach (var machine in machines)
- {
- if (machineIcons.ContainsKey(machine.machineName))
- {
- machine.machineStatusItem = machineIcons[machine.machineName];
- matchingMachines.Add(machine);
- iconToMachines.Add(machine.machineStatusItem, machine);
- }
- }
- }
- private Machine FindMachineWithCode(string worknm)
- {
- Machine machine = null;
- for (int i = 0; i < machines.Length; i++)
- {
- if (machines[i].machineName == worknm)
- {
- machine = machines[i];
- break;
- }
- }
- return machine;
- }
- private void OnClickMachineKPI(MachineStatusItem machineKPI)
- {
- machineKPI.transform.SetAsLastSibling();
- var currentMachine = iconToMachines[machineKPI];
-
- new OpenMachineDetailDashboardPanelCommand(currentMachine, machineKPI.data).Execute();
- }
public void SetInteractableIcons(bool isActive)
{
- foreach(var machineIcon in machineIcons.Values)
+ foreach (var machineIcon in machineIcons.Values)
{
machineIcon.isInteractable = isActive;
}
@@ -129,148 +68,232 @@ namespace AZTECHWB.Management
}
}
+
+ public void SetMachineData(List machineData)
+ {
+ foreach (var data in machineData)
+ {
+ var machine = FindMachineWithCode(data.worknm);
+
+ if (machine == null)
+ continue;
+
+ if (machineIcons.ContainsKey(data.worknm))
+ {
+ machineIcons[data.worknm].SetData(data);
+ continue;
+ }
+
+ var machineIcon = Instantiate(machineStatusItem, transform);
+ machineIcon.Init(machine);
+ machineIcon.SetData(data);
+ machineIcon.onClickIcon += OnClickMachineKPI;
+
+ machineIcons.Add(data.worknm, machineIcon);
+ machineIcon.gameObject.SetActive(false);
+
+ machine.machineStatusItem = machineIcon;
+ iconToMachines.Add(machineIcon, machine);
+ }
+ }
+
+ private void OnClickMachineKPI(MachineStatusItem machineKPI)
+ {
+ machineKPI.transform.SetAsLastSibling();
+ var currentMachine = iconToMachines[machineKPI];
+
+ new OpenMachineDetailDashboardPanelCommand(currentMachine, machineKPI.data).Execute();
+ }
+
+ private Machine FindMachineWithCode(string worknm)
+ {
+ Machine machine = null;
+ for (int i = 0; i < machines.Length; i++)
+ {
+ if (machines[i].machineName == worknm)
+ {
+ machine = machines[i];
+ break;
+ }
+ }
+ return machine;
+ }
private void LateUpdate()
{
- GroupOverlappingUIElements();
- RangeDetection();
+ if (machineIcons.Values.Count == 0 || controller.Camera == null)
+ return;
+
+ List allLabels = new List(machineIcons.Values);
+
+ // 1단계: 전체 라벨 스크린 위치 & 거리 계산
+ UpdateScreenData(allLabels);
+
+ // 2단계: 활성화 여부 판정 (화면 이탈 + 가려짐)
+ occlusionTimer += Time.deltaTime;
+ if (occlusionTimer >= occlusionCheckInterval)
+ {
+ UpdateVisibility(allLabels);
+ occlusionTimer = 0f;
+ }
+ // 3단계: 활성화된 라벨만 카메라 거리순 정렬
+ List visibleLabels = allLabels.FindAll(l => l.gameObject.activeSelf);
+ visibleLabels.Sort((a, b) => a.distanceToCamera.CompareTo(b.distanceToCamera));
+
+ // 4단계: 거리에 따른 UI 크기 조정
+ UpdateScales(visibleLabels);
+ // 5단계: 겹침 해소 → 목표 위치 계산
+ Dictionary targetPositions = ResolveOverlaps(visibleLabels);
+ // 6단계: 스무딩 적용 후 RectTransform에 반영
+ ApplyPositions(targetPositions);
}
- public List uiElements = new List();
- public List> groupedElements = new List>();
- void GroupOverlappingUIElements()
+ void UpdateScreenData(List labels)
{
- foreach (var matchingMachine in matchingMachines)
+ foreach (var label in labels)
{
- var machinePos = matchingMachine.centerPos;
- var screenPos = controller.Camera.WorldToScreenPoint(new Vector3(machinePos.x, machinePos.y + defaultNameHeight, machinePos.z));
+ if (label.targetTransform == null) continue;
- matchingMachine.machineStatusItem.transform.position = screenPos;
- }
- groupedElements.Clear();
- var uncheckedElements = new List(uiElements);
+ Vector3 worldPos = label.targetTransform.position + worldOffset;
+ Vector3 screenPos = controller.Camera.WorldToScreenPoint(worldPos);
- while (uncheckedElements.Count > 0)
- {
- var currentElement = uncheckedElements[0];
- uncheckedElements.RemoveAt(0);
- var group = new List { currentElement };
-
- for (int i = uncheckedElements.Count - 1; i >= 0; i--)
- {
- if (AreRectanglesOverlapping(currentElement, uncheckedElements[i]))
- {
- group.Add(uncheckedElements[i]);
- uncheckedElements.RemoveAt(i);
- }
- }
-
- 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;
- }
+ label.screenPosition = new Vector2(screenPos.x, screenPos.y);
+ label.distanceToCamera = Vector3.Distance(controller.Camera.transform.position, label.targetTransform.position);
}
}
- 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 static Vector3[] worldCorners = new Vector3[4];
- private Rect GetWorldRect(RectTransform rectTransform)
- {
- rectTransform.GetWorldCorners(worldCorners);
- Vector2 min = worldCorners[0];
- Vector2 max = worldCorners[2];
- 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");
-
- float t = Mathf.InverseLerp(controller.maxDistance, 0f, controller.currentDistance);
- float scale = Mathf.Lerp(minScale, maxScale, t);
- var newScale = new Vector3(scale, scale, scale);
-
- float expandScale = Mathf.Lerp(1.6f, 1f, t);
- var newExpandScale = new Vector3(expandScale, expandScale, expandScale);
-
- foreach (var machine in matchingMachines)
- {
- MachineKPIsActive(machine, layerMask);
- var machineKPI = machine.machineStatusItem;
- machineKPI.transform.localScale = newScale;
- machineKPI.Expand_KPI.transform.localScale = newExpandScale;
- }
- }
- bool IsScreenRange(Machine machine)
- {
- Vector3 viewPos = controller.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)
+ void UpdateVisibility(List labels)
{
if (!activeIconEnable) return;
- var dir = (controller.Camera.transform.position - machine.centerPos).normalized;
- var radius = 1f;
- var ray = new Ray(machine.centerPos, dir);
-
- if (Physics.SphereCast(ray, radius, out RaycastHit hit, Mathf.Infinity, layerMask))
+ foreach (var label in labels)
{
- var hitCameraLayer = hit.collider.gameObject.layer.Equals(LayerMask.NameToLayer("Camera"));
+ // 카메라 뒤쪽 체크
+ Vector3 screenPos = controller.Camera.WorldToScreenPoint(label.targetTransform.position + worldOffset);
+ if (screenPos.z < 0)
+ {
+ label.gameObject.SetActive(false);
+ continue;
+ }
- if (hitCameraLayer)
+ // 화면 이탈 체크
+ if (!IsOnScreen(label.screenPosition))
{
- if (!IsScreenRange(machine))
- {
- machine.machineStatusItem.Deactive();
- }
- else
- {
- machine.machineStatusItem.Active();
- }
+ label.gameObject.SetActive(false);
+ continue;
}
- else
+
+ // 특정 레이어에 가려짐 체크
+ if (IsOccluded(label.targetTransform.position))
{
- machine.machineStatusItem.Deactive();
+ label.gameObject.SetActive(false);
+ continue;
}
+
+ label.gameObject.SetActive(true);
}
}
+
+ bool IsOnScreen(Vector2 screenPos)
+ {
+ return screenPos.x >= 0 && screenPos.x <= Screen.width && screenPos.y >= 0 && screenPos.y <= Screen.height;
+ }
+
+ bool IsOccluded(Vector3 targetWorldPos)
+ {
+ Vector3 camPos = controller.Camera.transform.position;
+ Vector3 direction = targetWorldPos - camPos;
+ float distance = direction.magnitude;
+
+ return Physics.Raycast(camPos, direction.normalized, distance - 0.1f, occlusionLayer);
+ }
+
+ Dictionary ResolveOverlaps(List visibleLabels)
+ {
+ Dictionary targetPositions = new Dictionary();
+ List placedRects = new List();
+
+ foreach (var label in visibleLabels)
+ {
+ Vector2 size = label.GetSize();
+ Vector2 targetPos = label.screenPosition;
+
+ bool overlapping = true;
+ int maxIterations = 20;
+
+ while (overlapping && maxIterations-- > 0)
+ {
+ overlapping = false;
+ Rect currentRect = GetRect(targetPos, size);
+
+ foreach (Rect placed in placedRects)
+ {
+ if (IsOverlapping(currentRect, placed))
+ {
+ // X는 겹친 라벨의 X로 고정, Y만 위로 밀어올림
+ targetPos.x = placed.center.x;
+ targetPos.y = placed.yMax + verticalPadding + size.y * 0.5f;
+ overlapping = true;
+ break;
+ }
+ }
+ }
+
+ targetPositions[label] = targetPos;
+ placedRects.Add(GetRect(targetPos, size));
+ }
+
+ return targetPositions;
+ }
+
+ void ApplyPositions(Dictionary targetPositions)
+ {
+ foreach (var kvp in targetPositions)
+ {
+ MachineStatusItem label = kvp.Key;
+ Vector2 target = kvp.Value;
+
+ if (!smoothedPositions.ContainsKey(label))
+ smoothedPositions[label] = target;
+
+ smoothedPositions[label] = Vector2.Lerp(smoothedPositions[label],target,Time.deltaTime * smoothSpeed);
+
+ // Manager가 직접 RectTransform 조작
+ label.rectTransform.position = new Vector3(smoothedPositions[label].x, smoothedPositions[label].y, 0);
+ }
+ }
+
+ void UpdateScales(List visibleLabels)
+ {
+ foreach (var label in visibleLabels)
+ {
+ // 거리 비율 → 목표 스케일 계산 (원본과 동일한 방식)
+ float t = Mathf.InverseLerp(maxDistance, 0f, label.distanceToCamera);
+ float targetScale = Mathf.Lerp(minScale, maxScale, t);
+
+ // 스무딩 초기값 설정
+ if (!smoothedScales.ContainsKey(label))
+ smoothedScales[label] = targetScale;
+
+ // Lerp로 부드럽게 스케일 변화
+ smoothedScales[label] = Mathf.Lerp(
+ smoothedScales[label],
+ targetScale,
+ Time.deltaTime * scaleSmoothing
+ );
+
+ label.transform.localScale = Vector3.one * smoothedScales[label];
+ }
+ }
+
+ Rect GetRect(Vector2 centerPos, Vector2 size)
+ {
+ return new Rect(centerPos.x - size.x * 0.5f, centerPos.y - size.y * 0.5f, size.x, size.y);
+ }
+
+ bool IsOverlapping(Rect a, Rect b)
+ {
+ return a.xMin < b.xMax - overlapThreshold && a.xMax > b.xMin + overlapThreshold &&
+ a.yMin < b.yMax - overlapThreshold && a.yMax > b.yMin + overlapThreshold;
+ }
}
}
\ No newline at end of file
diff --git a/Assets/Scripts/UI/AlarmSituation/AlarmSituationPanel.cs b/Assets/Scripts/UI/AlarmSituation/AlarmSituationPanel.cs
index fb67dc6..8df8477 100644
--- a/Assets/Scripts/UI/AlarmSituation/AlarmSituationPanel.cs
+++ b/Assets/Scripts/UI/AlarmSituation/AlarmSituationPanel.cs
@@ -62,7 +62,7 @@ namespace AZTECHWB.UI
{
var building = AZTECHSceneMain.Instance.building;
var machines = building.floors.SelectMany(f => f.machines);
- var selectedMachine = machines.Where(m => m.machineName == alarmInfo.completeInfo.worknm).FirstOrDefault();
+ var selectedMachine = machines.FirstOrDefault(m => m.machineName == alarmInfo.completeInfo.worknm);
new SelectedMachineCommand(selectedMachine, false).Execute();
}
diff --git a/Assets/Scripts/UI/ExitProgramPanel/ExitProgramPanel.cs b/Assets/Scripts/UI/ExitProgramPanel/ExitProgramPanel.cs
index 9ee7a3e..3cf75d7 100644
--- a/Assets/Scripts/UI/ExitProgramPanel/ExitProgramPanel.cs
+++ b/Assets/Scripts/UI/ExitProgramPanel/ExitProgramPanel.cs
@@ -12,7 +12,6 @@ namespace AZTECHWB.UI
public Button Button_Close;
private LayerPanel layerPanel;
- public Action isClickUI;
public override async UniTask Init()
{
@@ -32,13 +31,11 @@ namespace AZTECHWB.UI
gameObject.SetActive(true);
layerPanel.Open();
gameObject.transform.SetAsLastSibling();
- isClickUI?.Invoke(true);
}
public override void Close()
{
layerPanel.Close();
gameObject.SetActive(false);
- isClickUI?.Invoke(false);
}
private void OnClickExitButton()
{
diff --git a/Assets/Scripts/UI/LeftSidePanel/LeftSidePanel.cs b/Assets/Scripts/UI/LeftSidePanel/LeftSidePanel.cs
index 136b367..8414597 100644
--- a/Assets/Scripts/UI/LeftSidePanel/LeftSidePanel.cs
+++ b/Assets/Scripts/UI/LeftSidePanel/LeftSidePanel.cs
@@ -99,6 +99,16 @@ namespace AZTECHWB.UI
null,
" UI Ȱȭ/Ȱȭ մϴ.");
+ toolbarModel.AddToggleButton("button_floor_control", true,
+ "Prefabs/UI/Toolbar/images/ic_floor_active",
+ "Prefabs/UI/Toolbar/images/ic_floor_deactive",
+ (isSelected) =>
+ {
+ //AZTECHSceneMain.Instance.building.ActiveRoof(isSelected);
+ },
+ null,
+ " Ȱȭ/Ȱȭ մϴ.");
+
// --- ---
toolbar.SetData(toolbarModel);
toolbar.Initialize();
diff --git a/Assets/Scripts/UI/Library/FilterButton.cs b/Assets/Scripts/UI/Library/FilterButton.cs
index 84b132e..7ac7121 100644
--- a/Assets/Scripts/UI/Library/FilterButton.cs
+++ b/Assets/Scripts/UI/Library/FilterButton.cs
@@ -14,9 +14,6 @@ namespace AZTECHWB.UI
private TextMeshProUGUI Text_Selected;
private TextMeshProUGUI Text_Deselected;
- public Color selectColor;
- public Color origingColor;
-
public void SettingButton(string labelName)
{
transform.TryGetComponentInChildren(nameof(Text_Selected), out Text_Selected);
@@ -24,8 +21,6 @@ namespace AZTECHWB.UI
var button = transform.GetComponentInChildren