Projext/Assets/5.TestScript/Attack.cs
2026-01-29 15:58:38 +09:00

122 lines
4.4 KiB
C#

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;
[Header("--- 단계별 투척 정확도(Spread) 설정 ---")]
[Tooltip("Lv1 (0~1초) 투척 시 오차 범위 (값이 클수록 빗나감)")]
[SerializeField] private float level1Spread = 15f;
[Tooltip("Lv2 (1~2초) 투척 시 오차 범위")]
[SerializeField] private float level2Spread = 7f;
[Tooltip("Lv3 (풀차징) 투척 시 오차 범위 (0이면 100% 명중)")]
[SerializeField] private float level3Spread = 0f;
[SerializeField] private float fullChargeTime = 2f;
private float _lastAttackTime, _chargeTimer;
private bool _isCharging;
// UI에서 차징 상태와 진행도를 확인하기 위한 통로
public float ChargeProgress => Mathf.Clamp01(_chargeTimer / fullChargeTime);
public bool IsCharging => _isCharging;
private void Update()
{
if (_isCharging)
{
_chargeTimer += Time.deltaTime;
// 차징 중 우클릭(1) 시 즉시 취소
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;
}
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);
// ⭐ [수정] 인스펙터에서 설정한 단계별 정확도(Spread) 적용
int lv = _chargeTimer >= 2f ? 3 : (_chargeTimer >= 1f ? 2 : 1);
float currentSpread = (lv == 3) ? level3Spread : (lv == 2 ? level2Spread : level1Spread);
// 최종 투척 방향 계산 (Spread 적용)
Vector3 finalThrowDir = Quaternion.Euler(0, Random.Range(-currentSpread, currentSpread), 0) * targetDirection;
interaction.CurrentWeapon.OnThrown(finalThrowDir, interaction.CurrentWeapon.Config.GetForce(lv), lv, stats);
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);
}
public void CancelCharging()
{
_isCharging = false;
_chargeTimer = 0f;
if (pAnim != null) pAnim.SetCharging(false);
}
}