using System; using System.Collections; using System.Collections.Generic; using Animation; using Unity.VisualScripting; using UnityEngine; using UnityEngine.AI; using UnityEngine.InputSystem; public partial class PlayerController : MonoBehaviour, IDamageable { #region Enum 정의 enum AnimatorParam { speed, atk_speed, attack, run, } enum PlayerBehavior { Idle, Move, Attack, Die } // rigidbody와 NavMeshAgent를 함께 사용할 때의 물리엔진 상 문제가 생겨서 그거를 구분하기 위한 부분 // NavMeshAgent가 뇌, rigidbody가 몸 역할을 하게 됨 public enum ControlMode { Player, AI } #endregion // 이벤트 public event Action OnExpChanged; // 상태 정보 private Status_Player _status; public Status_Player Status { get { return _status; } private set { _status = value;} } // 획득 private MagnetComponent _magnet; // 이동 관련 private float _runTriggerRatio = 0.6f; private float _rotationSpeed = 10f; private ControlMode _controlMode = ControlMode.Player; // 공격 관련 private NavMeshAgent _agent; private Rigidbody _rigidbody; public bool _isAttack = false; private Damage _weaponDamage; [SerializeField] private Transform _weaponSocket; [SerializeField] private Transform _shieldSocket; // 애니메이터 관련 private AnimatorManager _mAnimator; private PlayerBehavior _behavior = PlayerBehavior.Idle; private PlayerBehavior Behavior { get { return _behavior; } set { _behavior = value; switch (_behavior) { case PlayerBehavior.Die: case PlayerBehavior.Idle: _mAnimator.SetValue(AnimatorParam.speed, 0); break; case PlayerBehavior.Move: _mAnimator.SetValue(AnimatorParam.attack, false); break; case PlayerBehavior.Attack: _mAnimator.SetValue(AnimatorParam.attack, true); break; } } } private void Awake() { _mAnimator = new AnimatorManager(GetComponent()); _agent = gameObject.GetComponent(); _rigidbody = gameObject.GetComponent(); _status = gameObject.GetOrAddComponent(); _magnet = gameObject.GetComponentInChildren(); if(_agent != null) _agent.updateRotation = false; } void Start() { _status.Data = Manager.Data.LoadSingle("Data/PlayerData"); SetAnimator(); EquipWeapon(_weaponSocket, "Prefabs/Equipment/Weapon"); // EquipWeapon(_shieldSocket, "Prefabs/Equipment/Shield"); SubscribeAction(); } void SubscribeAction() { Manager.Input.MoveAction -= OnMove; Manager.Input.MoveAction += OnMove; Manager.Input.RegisterAction("Button_Attack", OnAttack); _magnet.OnGetExp -= OnGetExp; _magnet.OnGetExp += OnGetExp; } // 애니메이터 세팅 (지금은 한 줄 뿐이지만 후에 더 추가될 수도) void SetAnimator() { if (_status != null) { _mAnimator.SetValue(AnimatorParam.atk_speed, _status.AtkSpeed); } } // --- 매 프레임 실행 --- void Update() { PlayerMovement(); } private void PlayerMovement() { if (_controlMode == ControlMode.AI) { if(_agent.pathPending || !_agent.hasPath) return; _rigidbody.linearVelocity = Vector3.zero; // 회전 if (_agent.desiredVelocity.sqrMagnitude > 0.1f) { Quaternion targetRotation = Quaternion.LookRotation(_agent.desiredVelocity.normalized); transform.rotation = Quaternion.Slerp(transform.rotation, targetRotation, Time.deltaTime * _rotationSpeed); } if (_agent.remainingDistance <= _agent.stoppingDistance) { _rigidbody.linearVelocity = Vector3.zero; SetControlMode(ControlMode.Player); } } } public void SetControlMode(ControlMode mode) { _controlMode = mode; switch (_controlMode) { case ControlMode.Player: if(_agent != null) _agent.enabled = false; _rigidbody.isKinematic = false; _rigidbody.linearVelocity = Vector3.zero; break; case ControlMode.AI: _rigidbody.isKinematic = true; _rigidbody.linearVelocity = Vector3.zero; if (_agent != null) _agent.enabled = true; break; } } #region Navi Event void OnInput(Define.InputEvent evt) { switch (evt) { case Define.InputEvent.Click: break; case Define.InputEvent.Down: break; case Define.InputEvent.Up: break; case Define.InputEvent.Press: break; } } public void NavigateTo(Vector3 target) { SetControlMode(ControlMode.AI); if (_agent != null) { _agent.isStopped = false; // 목적지 설정 전 에이전트가 멈춰있지 않도록 합니다. _agent.SetDestination(target); _agent.speed = _status.MoveSpeed; } } #endregion void EquipWeapon(Transform transform, string path) { GameObject newParts = Manager.Resource.Instantiate($"{path}"); Damage damageScript = newParts.GetComponent(); // damageScript._targetlayer = (1<< 11) | (1 << 12); newParts.transform.SetParent(transform); newParts.transform.localPosition = Vector3.zero; newParts.transform.localRotation = Quaternion.identity; newParts.transform.localScale = Vector3.one; _weaponDamage = newParts.GetComponentInChildren(); if (_weaponDamage != null) { _weaponDamage.OnHit -= OnWeaponHit; _weaponDamage.OnHit += OnWeaponHit; } } #region Action Event private void OnMove(Vector2 direction) { SetControlMode(ControlMode.Player); float magnitude = direction.magnitude; bool isRun = direction.magnitude > _runTriggerRatio ? true : false; float speed = 0f; // 입력이 거의 없을 때 if (magnitude < 0.1f) speed = 0f; // 걷기 구간일 때 else if (magnitude <= _runTriggerRatio) speed = Mathf.Lerp(0, _status.MoveSpeed, (magnitude/_runTriggerRatio)); // 달리기 구간일 때 else { float runPercentage = (magnitude - _runTriggerRatio) / (1.0f - _runTriggerRatio); speed = Mathf.Lerp(_status.MoveSpeed, _status.MoveSpeed * 2.0f, runPercentage); } _agent.speed = speed; _mAnimator.SetValue(AnimatorParam.run, isRun); _mAnimator.SetValue(AnimatorParam.speed , speed); if (magnitude < 0.1f) { _rigidbody.linearVelocity = Vector3.zero; return; } Vector3 camForward = Camera.main.transform.forward; Vector3 camRight = Camera.main.transform.right; camForward.y = 0; camRight.y = 0; camForward.Normalize(); camRight.Normalize(); Vector3 moveDir = (camForward * direction.y + camRight * direction.x).normalized; if (moveDir != Vector3.zero) { Quaternion targetRotation = Quaternion.LookRotation(moveDir); transform.rotation = Quaternion.Slerp(transform.rotation, targetRotation, _rotationSpeed * Time.deltaTime); } _rigidbody.linearVelocity = transform.forward * speed; } private void OnWeaponHit(IDamageable target, Collider collider) { // 적 캐릭터인지 확인 // var enemy = target.GetComponent(); if (collider.gameObject.layer == LayerMask.NameToLayer("Enemy")) { Debug.Log($"Enemy Hit! {collider.name}"); } else { Debug.Log($"other Hit! {collider.name}"); } } private void OnAttack() { if (!_isAttack) { _isAttack = true; Behavior = PlayerBehavior.Attack; } } private void OnSkill(int index) { } private void OnGetExp(float amount) { if (_status != null) { _status.ApplyExp(amount); OnExpChanged?.Invoke(_status.Level ,_status.Exp, _status.MaxExp); Debug.Log($"Player Exp: {amount} / {_status.Exp}"); } } #endregion #region Interface 구현 public void TakeDamage(float damage) { float hp = Status.ApplyDamage(damage); Debug.Log($"적에게 피격! 데미지: {damage} / 남은 체력: {hp}"); } #endregion private void OnDestroy() { } } // 애니메이션의 이벤트 함수 부분 public partial class PlayerController { public void Hit() { _mAnimator.SetValue(AnimatorParam.attack, false); Debug.Log("Knight Hit!"); } public void FootR() { } public void FootL() { } public void EnableCollider() { _weaponDamage?.SetTriggerActive(true); } public void DisableCollider() { _weaponDamage?.SetTriggerActive(false); } public void EndAttack() { if (_isAttack) { _isAttack = false; _mAnimator.SetValue(AnimatorParam.attack, _isAttack); _weaponDamage?.SetTriggerActive(false); Behavior = PlayerBehavior.Idle; } } }