using UnityEngine; public class NPCController : MonoBehaviour { private Animator _anim; [SerializeField] private int _defaultAnimNo; [Tooltip("애니메이션 이벤트로 켜고 끌 이펙트들 — 이벤트의 Int 파라미터가 이 목록의 인덱스")] [SerializeField] private GameObject[] _animationEffects; [Tooltip("애니메이션 이벤트로 재생할 SFX들 — 이벤트의 Int 파라미터가 이 목록의 인덱스")] [SerializeField] private AudioClip[] _animationSfx; private static readonly int _speedHash = Animator.StringToHash("Speed"); private Vector3 _lastPosition; private float _smoothedSpeed; private void Awake() { _anim = GetComponent(); } private void OnEnable() { _lastPosition = transform.position; _smoothedSpeed = 0f; } public void SetAnimState(int animNo) { _anim.SetFloat("AnimNo", (float)animNo); } public void SetDefaultAnimState() { SetAnimState(_defaultAnimNo); } // 이동 속도를 재서 Animator의 "Speed" 파라미터로 넘긴다 (블렌드 트리용). // 속도는 위치 변화량으로 직접 잰다 — Rigidbody가 있어도 kinematic + 트랜스폼 직접 이동(타임라인)은 // 물리를 안 거쳐서 linearVelocity가 항상 0이기 때문. 타임라인이 위치를 쓴 뒤인 LateUpdate에서 잰다. private void LateUpdate() { if (Time.deltaTime <= 0f) return; // 수평 이동 속도 (m/s) Vector3 delta = transform.position - _lastPosition; delta.y = 0f; _lastPosition = transform.position; // 프레임 편차로 인한 떨림 방지용 스무딩 _smoothedSpeed = Mathf.Lerp(_smoothedSpeed, delta.magnitude / Time.deltaTime, 10f * Time.deltaTime); _anim.SetFloat(_speedHash, _smoothedSpeed); } // 애니메이션 이벤트용 — 등록된 이펙트 활성화 + 재생 (Int 파라미터 = _animationEffects 인덱스) public void OnAnimationEffect(int effectNo) { var effect = GetEffect(effectNo); if (effect == null) return; effect.SetActive(true); // 이미 켜져 있는 상태에서 이벤트가 또 와도 처음부터 다시 재생되도록 var ps = effect.GetComponentInChildren(); if (ps != null) ps.Play(withChildren: true); } // 애니메이션 이벤트용 — 등록된 이펙트 비활성화 public void OffAnimationEffect(int effectNo) { var effect = GetEffect(effectNo); if (effect != null) effect.SetActive(false); } private GameObject GetEffect(int effectNo) { if (_animationEffects == null || effectNo < 0 || effectNo >= _animationEffects.Length || _animationEffects[effectNo] == null) { Debug.LogWarning($"[NPCController] 등록되지 않은 이펙트 번호: {effectNo} ({name})"); return null; } return _animationEffects[effectNo]; } // 애니메이션 이벤트용 — 등록된 SFX를 재생 (Int 파라미터 = _animationSfx 인덱스) public void OnAnimationSfx(int sfxNo) { if (_animationSfx == null || sfxNo < 0 || sfxNo >= _animationSfx.Length || _animationSfx[sfxNo] == null) { Debug.LogWarning($"[NPCController] 등록되지 않은 SFX 번호: {sfxNo} ({name})"); return; } if (SoundManager.Instance != null) SoundManager.Instance.PlaySFX(_animationSfx[sfxNo]); } }