2026-01-29 06:58:38 +00:00
|
|
|
|
using UnityEngine;
|
2026-01-30 07:45:11 +00:00
|
|
|
|
using System.Collections;
|
2026-01-29 06:58:38 +00:00
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
|
2026-01-29 16:11:10 +00:00
|
|
|
|
[Header("--- 설정 ---")]
|
2026-01-30 07:45:11 +00:00
|
|
|
|
[SerializeField] private float attackCooldown = 0.4f;
|
2026-01-30 06:30:27 +00:00
|
|
|
|
[SerializeField] private float fullChargeTime = 2f;
|
2026-01-30 07:45:11 +00:00
|
|
|
|
[SerializeField] private float postComboDelay = 1.2f; // 막타 후 딜레이 (n초)
|
2026-01-29 06:58:38 +00:00
|
|
|
|
|
|
|
|
|
|
private float _lastAttackTime, _chargeTimer;
|
2026-01-30 07:45:11 +00:00
|
|
|
|
private bool _isCharging, _canAttack = true, _isAttacking = false;
|
|
|
|
|
|
private int _comboCount = 0; // 콤보 카운트
|
2026-01-29 06:58:38 +00:00
|
|
|
|
|
|
|
|
|
|
public float ChargeProgress => Mathf.Clamp01(_chargeTimer / fullChargeTime);
|
|
|
|
|
|
public bool IsCharging => _isCharging;
|
|
|
|
|
|
|
2026-01-30 07:45:11 +00:00
|
|
|
|
// ⭐ 이동 스크립트에서 공격 중에 멈추게 할 때 사용하세요!
|
|
|
|
|
|
public bool IsAttacking => _isAttacking;
|
|
|
|
|
|
|
2026-01-29 06:58:38 +00:00
|
|
|
|
private void Update()
|
|
|
|
|
|
{
|
|
|
|
|
|
if (_isCharging)
|
|
|
|
|
|
{
|
|
|
|
|
|
_chargeTimer += Time.deltaTime;
|
2026-01-30 07:45:11 +00:00
|
|
|
|
if (Input.GetMouseButtonDown(1)) { CancelCharging(); }
|
2026-01-29 06:58:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public void PerformNormalAttack()
|
|
|
|
|
|
{
|
2026-01-30 07:45:11 +00:00
|
|
|
|
if (!_canAttack || (playerHealth != null && playerHealth.isHit)) return;
|
2026-01-29 06:58:38 +00:00
|
|
|
|
if (interaction.CurrentWeapon == null || pAnim == null) return;
|
|
|
|
|
|
if (Time.time < _lastAttackTime + attackCooldown) return;
|
|
|
|
|
|
|
2026-01-30 07:45:11 +00:00
|
|
|
|
_isAttacking = true; // 🚫 공격 중 이동 제한 시작
|
|
|
|
|
|
_comboCount = (_comboCount % 3) + 1; // 1 -> 2 -> 3타 순환
|
|
|
|
|
|
|
2026-01-29 06:58:38 +00:00
|
|
|
|
pAnim.TriggerAttack();
|
|
|
|
|
|
_lastAttackTime = Time.time;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-30 06:30:27 +00:00
|
|
|
|
public void OnAttackShake()
|
|
|
|
|
|
{
|
2026-01-30 07:45:11 +00:00
|
|
|
|
if (CinemachineShake.Instance == null) return;
|
|
|
|
|
|
|
|
|
|
|
|
if (_comboCount == 3) // 🔥 인왕 스타일 3타 막타 연출!
|
|
|
|
|
|
{
|
|
|
|
|
|
CinemachineShake.Instance.HitSlow(0.2f, 0.05f); // 묵직한 슬로우
|
|
|
|
|
|
CinemachineShake.Instance.CameraKick(10f); // 화끈한 카메라 킥
|
|
|
|
|
|
CinemachineShake.Instance.ShakeAttack();
|
|
|
|
|
|
}
|
|
|
|
|
|
else // 일반 1, 2타
|
|
|
|
|
|
{
|
|
|
|
|
|
CinemachineShake.Instance.HitSlow(0.1f, 0.2f);
|
|
|
|
|
|
CinemachineShake.Instance.ShakeAttack();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ⭐ 공격 애니메이션 마지막 프레임에 꼭 넣으세요!
|
|
|
|
|
|
public void OnAttackEnd()
|
|
|
|
|
|
{
|
|
|
|
|
|
if (_comboCount == 3) // 막타가 끝났다면 후딜레이 시작
|
|
|
|
|
|
{
|
|
|
|
|
|
StartCoroutine(PostComboRecovery());
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
2026-01-30 06:30:27 +00:00
|
|
|
|
{
|
2026-01-30 07:45:11 +00:00
|
|
|
|
_isAttacking = false; // 1, 2타는 즉시 이동 가능
|
2026-01-30 06:30:27 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-30 07:45:11 +00:00
|
|
|
|
private IEnumerator PostComboRecovery()
|
2026-01-29 06:58:38 +00:00
|
|
|
|
{
|
2026-01-30 07:45:11 +00:00
|
|
|
|
_canAttack = false;
|
|
|
|
|
|
_isAttacking = true; // 딜레이 중에도 이동 제한
|
|
|
|
|
|
yield return new WaitForSeconds(postComboDelay);
|
|
|
|
|
|
_canAttack = true;
|
|
|
|
|
|
_isAttacking = false; // 이제 움직이기 가능
|
|
|
|
|
|
_comboCount = 0; // 콤보 리셋
|
2026-01-29 06:58:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-30 07:45:11 +00:00
|
|
|
|
public void CancelCharging() // 🛠️ 에러 해결: 확실하게 포함됨!
|
2026-01-29 06:58:38 +00:00
|
|
|
|
{
|
2026-01-30 07:45:11 +00:00
|
|
|
|
_isCharging = false;
|
|
|
|
|
|
_chargeTimer = 0f;
|
|
|
|
|
|
if (pAnim != null) pAnim.SetCharging(false);
|
|
|
|
|
|
if (CinemachineShake.Instance != null) CinemachineShake.Instance.SetZoom(false);
|
2026-01-29 06:58:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public void ReleaseAttack()
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!_isCharging || interaction.CurrentWeapon == null) return;
|
|
|
|
|
|
pAnim.TriggerThrow();
|
|
|
|
|
|
pAnim.SetCharging(false);
|
|
|
|
|
|
|
2026-01-30 07:45:11 +00:00
|
|
|
|
// 던지기 물리 로직 생략 (기존 것 유지)
|
2026-01-29 06:58:38 +00:00
|
|
|
|
|
2026-01-30 07:45:11 +00:00
|
|
|
|
if (CinemachineShake.Instance != null)
|
2026-01-29 06:58:38 +00:00
|
|
|
|
{
|
2026-01-30 07:45:11 +00:00
|
|
|
|
CinemachineShake.Instance.HitSlow(0.15f, 0.3f);
|
|
|
|
|
|
CinemachineShake.Instance.SetZoom(false);
|
|
|
|
|
|
CinemachineShake.Instance.ShakeAttack();
|
2026-01-29 06:58:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
interaction.ClearCurrentWeapon();
|
|
|
|
|
|
stats.ResetWeight();
|
|
|
|
|
|
_isCharging = false;
|
|
|
|
|
|
_chargeTimer = 0f;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public void StartCharging()
|
|
|
|
|
|
{
|
|
|
|
|
|
if (playerHealth != null && playerHealth.isHit) return;
|
|
|
|
|
|
if (interaction.CurrentWeapon == null) return;
|
|
|
|
|
|
_isCharging = true;
|
|
|
|
|
|
_chargeTimer = 0f;
|
|
|
|
|
|
pAnim.SetCharging(true);
|
2026-01-30 07:45:11 +00:00
|
|
|
|
if (CinemachineShake.Instance != null) CinemachineShake.Instance.SetZoom(true);
|
2026-01-29 06:58:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-30 07:45:11 +00:00
|
|
|
|
public void StartWeaponCollision() { /* ... */ }
|
|
|
|
|
|
public void StopWeaponCollision() { /* ... */ }
|
2026-01-29 06:58:38 +00:00
|
|
|
|
}
|