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("--- 활 설정 ---")]
|
2026-02-06 05:32:48 +00:00
|
|
|
|
[SerializeField] private Transform firePoint;
|
|
|
|
|
|
[SerializeField] private PlayerAnimator pAnim;
|
2026-02-06 04:20:12 +00:00
|
|
|
|
|
2026-02-09 14:49:44 +00:00
|
|
|
|
[Header("--- 스탯 참조 ---")]
|
|
|
|
|
|
[SerializeField] private Stats playerStats;
|
|
|
|
|
|
|
2026-02-06 04:20:12 +00:00
|
|
|
|
[Header("--- 일반 공격 (좌클릭) ---")]
|
|
|
|
|
|
[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-09 14:49:44 +00:00
|
|
|
|
[Header("--- 에임 보정 설정 ---")]
|
|
|
|
|
|
[SerializeField] private bool enableAutoAim = true;
|
|
|
|
|
|
[SerializeField] private float autoAimRange = 15f;
|
|
|
|
|
|
[SerializeField] private float autoAimAngle = 45f;
|
2026-02-06 09:27:08 +00:00
|
|
|
|
[Range(0f, 1f)]
|
2026-02-09 14:49:44 +00:00
|
|
|
|
[SerializeField] private float aimAssistStrength = 0.4f;
|
|
|
|
|
|
[SerializeField] private LayerMask enemyLayer;
|
|
|
|
|
|
[SerializeField] private bool onlyMaxCharge = true;
|
|
|
|
|
|
|
|
|
|
|
|
// ============================================
|
|
|
|
|
|
// [NEW] 파티클 기반 화살 시스템
|
|
|
|
|
|
// ============================================
|
|
|
|
|
|
[Header("--- 파티클 화살 시스템 ---")]
|
|
|
|
|
|
[SerializeField] private GameObject defaultProjectilePrefab; // 기본 발사체 프리팹
|
|
|
|
|
|
|
|
|
|
|
|
private GameObject _currentProjectilePrefab;
|
|
|
|
|
|
private ArrowElementType _currentElementType = ArrowElementType.None;
|
|
|
|
|
|
private float _currentElementDamage = 0f;
|
|
|
|
|
|
private float _currentElementDuration = 0f;
|
2026-02-06 09:27:08 +00:00
|
|
|
|
|
2026-02-06 04:20:12 +00:00
|
|
|
|
private float _lastAttackTime;
|
|
|
|
|
|
private float _chargeTimer;
|
|
|
|
|
|
private bool _isCharging = false;
|
|
|
|
|
|
private bool _isAttacking = false;
|
2026-02-06 05:32:48 +00:00
|
|
|
|
private bool _waitForRelease = false;
|
|
|
|
|
|
|
2026-02-06 04:20:12 +00:00
|
|
|
|
private float _pendingDamage;
|
|
|
|
|
|
private float _pendingSpeed;
|
|
|
|
|
|
private float _pendingRange;
|
2026-02-06 09:27:08 +00:00
|
|
|
|
private Vector3 _pendingShootDirection;
|
|
|
|
|
|
|
2026-02-06 04:20:12 +00:00
|
|
|
|
public bool IsCharging => _isCharging;
|
2026-02-09 14:49:44 +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);
|
|
|
|
|
|
|
2026-02-09 14:49:44 +00:00
|
|
|
|
// [NEW] 외부에서 현재 속성 확인용
|
|
|
|
|
|
public ArrowElementType CurrentElement => _currentElementType;
|
|
|
|
|
|
public GameObject CurrentProjectilePrefab => _currentProjectilePrefab;
|
|
|
|
|
|
|
2026-02-06 04:20:12 +00:00
|
|
|
|
private void Start()
|
|
|
|
|
|
{
|
2026-02-09 14:49:44 +00:00
|
|
|
|
if (playerStats == null) playerStats = GetComponentInParent<Stats>();
|
|
|
|
|
|
|
|
|
|
|
|
// 기본 발사체 프리팹 설정
|
|
|
|
|
|
if (_currentProjectilePrefab == null)
|
|
|
|
|
|
_currentProjectilePrefab = defaultProjectilePrefab;
|
|
|
|
|
|
|
2026-02-06 04:20:12 +00:00
|
|
|
|
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-02-06 09:27:08 +00:00
|
|
|
|
|
|
|
|
|
|
float calculatedMaxTime = 0f;
|
|
|
|
|
|
foreach (var stage in chargeStages)
|
|
|
|
|
|
{
|
2026-02-09 14:49:44 +00:00
|
|
|
|
if (stage.chargeTime > calculatedMaxTime) calculatedMaxTime = stage.chargeTime;
|
2026-02-06 09:27:08 +00:00
|
|
|
|
}
|
|
|
|
|
|
maxChargeTime = Mathf.Max(calculatedMaxTime, 0.1f);
|
2026-02-06 04:20:12 +00:00
|
|
|
|
}
|
2026-01-30 07:45:11 +00:00
|
|
|
|
|
2026-01-29 06:58:38 +00:00
|
|
|
|
private void Update()
|
|
|
|
|
|
{
|
2026-02-09 14:49:44 +00:00
|
|
|
|
if (_isCharging) _chargeTimer += Time.deltaTime;
|
2026-01-29 06:58:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-09 14:49:44 +00:00
|
|
|
|
// ============================================
|
|
|
|
|
|
// [NEW] 화살 장착 (SwapArrow 대체)
|
|
|
|
|
|
// ============================================
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// ArrowPickup에서 습득 시 호출합니다.
|
|
|
|
|
|
/// 파티클 프리팹 + 속성 정보를 저장합니다.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public void SetCurrentArrow(ArrowData data)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (data.projectilePrefab != null)
|
|
|
|
|
|
_currentProjectilePrefab = data.projectilePrefab;
|
|
|
|
|
|
else
|
|
|
|
|
|
_currentProjectilePrefab = defaultProjectilePrefab;
|
|
|
|
|
|
|
|
|
|
|
|
_currentElementType = data.elementType;
|
|
|
|
|
|
_currentElementDamage = data.elementDamage;
|
|
|
|
|
|
_currentElementDuration = data.elementDuration;
|
|
|
|
|
|
|
|
|
|
|
|
Debug.Log($"화살 장착: [{data.arrowName}] 속성={data.elementType}, " +
|
|
|
|
|
|
$"속성데미지={data.elementDamage}, 지속시간={data.elementDuration}s");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 기본 화살로 초기화
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public void ResetArrow()
|
|
|
|
|
|
{
|
|
|
|
|
|
_currentProjectilePrefab = defaultProjectilePrefab;
|
|
|
|
|
|
_currentElementType = ArrowElementType.None;
|
|
|
|
|
|
_currentElementDamage = 0f;
|
|
|
|
|
|
_currentElementDuration = 0f;
|
|
|
|
|
|
Debug.Log("화살 초기화: 기본 화살");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ============================================
|
|
|
|
|
|
// 일반 공격 (좌클릭) — 기존 로직 100% 유지
|
|
|
|
|
|
// ============================================
|
|
|
|
|
|
|
2026-01-29 06:58:38 +00:00
|
|
|
|
public void PerformNormalAttack()
|
|
|
|
|
|
{
|
2026-02-09 14:49:44 +00:00
|
|
|
|
if (Time.time < _lastAttackTime + attackCooldown || _isAttacking) return;
|
2026-01-29 06:58:38 +00:00
|
|
|
|
|
2026-02-09 14:49:44 +00:00
|
|
|
|
_pendingDamage = (playerStats != null) ? playerStats.TotalAttackDamage : 10f;
|
2026-02-06 04:20:12 +00:00
|
|
|
|
_pendingSpeed = normalSpeed;
|
|
|
|
|
|
_pendingRange = normalRange;
|
2026-01-30 07:45:11 +00:00
|
|
|
|
|
2026-02-06 09:27:08 +00:00
|
|
|
|
_pendingShootDirection = GetMouseDirection();
|
2026-01-29 06:58:38 +00:00
|
|
|
|
_lastAttackTime = Time.time;
|
|
|
|
|
|
|
2026-02-06 04:20:12 +00:00
|
|
|
|
if (pAnim != null) pAnim.TriggerThrow();
|
|
|
|
|
|
StartCoroutine(AttackRoutine());
|
2026-01-30 07:45:11 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-09 14:49:44 +00:00
|
|
|
|
// ============================================
|
|
|
|
|
|
// 차징 공격 (우클릭) — 기존 로직 100% 유지
|
|
|
|
|
|
// ============================================
|
|
|
|
|
|
|
2026-02-06 04:20:12 +00:00
|
|
|
|
public void StartCharging()
|
2026-01-30 07:45:11 +00:00
|
|
|
|
{
|
2026-02-06 05:32:48 +00:00
|
|
|
|
if (_waitForRelease) return;
|
2026-02-06 04:20:12 +00:00
|
|
|
|
_isCharging = true;
|
|
|
|
|
|
_chargeTimer = 0f;
|
|
|
|
|
|
if (pAnim != null) pAnim.SetCharging(true);
|
|
|
|
|
|
if (CinemachineShake.Instance != null) CinemachineShake.Instance.SetZoom(true);
|
2026-01-29 06:58:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-06 05:32:48 +00:00
|
|
|
|
private void ResetChargingEffects()
|
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
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-06 05:32:48 +00:00
|
|
|
|
public void CancelCharging()
|
|
|
|
|
|
{
|
|
|
|
|
|
ResetChargingEffects();
|
2026-02-06 09:27:08 +00:00
|
|
|
|
_waitForRelease = false;
|
2026-02-06 05:32:48 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
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-09 14:49:44 +00:00
|
|
|
|
float baseDmg = (playerStats != null) ? playerStats.TotalAttackDamage : 10f;
|
|
|
|
|
|
_pendingDamage = baseDmg * currentStage.damageMult;
|
2026-02-06 04:20:12 +00:00
|
|
|
|
_pendingSpeed = normalSpeed * currentStage.rangeMult;
|
|
|
|
|
|
_pendingRange = normalRange * currentStage.rangeMult;
|
2026-01-31 13:07:35 +00:00
|
|
|
|
|
2026-02-09 14:49:44 +00:00
|
|
|
|
bool isMaxCharge = _chargeTimer >= maxChargeTime * 0.95f;
|
2026-02-06 09:27:08 +00:00
|
|
|
|
_pendingShootDirection = GetShootDirection(isMaxCharge);
|
|
|
|
|
|
|
2026-02-06 04:20:12 +00:00
|
|
|
|
if (pAnim != null) pAnim.TriggerThrow();
|
2026-02-06 05:32:48 +00:00
|
|
|
|
_waitForRelease = true;
|
2026-02-06 04:20:12 +00:00
|
|
|
|
_lastAttackTime = Time.time;
|
|
|
|
|
|
StartCoroutine(AttackRoutine());
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-09 14:49:44 +00:00
|
|
|
|
// ============================================
|
|
|
|
|
|
// 에임 보정 시스템 — 기존 로직 100% 유지
|
|
|
|
|
|
// ============================================
|
|
|
|
|
|
|
2026-02-06 09:27:08 +00:00
|
|
|
|
private Vector3 GetMouseDirection()
|
|
|
|
|
|
{
|
|
|
|
|
|
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
|
2026-02-09 14:49:44 +00:00
|
|
|
|
Plane firePlane = new Plane(Vector3.up, firePoint.position);
|
2026-02-06 09:27:08 +00:00
|
|
|
|
|
2026-02-09 14:49:44 +00:00
|
|
|
|
if (firePlane.Raycast(ray, out float distance))
|
2026-02-06 09:27:08 +00:00
|
|
|
|
{
|
|
|
|
|
|
Vector3 worldMousePos = ray.GetPoint(distance);
|
|
|
|
|
|
Vector3 direction = (worldMousePos - firePoint.position).normalized;
|
2026-02-09 14:49:44 +00:00
|
|
|
|
direction.y = 0;
|
2026-02-06 09:27:08 +00:00
|
|
|
|
return direction;
|
|
|
|
|
|
}
|
|
|
|
|
|
return transform.forward;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private Vector3 GetShootDirection(bool isMaxCharge)
|
|
|
|
|
|
{
|
|
|
|
|
|
Vector3 mouseDir = GetMouseDirection();
|
2026-02-09 14:49:44 +00:00
|
|
|
|
if (!enableAutoAim || (onlyMaxCharge && !isMaxCharge)) return mouseDir;
|
2026-02-06 09:27:08 +00:00
|
|
|
|
|
|
|
|
|
|
Transform bestTarget = FindBestTarget(mouseDir);
|
|
|
|
|
|
if (bestTarget != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
Vector3 targetPos = bestTarget.position + Vector3.up * 1.2f;
|
|
|
|
|
|
Vector3 targetDir = (targetPos - firePoint.position).normalized;
|
2026-02-09 14:49:44 +00:00
|
|
|
|
targetDir.y = 0;
|
2026-02-06 09:27:08 +00:00
|
|
|
|
|
|
|
|
|
|
Vector3 assistedDir = Vector3.Lerp(mouseDir, targetDir, aimAssistStrength);
|
|
|
|
|
|
assistedDir.Normalize();
|
|
|
|
|
|
return assistedDir;
|
|
|
|
|
|
}
|
|
|
|
|
|
return mouseDir;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private Transform FindBestTarget(Vector3 mouseDirection)
|
|
|
|
|
|
{
|
|
|
|
|
|
Collider[] enemies = Physics.OverlapSphere(transform.position, autoAimRange, enemyLayer);
|
|
|
|
|
|
if (enemies.Length == 0) return null;
|
|
|
|
|
|
|
|
|
|
|
|
Transform bestTarget = null;
|
|
|
|
|
|
float bestScore = float.MaxValue;
|
|
|
|
|
|
|
|
|
|
|
|
foreach (var enemy in enemies)
|
|
|
|
|
|
{
|
|
|
|
|
|
Vector3 dirToEnemy = (enemy.transform.position - transform.position).normalized;
|
|
|
|
|
|
float angle = Vector3.Angle(mouseDirection, dirToEnemy);
|
2026-02-09 14:49:44 +00:00
|
|
|
|
if (angle > autoAimAngle) continue;
|
2026-02-06 09:27:08 +00:00
|
|
|
|
|
|
|
|
|
|
float distance = Vector3.Distance(transform.position, enemy.transform.position);
|
|
|
|
|
|
float score = angle * 0.5f + distance * 0.5f;
|
|
|
|
|
|
|
|
|
|
|
|
if (score < bestScore)
|
|
|
|
|
|
{
|
|
|
|
|
|
bestScore = score;
|
|
|
|
|
|
bestTarget = enemy.transform;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return bestTarget;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-09 14:49:44 +00:00
|
|
|
|
// ============================================
|
|
|
|
|
|
// [MODIFIED] 화살 생성 — 파티클 프리팹 발사
|
|
|
|
|
|
// ============================================
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 애니메이션 이벤트에서 호출됩니다.
|
|
|
|
|
|
/// 파티클 프리팹을 Instantiate하고 PlayerArrow 컴포넌트로 이동/충돌을 처리합니다.
|
|
|
|
|
|
/// </summary>
|
2026-02-06 04:20:12 +00:00
|
|
|
|
public void OnShootArrow()
|
|
|
|
|
|
{
|
2026-02-09 14:49:44 +00:00
|
|
|
|
if (_currentProjectilePrefab == null || firePoint == null)
|
2026-02-06 09:27:08 +00:00
|
|
|
|
{
|
2026-02-09 14:49:44 +00:00
|
|
|
|
Debug.LogWarning("발사체 프리팹 또는 firePoint가 없습니다!");
|
2026-02-06 09:27:08 +00:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
2026-01-29 06:58:38 +00:00
|
|
|
|
|
2026-02-09 14:49:44 +00:00
|
|
|
|
// 파티클 프리팹을 발사 위치에 생성
|
|
|
|
|
|
Quaternion rotation = Quaternion.LookRotation(_pendingShootDirection);
|
|
|
|
|
|
GameObject projectile = Instantiate(_currentProjectilePrefab, firePoint.position, rotation);
|
|
|
|
|
|
|
|
|
|
|
|
// PlayerArrow 컴포넌트 확인/추가 후 초기화
|
|
|
|
|
|
PlayerArrow arrowScript = projectile.GetComponent<PlayerArrow>();
|
|
|
|
|
|
if (arrowScript == null)
|
2026-01-29 06:58:38 +00:00
|
|
|
|
{
|
2026-02-09 14:49:44 +00:00
|
|
|
|
arrowScript = projectile.AddComponent<PlayerArrow>();
|
2026-01-29 06:58:38 +00:00
|
|
|
|
}
|
2026-02-09 14:49:44 +00:00
|
|
|
|
|
|
|
|
|
|
// 발사 정보 + 속성 정보 전달
|
|
|
|
|
|
arrowScript.Initialize(
|
|
|
|
|
|
_pendingDamage,
|
|
|
|
|
|
_pendingSpeed,
|
|
|
|
|
|
_pendingRange,
|
|
|
|
|
|
_pendingShootDirection,
|
|
|
|
|
|
_currentElementType,
|
|
|
|
|
|
_currentElementDamage,
|
|
|
|
|
|
_currentElementDuration
|
|
|
|
|
|
);
|
2026-01-29 06:58:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-09 14:49:44 +00:00
|
|
|
|
// ============================================
|
|
|
|
|
|
// 유틸리티 — 기존 로직 100% 유지
|
|
|
|
|
|
// ============================================
|
|
|
|
|
|
|
2026-02-06 05:32:48 +00:00
|
|
|
|
public void OnAttackEnd()
|
|
|
|
|
|
{
|
|
|
|
|
|
_isAttacking = false;
|
|
|
|
|
|
ResetChargingEffects();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
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;
|
2026-02-06 05:32:48 +00:00
|
|
|
|
yield return new WaitForSeconds(0.6f);
|
|
|
|
|
|
if (_isAttacking)
|
|
|
|
|
|
{
|
|
|
|
|
|
_isAttacking = false;
|
|
|
|
|
|
ResetChargingEffects();
|
|
|
|
|
|
}
|
2026-01-29 06:58:38 +00:00
|
|
|
|
}
|
2026-02-01 15:49:12 +00:00
|
|
|
|
|
2026-02-09 14:49:44 +00:00
|
|
|
|
// [DEPRECATED] 하위 호환성을 위해 남겨둠
|
|
|
|
|
|
[System.Obsolete("SetCurrentArrow(ArrowData)를 사용하세요.")]
|
2026-02-06 09:27:08 +00:00
|
|
|
|
public void SwapArrow(GameObject newArrow)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (newArrow == null) return;
|
2026-02-09 14:49:44 +00:00
|
|
|
|
Debug.LogWarning("SwapArrow()는 더 이상 사용되지 않습니다. SetCurrentArrow()를 사용하세요.");
|
2026-02-06 09:27:08 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-06 04:20:12 +00:00
|
|
|
|
public void StartWeaponCollision() { }
|
|
|
|
|
|
public void StopWeaponCollision() { }
|
2026-02-06 09:27:08 +00:00
|
|
|
|
|
2026-02-09 14:49:44 +00:00
|
|
|
|
// ============================================
|
|
|
|
|
|
// Gizmo 디버그 — 기존 로직 100% 유지
|
|
|
|
|
|
// ============================================
|
|
|
|
|
|
|
2026-02-06 09:27:08 +00:00
|
|
|
|
private void OnDrawGizmosSelected()
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!enableAutoAim) return;
|
|
|
|
|
|
Gizmos.color = Color.yellow;
|
|
|
|
|
|
Gizmos.DrawWireSphere(transform.position, autoAimRange);
|
|
|
|
|
|
|
|
|
|
|
|
if (Application.isPlaying && firePoint != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
Vector3 mouseDir = GetMouseDirection();
|
|
|
|
|
|
Gizmos.color = Color.blue;
|
|
|
|
|
|
Gizmos.DrawRay(firePoint.position, mouseDir * 5f);
|
|
|
|
|
|
|
|
|
|
|
|
Transform target = FindBestTarget(mouseDir);
|
|
|
|
|
|
if (target != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
Vector3 targetPos = target.position + Vector3.up * 1.2f;
|
|
|
|
|
|
Vector3 targetDir = (targetPos - firePoint.position).normalized;
|
|
|
|
|
|
targetDir.y = 0;
|
|
|
|
|
|
Vector3 assistedDir = Vector3.Lerp(mouseDir, targetDir, aimAssistStrength);
|
|
|
|
|
|
assistedDir.Normalize();
|
|
|
|
|
|
|
|
|
|
|
|
Gizmos.color = Color.red;
|
|
|
|
|
|
Gizmos.DrawRay(firePoint.position, assistedDir * 7f);
|
|
|
|
|
|
Gizmos.color = Color.green;
|
|
|
|
|
|
Gizmos.DrawWireSphere(targetPos, 0.3f);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-01-29 06:58:38 +00:00
|
|
|
|
}
|