조준 시스템 개선

다른 블록에 의해 경로가 막힐 경우, 막은 블록을 하이라이트
+ 조준과 공격 대상 선정 로직이 같아졌기 때문에 로직 통합
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;
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<NetworkObject>(out var netObj))
if (_lastHighlightedBlock.TryGetComponent<NetworkObject>(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
{
// 마우스가 허공을 보거나 너무 먼 곳을 볼 때만 '범위 탐색(SphereCast)'을 수행합니다.
float searchDist = Mathf.Min(distToTarget, attackRange);
worldAimPoint = aimRay.GetPoint(50f);
// 2. 캐릭터 가슴에서 조준점을 향하는 방향 계산
Vector3 origin = transform.position + Vector3.up * 1.2f;
Vector3 direction = (worldAimPoint - origin).normalized;
// 자기 자신 충돌 방지용 오프셋
Vector3 rayStart = origin + direction * 0.4f;
hasValidTarget = Physics.SphereCast(rayStart, aimRadius, direction, out finalHit, searchDist - 0.4f, mineableLayer);
// 3. [중요] 실제 공격과 동일한 SphereCast 실행
RaycastHit blockHit;
bool hasTarget = Physics.SphereCast(rayStart, aimRadius, direction, out blockHit, attackRange - 0.4f, mineableLayer);
// 4. 하이라이트 대상 업데이트
MineableBlock currentTarget = null;
if (hasTarget)
{
currentTarget = blockHit.collider.GetComponentInParent<MineableBlock>();
}
// 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)