182 lines
5.2 KiB
C#
182 lines
5.2 KiB
C#
using UnityEngine;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
|
|
public class PlayerAttack : MonoBehaviour
|
|
{
|
|
[Header("--- 활 설정 ---")]
|
|
[SerializeField] private GameObject arrowPrefab;
|
|
[SerializeField] private Transform firePoint;
|
|
[SerializeField] private PlayerAnimator pAnim;
|
|
|
|
[Header("--- 일반 공격 (좌클릭) ---")]
|
|
[SerializeField] private float normalDamage = 10f;
|
|
[SerializeField] private float normalRange = 15f;
|
|
[SerializeField] private float normalSpeed = 20f;
|
|
[SerializeField] private float attackCooldown = 0.5f;
|
|
|
|
[Header("--- 차징 공격 (우클릭) ---")]
|
|
[SerializeField] private float maxChargeTime = 2.0f;
|
|
|
|
[System.Serializable]
|
|
public struct ChargeStage
|
|
{
|
|
public float chargeTime;
|
|
public float damageMult;
|
|
public float rangeMult;
|
|
}
|
|
|
|
[SerializeField] private List<ChargeStage> chargeStages;
|
|
|
|
private float _lastAttackTime;
|
|
private float _chargeTimer;
|
|
private bool _isCharging = false;
|
|
private bool _isAttacking = false;
|
|
|
|
// 재장전 잠금 장치 (한 발 쏘면 우클릭 뗄 때까지 발사 불가)
|
|
private bool _waitForRelease = false;
|
|
|
|
private float _pendingDamage;
|
|
private float _pendingSpeed;
|
|
private float _pendingRange;
|
|
|
|
public bool IsCharging => _isCharging;
|
|
public bool IsAttacking
|
|
{
|
|
get => _isAttacking;
|
|
set => _isAttacking = value;
|
|
}
|
|
public float ChargeProgress => Mathf.Clamp01(_chargeTimer / maxChargeTime);
|
|
|
|
private void Start()
|
|
{
|
|
if (chargeStages == null || chargeStages.Count == 0)
|
|
{
|
|
chargeStages = new List<ChargeStage>
|
|
{
|
|
new ChargeStage { chargeTime = 0f, damageMult = 1f, rangeMult = 1f },
|
|
new ChargeStage { chargeTime = 1f, damageMult = 1.5f, rangeMult = 1.2f },
|
|
new ChargeStage { chargeTime = 2f, damageMult = 2.5f, rangeMult = 1.5f }
|
|
};
|
|
}
|
|
}
|
|
|
|
private void Update()
|
|
{
|
|
if (_isCharging)
|
|
{
|
|
_chargeTimer += Time.deltaTime;
|
|
}
|
|
}
|
|
|
|
// --- [1] 일반 공격 ---
|
|
public void PerformNormalAttack()
|
|
{
|
|
if (Time.time < _lastAttackTime + attackCooldown) return;
|
|
if (_isAttacking) return;
|
|
|
|
_pendingDamage = normalDamage;
|
|
_pendingSpeed = normalSpeed;
|
|
_pendingRange = normalRange;
|
|
|
|
_lastAttackTime = Time.time;
|
|
|
|
if (pAnim != null) pAnim.TriggerThrow();
|
|
|
|
StartCoroutine(AttackRoutine());
|
|
}
|
|
|
|
// --- [2] 차징 시작 ---
|
|
public void StartCharging()
|
|
{
|
|
// 🔒 잠금 상태(방금 쏨)라면 차징 시작 안 함 (손 떼야 풀림)
|
|
if (_waitForRelease) return;
|
|
|
|
_isCharging = true;
|
|
_chargeTimer = 0f;
|
|
|
|
if (pAnim != null) pAnim.SetCharging(true);
|
|
if (CinemachineShake.Instance != null) CinemachineShake.Instance.SetZoom(true);
|
|
}
|
|
|
|
// 내부용: 효과 끄기
|
|
private void ResetChargingEffects()
|
|
{
|
|
_isCharging = false;
|
|
_chargeTimer = 0f;
|
|
|
|
if (pAnim != null) pAnim.SetCharging(false);
|
|
if (CinemachineShake.Instance != null) CinemachineShake.Instance.SetZoom(false);
|
|
}
|
|
|
|
// 외부 호출: 우클릭 뗐을 때 (여기서 잠금을 풉니다)
|
|
public void CancelCharging()
|
|
{
|
|
ResetChargingEffects();
|
|
_waitForRelease = false; // ✅ 잠금 해제! 다시 쏠 수 있음
|
|
}
|
|
|
|
// --- [3] 차징 발사 ---
|
|
public void ReleaseAttack()
|
|
{
|
|
if (!_isCharging) return;
|
|
|
|
ChargeStage currentStage = chargeStages[0];
|
|
foreach (var stage in chargeStages)
|
|
{
|
|
if (_chargeTimer >= stage.chargeTime) currentStage = stage;
|
|
}
|
|
|
|
_pendingDamage = normalDamage * currentStage.damageMult;
|
|
_pendingSpeed = normalSpeed * currentStage.rangeMult;
|
|
_pendingRange = normalRange * currentStage.rangeMult;
|
|
|
|
if (pAnim != null) pAnim.TriggerThrow();
|
|
|
|
// 🔒 발사했으므로 잠금! (우클릭 뗄 때까지 차징 불가)
|
|
_waitForRelease = true;
|
|
|
|
_lastAttackTime = Time.time;
|
|
StartCoroutine(AttackRoutine());
|
|
}
|
|
|
|
// --- [4] 이벤트: 화살 생성 ---
|
|
public void OnShootArrow()
|
|
{
|
|
if (arrowPrefab == null || firePoint == null) return;
|
|
|
|
GameObject arrow = Instantiate(arrowPrefab, firePoint.position, transform.rotation);
|
|
PlayerArrow arrowScript = arrow.GetComponent<PlayerArrow>();
|
|
|
|
if (arrowScript != null)
|
|
{
|
|
arrowScript.Initialize(_pendingDamage, _pendingSpeed, _pendingRange);
|
|
}
|
|
}
|
|
|
|
// --- [5] 이벤트: 공격 끝 (정상적인 경우) ---
|
|
public void OnAttackEnd()
|
|
{
|
|
_isAttacking = false;
|
|
ResetChargingEffects();
|
|
}
|
|
|
|
// --- [6] 안전장치 코루틴 ---
|
|
private IEnumerator AttackRoutine()
|
|
{
|
|
_isAttacking = true;
|
|
|
|
// 🚨 안전장치: 0.6초 뒤에는 무조건 공격 상태를 풉니다!
|
|
// (애니메이션 이벤트 OnAttackEnd가 씹혀도 멈추지 않게 함)
|
|
yield return new WaitForSeconds(0.6f);
|
|
|
|
if (_isAttacking)
|
|
{
|
|
_isAttacking = false;
|
|
ResetChargingEffects();
|
|
}
|
|
}
|
|
|
|
public void StartWeaponCollision() { }
|
|
public void StopWeaponCollision() { }
|
|
} |