지하 최적화

블록 프리팹 단위 -> 블록 청크 단위 스폰
기타 건설, 조준 관련 사이드이펙트 버그 수정
This commit is contained in:
2026-01-21 21:34:05 +09:00
parent db5db4b106
commit 59246a67bd
24 changed files with 2622 additions and 127 deletions

View File

@@ -2,6 +2,7 @@ using UnityEngine;
using Unity.Netcode;
using UnityEngine.InputSystem;
using UnityEngine.EventSystems;
using UnityEngine.UI;
using System.Collections.Generic;
public class BuildManager : NetworkBehaviour
@@ -36,6 +37,9 @@ public class BuildManager : NetworkBehaviour
private Vector3Int _currentGridPos;
private PlayerInputActions _inputActions;
// Public property to check if currently in build mode
public bool IsBuildMode => _isBuildMode;
private Dictionary<Vector3Int, TunnelNode> _tunnelRegistry = new Dictionary<Vector3Int, TunnelNode>();
private HashSet<Vector3Int> _occupiedNodes = new HashSet<Vector3Int>();
@@ -139,10 +143,59 @@ public class BuildManager : NetworkBehaviour
return _tunnelRegistry.GetValueOrDefault(pos);
}
// Helper method to properly check if pointer is over UI with New Input System
private bool IsPointerOverUI()
{
if (EventSystem.current == null) return false;
// Use the new input system's pointer position
Vector2 pointerPosition = Mouse.current.position.ReadValue();
PointerEventData eventData = new PointerEventData(EventSystem.current)
{
position = pointerPosition
};
List<RaycastResult> results = new List<RaycastResult>();
EventSystem.current.RaycastAll(eventData, results);
// Filter out non-interactive UI elements (crosshair, HUD, etc.)
foreach (var result in results)
{
GameObject uiObject = result.gameObject;
// Ignore non-interactive UI elements by name
if (uiObject.name == "Crosshair" ||
uiObject.name.Contains("HUD") ||
uiObject.name.Contains("Display"))
{
continue;
}
// Check if the UI element is actually interactive (has a Selectable component)
UnityEngine.UI.Selectable selectable = uiObject.GetComponent<UnityEngine.UI.Selectable>();
if (selectable != null && selectable.interactable)
{
return true;
}
// Also check parent objects for Selectable components (in case we hit a child element)
selectable = uiObject.GetComponentInParent<UnityEngine.UI.Selectable>();
if (selectable != null && selectable.interactable)
{
return true;
}
}
// No interactive UI elements found
return false;
}
// 1. 건설 요청 시 실제 계산된 worldPos를 넘겨줍니다.
private void OnBuildRequested()
{
if (!_isBuildMode || EventSystem.current.IsPointerOverGameObject()) return;
if (!_isBuildMode) return;
if (IsPointerOverUI()) return;
// 고스트가 현재 위치한 '그 좌표'를 그대로 보냅니다.
RequestBuildRpc(_selectedTurretIndex, _currentGridPos, _ghostInstance.transform.position);
@@ -153,12 +206,30 @@ public class BuildManager : NetworkBehaviour
[Rpc(SendTo.Server, InvokePermission = RpcInvokePermission.Everyone)]
private void RequestBuildRpc(int index, Vector3Int gridPos, Vector3 worldPos)
{
if (constructionSitePrefab == null)
{
Debug.LogError("[BuildManager] Construction site prefab is null!");
return;
}
// GridToWorld를 다시 계산하지 않고 전달받은 worldPos를 그대로 사용합니다.
GameObject siteObj = Instantiate(constructionSitePrefab, worldPos, Quaternion.identity);
siteObj.GetComponent<NetworkObject>().Spawn();
NetworkObject netObj = siteObj.GetComponent<NetworkObject>();
if (netObj == null)
{
Debug.LogError("[BuildManager] Construction site has no NetworkObject component!");
Destroy(siteObj);
return;
}
netObj.Spawn();
ConstructionSite site = siteObj.GetComponent<ConstructionSite>();
if (site != null) site.Initialize(index, gridPos);
if (site != null)
{
site.Initialize(index, gridPos);
}
}
public void SelectTurret(int index)