using UnityEngine; using UnityEngine.AI; // 대상(보통 플레이어)을 NavMesh 위에서 따라다니는 간단한 동행 스크립트. // 속도/가속/높이(Base Offset) 등은 NavMeshAgent 컴포넌트에서 설정한다. // 추적은 _followEnabled로 동적으로 켜고/끌 수 있다. [RequireComponent(typeof(NavMeshAgent))] public class FollowObject : MonoBehaviour { [Header("Enable")] [SerializeField] private bool _followEnabled = true; // 추적 on/off (런타임에 동적 변경 가능) [Header("Target")] [SerializeField] private Transform _target; // 비워두면 Camera.main 사용 [Header("Follow")] [SerializeField] private float _followDistance = 3.0f; // 이 거리 안이면 멈춤 (= Agent stoppingDistance) [SerializeField] private float _repathInterval = 0.2f; // 목적지 갱신 주기(초) [Header("Rotation")] [SerializeField] private bool _lookAtTarget = true; // 켜면 이동방향이 아니라 항상 Target을 바라봄 [SerializeField] private float _lookSpeed = 8f; // 바라보기 회전 속도 (0이면 즉시) private NavMeshAgent _agent; private float _repathTimer; public bool FollowEnabled { get => _followEnabled; set => SetFollowEnabled(value); } private void Awake() { _agent = GetComponent(); _agent.stoppingDistance = _followDistance; ApplyAgentStopped(); } private void Update() { if (!_followEnabled) return; var target = ResolveTarget(); if (target == null || !_agent.isOnNavMesh) return; // 목적지 갱신 (주기적) _repathTimer -= Time.deltaTime; if (_repathTimer <= 0f) { _repathTimer = _repathInterval; _agent.SetDestination(target.position); // NavMesh가 지면/경사/장애물을 알아서 처리 } // 회전: 켜면 항상 Target을 바라봄(에이전트 자동회전 끔), 끄면 이동방향을 바라봄(기본) _agent.updateRotation = !_lookAtTarget; if (_lookAtTarget) FaceTarget(target); } // ── 동적 on/off ────────────────────────────────────────────── public void SetFollowEnabled(bool on) { _followEnabled = on; if (on) _repathTimer = 0f; // 켜면 즉시 경로 재계산 ApplyAgentStopped(); } public void EnableFollow() => SetFollowEnabled(true); // UnityEvent 연결용(무인자) public void DisableFollow() => SetFollowEnabled(false); // 끄면 즉시 멈추고 남은 경로 제거(잔여 이동 방지), 켜면 이동 재개 private void ApplyAgentStopped() { if (_agent == null || !_agent.isActiveAndEnabled || !_agent.isOnNavMesh) return; _agent.isStopped = !_followEnabled; if (!_followEnabled) _agent.ResetPath(); } // ───────────────────────────────────────────────────────────── private void FaceTarget(Transform target) { Vector3 dir = target.position - transform.position; dir.y = 0f; // 수평만 — 위아래로 안 기울게 if (dir.sqrMagnitude < 0.0001f) return; Quaternion look = Quaternion.LookRotation(dir); transform.rotation = _lookSpeed > 0f ? Quaternion.Slerp(transform.rotation, look, _lookSpeed * Time.deltaTime) : look; } private Transform ResolveTarget() { if (_target != null) return _target; return Camera.main != null ? Camera.main.transform : null; } public void SetTarget(Transform target) => _target = target; }