Practice_Unity/Assets/Scripts/Controllers/PlayerController.cs
SEAN d4b339c6af 작업
1. 패널
2. 세팅 오브젝트 생성

Todo
1. 패널 끄는거 만들기
2025-10-21 16:46:32 +09:00

390 lines
10 KiB
C#

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<string, float, float> 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<AnimatorParam> _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<AnimatorParam>(GetComponent<Animator>());
_agent = gameObject.GetComponent<NavMeshAgent>();
_rigidbody = gameObject.GetComponent<Rigidbody>();
_status = gameObject.GetOrAddComponent<Status_Player>();
_magnet = gameObject.GetComponentInChildren<MagnetComponent>();
if(_agent != null) _agent.updateRotation = false;
}
void Start()
{
_status.Data = Manager.Data.LoadSingle<PlayerDataLoader, Data_Status_Player>("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("Btn_Attack", OnAttack);
_magnet.OnGetExp -= OnGetExp;
_magnet.OnGetExp += OnGetExp;
SetEXPBar();
}
// 애니메이터 세팅 (지금은 한 줄 뿐이지만 후에 더 추가될 수도)
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<Damage>();
// 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<Damage>();
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<EnemyController>();
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);
SetEXPBar();
Debug.Log($"Player Exp: {amount} / {_status.Exp}");
}
}
private void SetEXPBar()
{
EventBus.Raise(new PlayerExpEvent
{
Level = _status.Level.ToString(),
CurrentExp = _status.Exp,
MaxExp = _status.MaxExp
});
}
#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;
}
}
}