using System; using System.Collections; using Sirenix.OdinInspector; using UnityEngine; using UnityEngine.InputSystem; // ReSharper disable once CheckNamespace namespace BlueWaterProject { public class CombatPlayer : MonoBehaviour, IDamageable { [Title("초기화 방식")] [SerializeField] private bool autoInit = true; [Title("캐릭터 변수")] [SerializeField] private float maxHp = 100f; [SerializeField] private float currentHp; [SerializeField] private float moveSpeed = 5f; [field: Title("대쉬")] [field: SerializeField] public float DashForce { get; set; } = 20f; [field: SerializeField] public float DashCooldown { get; set; } = 0.5f; [field: DisableIf("@true")] [field: SerializeField] public bool IsDashing { get; set; } [field: DisableIf("@true")] [field: SerializeField] public bool EnableDash { get; set; } = true; [field: Title("공격")] [field: SerializeField] public int MaxHitNum { get; set; } = 10; [field: SerializeField] public float AttackDamage { get; set; } = 10f; [field: SerializeField] public float AttackRange { get; set; } = 1.5f; [field: SerializeField] public float AttackAngle { get; set; } = 180f; [field: SerializeField] public float ComboTime { get; set; } = 0.5f; [field: SerializeField] public LayerMask TargetLayer { get; set; } [field: DisableIf("@true")] [field: SerializeField] public bool IsAttacking { get; set; } [field: DisableIf("@true")] [field: SerializeField] public bool IsComboAttacking { get; set; } [field: DisableIf("@true")] [field: SerializeField] public bool IsComboPossible { get; set; } [Title("컴포넌트")] [SerializeField] private PlayerInput playerInput; [field: SerializeField] public Rigidbody Rb { get; set; } [SerializeField] private Transform visualLook; [field: SerializeField] public Animator Animator { get; set; } private Vector2 movementInput; public Vector3 PreviousDir { get; set; } = Vector3.back; [field: SerializeField] public Collider[] HitColliders { get; set; } private static readonly int XDirectionHash = Animator.StringToHash("xDirection"); private static readonly int ZDirectionHash = Animator.StringToHash("zDirection"); private static readonly int IsMovingHash = Animator.StringToHash("isMoving"); public readonly int isDashingHash = Animator.StringToHash("isDashing"); public readonly int isAttackingHash = Animator.StringToHash("isAttacking"); [Button("셋팅 초기화")] private void Init() { playerInput = GetComponent(); Rb = GetComponent(); visualLook = transform.Find("VisualLook"); Animator = visualLook.GetComponent(); } private void Awake() { if (autoInit) { Init(); } } private void Start() { HitColliders = new Collider[MaxHitNum]; SetCurrentHp(maxHp); } private void OnEnable() { playerInput.actions.FindAction("Attack").performed += OnAttackEvent; } private void OnDisable() { playerInput.actions.FindAction("Attack").performed -= OnAttackEvent; } private void Update() { HandleMovement(); } public void TakeDamage(float attackerPower, Vector3? attackPos = null) { if (IsDashing) return; var changeHp = Mathf.Max(currentHp - attackerPower, 0); SetCurrentHp(changeHp); if (InIslandCamera.Inst.InIslandCam) { VisualFeedbackManager.Inst.CameraShake(InIslandCamera.Inst.InIslandCam); } // 죽었는지 체크 if (changeHp == 0f) { Die(); return; } } public void Die() { print("Combat Player Die"); } private void OnMove(InputValue value) { movementInput = value.Get(); } public void OnDash() { if (!EnableDash || IsDashing) return; Animator.SetBool(isDashingHash, true); } private void OnAttackEvent(InputAction.CallbackContext context) { if (IsAttacking && !IsComboPossible) return; // var control = context.control; // // if (control.name.Equals("leftButton")) // { // UseMouseAttack = true; // } // else if (control.name.Equals("k")) // { // UseMouseAttack = false; // } if (IsComboPossible) { IsComboAttacking = true; return; } Animator.SetBool(isAttackingHash, true); } private void HandleMovement() { if (!IsDashing) { var movement = new Vector3(movementInput.x, 0, movementInput.y); var moveDirection = movement.normalized; Rb.velocity = moveDirection * moveSpeed; } var localScale = visualLook.localScale; localScale.x = Rb.velocity.x switch { > 0.01f => Mathf.Abs(localScale.x), < -0.01f => -Mathf.Abs(localScale.x), _ => localScale.x }; visualLook.localScale = localScale; if (Rb.velocity != Vector3.zero) { PreviousDir = Rb.velocity.normalized; Animator.SetFloat(XDirectionHash, PreviousDir.x); Animator.SetFloat(ZDirectionHash, PreviousDir.z); } var isMoving = Rb.velocity != Vector3.zero; Animator.SetBool(IsMovingHash, isMoving); } public void AttackTiming() { var attackDirection = PreviousDir; Array.Clear(HitColliders, 0, MaxHitNum); var size = Physics.OverlapSphereNonAlloc(transform.position, AttackRange, HitColliders, TargetLayer); for (var i = 0; i < size; i++) { var targetDirection = (HitColliders[i].transform.position - transform.position).normalized; var angleBetween = Vector3.Angle(attackDirection, targetDirection); if (angleBetween >= AttackAngle * 0.5f) continue; if (HitColliders[i].gameObject.layer == LayerMask.NameToLayer("Enemy")) { var iDamageable = HitColliders[i].transform.GetComponent(); iDamageable.TakeDamage(AttackDamage); VisualFeedbackManager.Inst.TriggerHitStop(0.1f); } else if (HitColliders[i].gameObject.layer == LayerMask.NameToLayer("Skill") && HitColliders[i].CompareTag("DestructiveSkill")) { var iDamageable = HitColliders[i].transform.GetComponent(); iDamageable.TakeDamage(AttackDamage); } } } public void CoolDown(float waitTime, Action onCooldownComplete = null) { StartCoroutine(CoolDownCoroutine(waitTime, onCooldownComplete)); } private IEnumerator CoolDownCoroutine(float waitTime, Action onCooldownComplete = null) { var time = 0f; while (time <= waitTime) { time += Time.deltaTime; yield return null; } onCooldownComplete?.Invoke(); } private void SetCurrentHp(float value) { currentHp = value; } } }