조준 시스템 개선

다른 블록에 의해 경로가 막힐 경우, 막은 블록을 하이라이트
+ 조준과 공격 대상 선정 로직이 같아졌기 때문에 로직 통합
This commit is contained in:
2026-01-17 14:39:29 +09:00
parent 11b9112739
commit 616120b7c5

View File

@@ -34,6 +34,7 @@ public class PlayerNetworkController : NetworkBehaviour
[SerializeField] private Sprite targetCrosshair; [SerializeField] private Sprite targetCrosshair;
private RectTransform _crosshairRect; private RectTransform _crosshairRect;
private MineableBlock _currentTargetBlock; // 현재 강조 중인 블록 저장 private MineableBlock _currentTargetBlock; // 현재 강조 중인 블록 저장
private MineableBlock _lastHighlightedBlock;
private CharacterController _controller; private CharacterController _controller;
private PlayerInputActions _inputActions; private PlayerInputActions _inputActions;
@@ -152,27 +153,11 @@ public class PlayerNetworkController : NetworkBehaviour
{ {
if (!IsOwner) return; if (!IsOwner) return;
// 1. 마우스가 가리키는 월드상의 위치를 먼저 찾습니다. // 현재 하이라이트 중인 블록이 있다면 그 녀석이 바로 공격 대상입니다!
Ray mouseRay = Camera.main.ScreenPointToRay(Mouse.current.position.ReadValue()); if (_lastHighlightedBlock != null)
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 (blockHit.collider.TryGetComponent<NetworkObject>(out var netObj)) if (_lastHighlightedBlock.TryGetComponent<NetworkObject>(out var netObj))
{ {
// 서버에 대미지 요청
ApplyMiningDamageServerRpc(netObj.NetworkObjectId); ApplyMiningDamageServerRpc(netObj.NetworkObjectId);
} }
} }
@@ -286,48 +271,52 @@ public class PlayerNetworkController : NetworkBehaviour
{ {
if (!IsOwner || _crosshairRect == null) return; if (!IsOwner || _crosshairRect == null) return;
// 1. [조준점 확보] 카메라 레이로 마우스가 가리키는 '실제 지점'을 찾습니다. // 1. 카메라 레이로 조준점 계산 (플레이어 몸통 무시)
Ray cameraRay = Camera.main.ScreenPointToRay(_crosshairRect.position); Ray aimRay = Camera.main.ScreenPointToRay(_crosshairRect.position);
RaycastHit cameraHit; Vector3 worldAimPoint;
if (Physics.Raycast(aimRay, out RaycastHit mouseHit, 100f, ~ignoreDuringAim))
// 지형이나 블록을 모두 검사하여 조준점을 잡습니다. worldAimPoint = mouseHit.point;
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;
}
else 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)'을 수행합니다. currentTarget = blockHit.collider.GetComponentInParent<MineableBlock>();
float searchDist = Mathf.Min(distToTarget, attackRange);
Vector3 rayStart = origin + direction * 0.4f;
hasValidTarget = Physics.SphereCast(rayStart, aimRadius, direction, out finalHit, searchDist - 0.4f, mineableLayer);
} }
// 4. 디버그 및 시각화 업데이트 // 대상이 바뀌었을 때만 아웃라인 갱신 (최적화)
_debugOrigin = origin; if (_lastHighlightedBlock != currentTarget)
_debugDir = direction; {
_debugHit = hasValidTarget; if (_lastHighlightedBlock != null) _lastHighlightedBlock.SetHighlight(false);
_debugDist = hasValidTarget ? Vector3.Distance(origin, finalHit.point) : Mathf.Min(distToTarget, attackRange); 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) private void UpdateBlockVisuals(bool hasTarget, RaycastHit hit)