using UnityEngine; public class PlayerAttack : MonoBehaviour { [Header("--- 참조 ---")] [SerializeField] private PlayerInteraction interaction; [SerializeField] private Stats stats; [SerializeField] private PlayerAnimator pAnim; [SerializeField] private PlayerHealth playerHealth; [SerializeField] private WeaponHitBox weaponHitBox; [Header("--- 설정 ---")] [SerializeField] private float attackCooldown = 0.5f; [SerializeField] private float fullChargeTime = 2f; private float _lastAttackTime, _chargeTimer; private bool _isCharging; public float ChargeProgress => Mathf.Clamp01(_chargeTimer / fullChargeTime); public bool IsCharging => _isCharging; private void Update() { if (_isCharging) { _chargeTimer += Time.deltaTime; if (Input.GetMouseButtonDown(1)) { CancelCharging(); Debug.Log("차징 취소됨"); } } } public void PerformNormalAttack() { if (playerHealth != null && playerHealth.isHit) return; if (interaction.CurrentWeapon == null || pAnim == null) return; if (Time.time < _lastAttackTime + attackCooldown) return; pAnim.TriggerAttack(); _lastAttackTime = Time.time; // ❌ 여기서 ShakeAttack()을 호출하지 않습니다. (애니메이션 이벤트로 이동) } // ⭐ [추가] 애니메이션 이벤트에서 실제 타격 시점에 호출할 함수 public void OnAttackShake() { Debug.Log("[Event] 타격 타이밍! 카메라 흔들기 명령 전송"); if (CinemachineShake.Instance != null) { CinemachineShake.Instance.ShakeAttack(); // 애니메이션 타이밍에 맞춰 쾅! } } // (기존 StartWeaponCollision, StopWeaponCollision 로직 유지...) public void StartWeaponCollision() { if (interaction.CurrentWeapon == null || weaponHitBox == null) return; float damage = stats.BaseAttackDamage + interaction.CurrentWeapon.Config.BaseDamage; weaponHitBox.EnableHitBox(damage); } public void StopWeaponCollision() { if (weaponHitBox != null) weaponHitBox.DisableHitBox(); } public void ReleaseAttack() { if (!_isCharging || interaction.CurrentWeapon == null) return; pAnim.TriggerThrow(); pAnim.SetCharging(false); // 레이캐스트 조준 로직 Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition); Plane groundPlane = new Plane(Vector3.up, transform.position); float rayDistance; Vector3 targetDirection = transform.forward; if (groundPlane.Raycast(ray, out rayDistance)) { Vector3 pointOnGround = ray.GetPoint(rayDistance); targetDirection = (pointOnGround - transform.position).normalized; targetDirection.y = 0; } if (targetDirection != Vector3.zero) transform.rotation = Quaternion.LookRotation(targetDirection); // 차징 레벨 계산 및 무기 던지기 int lv = _chargeTimer >= 2f ? 3 : (_chargeTimer >= 1f ? 2 : 1); float currentSpread = interaction.CurrentWeapon.Config.GetSpread(lv); float throwForce = interaction.CurrentWeapon.Config.GetForce(lv); Vector3 finalThrowDir = Quaternion.Euler(0, Random.Range(-currentSpread, currentSpread), 0) * targetDirection; interaction.CurrentWeapon.OnThrown(finalThrowDir, throwForce, lv, stats); // 상태 초기화 및 카메라 효과 interaction.ClearCurrentWeapon(); stats.ResetWeight(); _isCharging = false; _chargeTimer = 0f; CinemachineShake.Instance.SetZoom(false); CinemachineShake.Instance.ShakeAttack(); // 강공격도 묵직하게 흔들어줌 } public void StartCharging() { if (playerHealth != null && playerHealth.isHit) return; if (interaction.CurrentWeapon == null) return; _isCharging = true; _chargeTimer = 0f; pAnim.SetCharging(true); CinemachineShake.Instance.SetZoom(true); } public void CancelCharging() { _isCharging = false; _chargeTimer = 0f; if (pAnim != null) pAnim.SetCharging(false); CinemachineShake.Instance.SetZoom(false); } }