using System; using System.Collections; using System.Collections.Generic; using UnityEngine; public class WeaponController : MonoBehaviour, IObservable { [Serializable] public class WeaponTriggerZone { public Vector3 position; public float radius; } [SerializeField] private WeaponTriggerZone[] _triggerZones; [SerializeField] private LayerMask targetLayerMask; private List> _observers = new List>(); // 공격 데미지 처리 private int attackPower = 1; private int _comboStep = 1; public int AttackPower // 플레이어 공격 데미지 { get { // 마지막 콤보일 경우 공격력 증가 return _comboStep == 4 ? attackPower * 4 : attackPower; } } private PlayerController _playerController; private bool _isAttacking = false; public bool IsAttacking => _isAttacking; // 충돌 처리 private Vector3[] _previousPositions; private HashSet _hitColliders; private Ray _ray = new Ray(); private RaycastHit[] _hits = new RaycastHit[10]; private void Start() { if (_triggerZones == null || _triggerZones.Length == 0) { Debug.LogWarning("Trigger Zones이 설정되지 않았습니다."); return; } _playerController = GetComponent(); if (_playerController == null) { var player = GameObject.FindGameObjectWithTag("Player"); if (player != null) { _playerController = player.GetComponent(); } } _previousPositions = new Vector3[_triggerZones.Length]; _hitColliders = new HashSet(); } public void AttackStart() { if (_hitColliders == null) { Debug.LogError("_hitColliders가 null입니다. 무기를 들고 있는지 확인해 주세요!"); return; } _isAttacking = true; _hitColliders.Clear(); StopAllCoroutines(); StartCoroutine(AutoEndAttack()); // 자동 공격 종료 for (int i = 0; i < _triggerZones.Length; i++) { _previousPositions[i] = transform.position + transform.TransformVector(_triggerZones[i].position); } } private IEnumerator AutoEndAttack() { yield return new WaitForSeconds(0.6f); // 0.6초 가량 대기 if (_isAttacking) // 아직 공격 중이면 { Debug.Log("공격 자동 종료 - 타임아웃"); AttackEnd(); } } public void AttackEnd() { _isAttacking = false; } private void FixedUpdate() { if (_isAttacking) { for (int i = 0; i < _triggerZones.Length; i++) { var worldPosition = transform.position + transform.TransformVector(_triggerZones[i].position); var direction = worldPosition - _previousPositions[i]; _ray.origin = _previousPositions[i]; _ray.direction = direction; if (direction.magnitude < 0.01f) return; var hitCount = Physics.SphereCastNonAlloc(_ray, _triggerZones[i].radius, _hits, direction.magnitude, targetLayerMask, QueryTriggerInteraction.UseGlobal); for (int j = 0; j < hitCount; j++) { var hit = _hits[j]; if (!_hitColliders.Contains(hit.collider)) { _hitColliders.Add(hit.collider); Notify(hit.collider.gameObject); } } _previousPositions[i] = worldPosition; } } } private IEnumerator ResumeTimeScale() { yield return new WaitForSecondsRealtime(10f); Time.timeScale = 1f; } public void Subscribe(IObserver observer) { if (!_observers.Contains(observer)) { _observers.Add(observer); } } public void Unsubscribe(IObserver observer) { _observers.Remove(observer); } public void Notify(GameObject value) { foreach (var observer in _observers) { observer.OnNext(value); } } private void OnDestroy() { var copyObservers = new List>(_observers); foreach (var observer in copyObservers) { observer.OnCompleted(); } _observers.Clear(); } public void SetComboStep(int step) { _comboStep = step; } #if UNITY_EDITOR private void OnDrawGizmos() { if (_triggerZones == null) return; if (_isAttacking && _previousPositions != null) { for (int i = 0; i < _triggerZones.Length; i++) { if (_triggerZones[i] == null) continue; var worldPosition = transform.position + transform.TransformVector(_triggerZones[i].position); var direction = worldPosition - _previousPositions[i]; Gizmos.color = Color.green; Gizmos.DrawWireSphere(worldPosition, _triggerZones[i].radius); Gizmos.color = Color.red; Gizmos.DrawWireSphere(worldPosition + direction, _triggerZones[i].radius); } } else { foreach (var triggerZone in _triggerZones) { if (triggerZone == null) continue; Gizmos.color = Color.green; Gizmos.DrawSphere(triggerZone.position, triggerZone.radius); } } } #endif }