Projext/Assets/Scripts/Player/Combat/Attack.cs

155 lines
4.8 KiB
C#
Raw Normal View History

2026-01-29 06:58:38 +00:00
using UnityEngine;
2026-01-30 07:45:11 +00:00
using System.Collections;
2026-02-06 04:20:12 +00:00
using System.Collections.Generic;
2026-01-29 06:58:38 +00:00
public class PlayerAttack : MonoBehaviour
{
2026-02-06 04:20:12 +00:00
[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;
2026-02-02 15:02:12 +00:00
2026-02-06 04:20:12 +00:00
private float _lastAttackTime;
private float _chargeTimer;
private bool _isCharging = false;
private bool _isAttacking = false;
// 🔥 추가: 애니메이션 이벤트 발생 시점에 사용할 데이터 저장용 변수
private float _pendingDamage;
private float _pendingSpeed;
private float _pendingRange;
public bool IsCharging => _isCharging;
2026-02-02 15:02:12 +00:00
public bool IsAttacking
{
get => _isAttacking;
set => _isAttacking = value;
}
2026-02-06 04:20:12 +00:00
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 }
};
}
}
2026-01-30 07:45:11 +00:00
2026-01-29 06:58:38 +00:00
private void Update()
{
if (_isCharging)
{
_chargeTimer += Time.deltaTime;
}
}
2026-02-06 04:20:12 +00:00
// 1. 좌클릭: 일반 공격 시도 (데이터만 저장하고 애니메이션 재생)
2026-01-29 06:58:38 +00:00
public void PerformNormalAttack()
{
if (Time.time < _lastAttackTime + attackCooldown) return;
2026-02-06 04:20:12 +00:00
if (_isAttacking) return;
2026-01-29 06:58:38 +00:00
2026-02-06 04:20:12 +00:00
// ✅ 여기서 바로 발사하지 않고 정보만 저장합니다.
_pendingDamage = normalDamage;
_pendingSpeed = normalSpeed;
_pendingRange = normalRange;
2026-01-30 07:45:11 +00:00
2026-01-29 06:58:38 +00:00
_lastAttackTime = Time.time;
2026-02-06 04:20:12 +00:00
// 애니메이션 트리거 실행
if (pAnim != null) pAnim.TriggerThrow();
2026-01-30 07:45:11 +00:00
2026-02-06 04:20:12 +00:00
StartCoroutine(AttackRoutine());
2026-01-30 07:45:11 +00:00
}
2026-02-06 04:20:12 +00:00
public void StartCharging()
2026-01-30 07:45:11 +00:00
{
2026-02-06 04:20:12 +00:00
_isCharging = true;
_chargeTimer = 0f;
2026-01-30 06:30:27 +00:00
2026-02-06 04:20:12 +00:00
if (pAnim != null) pAnim.SetCharging(true);
if (CinemachineShake.Instance != null) CinemachineShake.Instance.SetZoom(true);
2026-01-29 06:58:38 +00:00
}
2026-01-31 13:07:35 +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;
2026-02-06 04:20:12 +00:00
2026-01-30 07:45:11 +00:00
if (pAnim != null) pAnim.SetCharging(false);
if (CinemachineShake.Instance != null) CinemachineShake.Instance.SetZoom(false);
2026-01-29 06:58:38 +00:00
}
2026-02-06 04:20:12 +00:00
// 2. 우클릭 중 좌클릭: 차징 발사 시도 (수치 계산 후 애니메이션 재생)
2026-01-29 06:58:38 +00:00
public void ReleaseAttack()
{
2026-02-06 04:20:12 +00:00
if (!_isCharging) return;
2026-01-29 06:58:38 +00:00
2026-02-06 04:20:12 +00:00
ChargeStage currentStage = chargeStages[0];
foreach (var stage in chargeStages)
2026-01-31 13:07:35 +00:00
{
2026-02-06 04:20:12 +00:00
if (_chargeTimer >= stage.chargeTime) currentStage = stage;
2026-01-31 13:07:35 +00:00
}
2026-02-06 04:20:12 +00:00
// ✅ 계산된 결과값을 저장해둡니다.
_pendingDamage = normalDamage * currentStage.damageMult;
_pendingSpeed = normalSpeed * currentStage.rangeMult;
_pendingRange = normalRange * currentStage.rangeMult;
2026-01-31 13:07:35 +00:00
2026-02-06 04:20:12 +00:00
// 애니메이션 트리거 실행
if (pAnim != null) pAnim.TriggerThrow();
2026-01-31 13:07:35 +00:00
2026-02-06 04:20:12 +00:00
CancelCharging();
_lastAttackTime = Time.time;
StartCoroutine(AttackRoutine());
}
// 3. 🎯 핵심: 애니메이션의 'OnShootArrow' 이벤트 타이밍에 맞춰 실제 화살 생성
public void OnShootArrow()
{
if (arrowPrefab == null || firePoint == null) return;
2026-01-31 13:07:35 +00:00
2026-02-06 04:20:12 +00:00
GameObject arrow = Instantiate(arrowPrefab, firePoint.position, transform.rotation);
PlayerArrow arrowScript = arrow.GetComponent<PlayerArrow>();
2026-01-29 06:58:38 +00:00
2026-02-06 04:20:12 +00:00
if (arrowScript != null)
2026-01-29 06:58:38 +00:00
{
2026-02-06 04:20:12 +00:00
// 미리 저장해둔(_pending...) 수치를 사용해 화살 초기화
arrowScript.Initialize(_pendingDamage, _pendingSpeed, _pendingRange);
2026-01-29 06:58:38 +00:00
}
}
2026-02-06 04:20:12 +00:00
private IEnumerator AttackRoutine()
2026-01-29 06:58:38 +00:00
{
2026-02-06 04:20:12 +00:00
_isAttacking = true;
yield return new WaitForSeconds(0.2f);
_isAttacking = false;
2026-01-29 06:58:38 +00:00
}
2026-02-06 04:20:12 +00:00
public void StartWeaponCollision() { }
public void StopWeaponCollision() { }
2026-01-29 06:58:38 +00:00
}