From 616120b7c56bb221a0262c8cb8c642b9049be173 Mon Sep 17 00:00:00 2001 From: Dal4segno Date: Sat, 17 Jan 2026 14:39:29 +0900 Subject: [PATCH] =?UTF-8?q?=EC=A1=B0=EC=A4=80=20=EC=8B=9C=EC=8A=A4?= =?UTF-8?q?=ED=85=9C=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 다른 블록에 의해 경로가 막힐 경우, 막은 블록을 하이라이트 + 조준과 공격 대상 선정 로직이 같아졌기 때문에 로직 통합 --- .../Scripts/Player/PlayerNetworkController.cs | 101 ++++++++---------- 1 file changed, 45 insertions(+), 56 deletions(-) diff --git a/Assets/Scripts/Player/PlayerNetworkController.cs b/Assets/Scripts/Player/PlayerNetworkController.cs index 2eec55f..3755e1a 100644 --- a/Assets/Scripts/Player/PlayerNetworkController.cs +++ b/Assets/Scripts/Player/PlayerNetworkController.cs @@ -34,6 +34,7 @@ public class PlayerNetworkController : NetworkBehaviour [SerializeField] private Sprite targetCrosshair; private RectTransform _crosshairRect; private MineableBlock _currentTargetBlock; // 현재 강조 중인 블록 저장 + private MineableBlock _lastHighlightedBlock; private CharacterController _controller; private PlayerInputActions _inputActions; @@ -152,27 +153,11 @@ public class PlayerNetworkController : NetworkBehaviour { if (!IsOwner) return; - // 1. 마우스가 가리키는 월드상의 위치를 먼저 찾습니다. - Ray mouseRay = Camera.main.ScreenPointToRay(Mouse.current.position.ReadValue()); - Vector3 worldAimPoint; - - // 아주 멀리까지 레이를 쏴서 조준 방향을 결정합니다. - if (Physics.Raycast(mouseRay, out RaycastHit mouseHit, 1000f, ~ignoreDuringAim)) - worldAimPoint = mouseHit.point; - else - worldAimPoint = mouseRay.GetPoint(100f); - - // 2. 캐릭터 가슴 높이에서 조준점을 향하는 방향 계산 - Vector3 origin = transform.position + Vector3.up * 1.2f; - Vector3 direction = (worldAimPoint - origin).normalized; - - // 3. 캐릭터에서 해당 방향으로 SphereCast를 쏴서 가장 가까운 블록 하나를 찾습니다. - // SphereCast는 가장 먼저 닿는 오브젝트 하나만 hit에 담습니다. - if (Physics.SphereCast(origin, aimRadius, direction, out RaycastHit blockHit, attackRange, mineableLayer)) + // 현재 하이라이트 중인 블록이 있다면 그 녀석이 바로 공격 대상입니다! + if (_lastHighlightedBlock != null) { - if (blockHit.collider.TryGetComponent(out var netObj)) + if (_lastHighlightedBlock.TryGetComponent(out var netObj)) { - // 서버에 대미지 요청 ApplyMiningDamageServerRpc(netObj.NetworkObjectId); } } @@ -286,48 +271,52 @@ public class PlayerNetworkController : NetworkBehaviour { if (!IsOwner || _crosshairRect == null) return; - // 1. [조준점 확보] 카메라 레이로 마우스가 가리키는 '실제 지점'을 찾습니다. - Ray cameraRay = Camera.main.ScreenPointToRay(_crosshairRect.position); - RaycastHit cameraHit; - - // 지형이나 블록을 모두 검사하여 조준점을 잡습니다. - bool hitSomething = Physics.Raycast(cameraRay, out cameraHit, 150f, ~ignoreDuringAim); - Vector3 worldAimPoint = hitSomething ? cameraHit.point : cameraRay.GetPoint(100f); - - // 2. [거리 및 방향 계산] 캐릭터 가슴에서 그 지점까지의 벡터를 구합니다. - Vector3 origin = transform.position + Vector3.up * 1.2f; - Vector3 toTarget = worldAimPoint - origin; - float distToTarget = toTarget.magnitude; - Vector3 direction = toTarget.normalized; - - // 3. [대상 우선 판정] - // 만약 카메라 레이가 '채광 가능한 블록'을 직접 때렸고, 그게 사거리 이내라면? -> 바로 타겟팅! - bool isDirectHit = hitSomething && ((1 << cameraHit.collider.gameObject.layer) & mineableLayer) != 0; - - RaycastHit finalHit; - bool hasValidTarget = false; - - if (isDirectHit && distToTarget <= attackRange) - { - // 마우스가 직접 블록을 가리키고 사거리 내에 있는 경우 - finalHit = cameraHit; - hasValidTarget = true; - } + // 1. 카메라 레이로 조준점 계산 (플레이어 몸통 무시) + Ray aimRay = Camera.main.ScreenPointToRay(_crosshairRect.position); + Vector3 worldAimPoint; + if (Physics.Raycast(aimRay, out RaycastHit mouseHit, 100f, ~ignoreDuringAim)) + worldAimPoint = mouseHit.point; else + worldAimPoint = aimRay.GetPoint(50f); + + // 2. 캐릭터 가슴에서 조준점을 향하는 방향 계산 + Vector3 origin = transform.position + Vector3.up * 1.2f; + Vector3 direction = (worldAimPoint - origin).normalized; + + // 자기 자신 충돌 방지용 오프셋 + Vector3 rayStart = origin + direction * 0.4f; + + // 3. [중요] 실제 공격과 동일한 SphereCast 실행 + RaycastHit blockHit; + bool hasTarget = Physics.SphereCast(rayStart, aimRadius, direction, out blockHit, attackRange - 0.4f, mineableLayer); + + // 4. 하이라이트 대상 업데이트 + MineableBlock currentTarget = null; + if (hasTarget) { - // 마우스가 허공을 보거나 너무 먼 곳을 볼 때만 '범위 탐색(SphereCast)'을 수행합니다. - float searchDist = Mathf.Min(distToTarget, attackRange); - Vector3 rayStart = origin + direction * 0.4f; - hasValidTarget = Physics.SphereCast(rayStart, aimRadius, direction, out finalHit, searchDist - 0.4f, mineableLayer); + currentTarget = blockHit.collider.GetComponentInParent(); } - // 4. 디버그 및 시각화 업데이트 - _debugOrigin = origin; - _debugDir = direction; - _debugHit = hasValidTarget; - _debugDist = hasValidTarget ? Vector3.Distance(origin, finalHit.point) : Mathf.Min(distToTarget, attackRange); + // 대상이 바뀌었을 때만 아웃라인 갱신 (최적화) + if (_lastHighlightedBlock != currentTarget) + { + if (_lastHighlightedBlock != null) _lastHighlightedBlock.SetHighlight(false); + if (currentTarget != null) currentTarget.SetHighlight(true); + _lastHighlightedBlock = currentTarget; + } - UpdateBlockVisuals(hasValidTarget, finalHit); + // 기즈모 디버그 데이터 동기화 + _debugOrigin = rayStart; + _debugDir = direction; + _debugHit = hasTarget; + _debugDist = hasTarget ? blockHit.distance : (attackRange - 0.4f); + + // 크로스헤어 이미지 교체 + if (crosshairUI != null) + { + crosshairUI.sprite = hasTarget ? targetCrosshair : idleCrosshair; + crosshairUI.color = hasTarget ? Color.green : Color.white; + } } private void UpdateBlockVisuals(bool hasTarget, RaycastHit hit)