Degulleo3D/Assets/JAY/Scripts/PlayerController.cs

294 lines
8.0 KiB
C#

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEditor.TextCore.Text;
using UnityEngine;
public enum PlayerState { None, Idle, Move, Win, Hit, Dead }
public class PlayerController : CharacterBase, IObserver<GameObject>
{
// 외부 접근 가능 변수
[Header("Attach Points")]
[SerializeField] private Transform rightHandTransform;
[SerializeField] private CameraShake cameraShake;
// 내부에서만 사용하는 변수
private PlayerHitEffectController hitEffectController;
private CharacterController _characterController;
private bool _isBattle;
private GameObject weapon;
private WeaponController _weaponController;
private IPlayerState _currentStateClass { get; set; }
private IPlayerAction _currentAction;
public IPlayerAction CurrentAction => _currentAction;
// 상태 관련
private PlayerStateIdle _playerStateIdle;
private PlayerStateMove _playerStateMove;
private PlayerStateWin _playerStateWin;
private PlayerStateDead _playerStateDead;
// 행동 관련
private PlayerActionAttack _attackAction;
private PlayerActionDash _actionDash;
// 외부에서도 사용하는 변수
public FixedJoystick Joystick { get; private set; }
public PlayerState CurrentState { get; private set; }
private Dictionary<PlayerState, IPlayerState> _playerStates;
public Animator PlayerAnimator { get; private set; }
public CharacterController CharacterController => _characterController;
private void Awake()
{
PlayerAnimator = GetComponent<Animator>();
_characterController = GetComponent<CharacterController>();
if (Joystick == null)
{
Joystick = FindObjectOfType<FixedJoystick>();
}
}
protected override void Start()
{
base.Start();
hitEffectController = GetComponentInChildren<PlayerHitEffectController>();
PlayerInit();
SwitchBattleMode();
}
private void Update()
{
if (CurrentState != PlayerState.None)
{
_playerStates[CurrentState].Update();
}
// 대시 우선 입력 처리
if (Input.GetKeyDown(KeyCode.Space))
{
StartDashAction();
return;
}
// 공격 입력 처리
if (Input.GetKeyDown(KeyCode.X) && (_currentAction == null || !_currentAction.IsActive)
&& (CurrentState != PlayerState.Win && CurrentState != PlayerState.Dead)) {
StartAttackAction();
}
// 액션 업데이트
if (_currentAction != null && _currentAction.IsActive)
{
_currentAction.UpdateAction();
}
}
private void OnDestroy()
{
OnGetHit -= TakeDamage;
}
#region
private void PlayerInit()
{
// 상태 초기화
_playerStateIdle = new PlayerStateIdle();
_playerStateMove = new PlayerStateMove();
_playerStateWin = new PlayerStateWin();
_playerStateDead = new PlayerStateDead();
_playerStates = new Dictionary<PlayerState, IPlayerState>
{
{ PlayerState.Idle, _playerStateIdle },
{ PlayerState.Move, _playerStateMove },
{ PlayerState.Win, _playerStateWin },
{ PlayerState.Dead, _playerStateDead },
};
_attackAction = new PlayerActionAttack();
_actionDash = new PlayerActionDash();
OnGetHit -= TakeDamage;
OnGetHit += TakeDamage;
SetState(PlayerState.Idle);
InstantiateWeapon();
}
private void InstantiateWeapon()
{
if (weapon == null)
{
GameObject weaponObject = Resources.Load<GameObject>("Player/Weapon/Chopstick");
weapon = Instantiate(weaponObject, rightHandTransform);
_weaponController = weapon?.GetComponent<WeaponController>();
_weaponController?.Subscribe(this);
weapon?.SetActive(_isBattle);
}
}
#endregion
#region ,
public void SetState(PlayerState state)
{
if (CurrentState != PlayerState.None)
{
_playerStates[CurrentState].Exit();
}
CurrentState = state;
_currentStateClass = _playerStates[state];
_currentStateClass.Enter(this);
}
public void StartAttackAction() {
_currentAction = _attackAction;
_currentAction.StartAction(this);
}
public void StartDashAction()
{
// 만약 공격 중이면 강제로 공격 종료
if (_currentAction == _attackAction && _attackAction.IsActive)
{
_attackAction.EndAction(); // 애니메이션도 중단
_weaponController.AttackEnd();
}
// 기존 대시 중이면 중복 실행 안 함
if (_actionDash.IsActive)
return;
_currentAction = _actionDash;
_actionDash.StartAction(this);
}
public void OnActionEnded(IPlayerAction action)
{
if (_currentAction == action) _currentAction = null;
}
#endregion
#region
public void SwitchBattleMode()
{
_isBattle = !_isBattle;
weapon.SetActive(_isBattle);
}
// Animation Event에서 호출될 메서드
public void SetAttackComboTrue()
{
if (_weaponController.IsAttacking) return; // 이미 공격 중이면 실행 안함
if (_currentAction == _attackAction) {
_attackAction.EnableCombo();
_weaponController.AttackStart();
}
}
public void SetAttackComboFalse()
{
if (_currentAction == _attackAction) {
// 이벤트 중복 호출? 공격 종료 시 SetAttackComboFalse가 아니라 ~True로 끝나서 오류 발생. (공격 안하는 상태여도 공격으로 판정됨)
_attackAction.DisableCombo();
_weaponController.AttackEnd(); // IsAttacking = false로 변경
}
}
#endregion
#region
public Vector3 GetMoveDirectionOrForward()
{
Vector3 dir = new Vector3(Joystick.Horizontal, 0, Joystick.Vertical);
return dir.sqrMagnitude > 0.01f ? dir.normalized : transform.forward;
}
public void DashButtonPressed()
{
if (!_actionDash.IsActive)
{
StartDashAction();
}
}
#endregion
#region IObserver
public void OnNext(GameObject value)
{
float playerAttackPower = _weaponController.AttackPower * attackPower;
if (value.CompareTag("Enemy")) // 적이 Enemy일 때만 공격 처리
{
var enemyController = value.transform.GetComponent<EnemyController>();
if (enemyController != null)
{
enemyController.TakeDamage(playerAttackPower);
}
}
}
public void OnError(Exception error)
{
}
public void OnCompleted()
{
_weaponController.Unsubscribe(this);
}
#endregion
#region
// TODO: Editor에서 확인하기 위한 임시용
public void TakeDamage()
{
if (CurrentState == PlayerState.Dead) return;
// 피격 이벤트 재생
PlayerHitEffect();
// 죽었는지 체크
if (currentHP <= 0) SetState(PlayerState.Dead);
}
public void TakeDamage(CharacterBase character)
{
if (character != this) return; // 혹시 다른 애가 맞은 경우 무시
if (CurrentState == PlayerState.Dead) return;
// 피격 이벤트 재생
PlayerHitEffect();
// 죽었는지 체크
if (currentHP <= 0) SetState(PlayerState.Dead);
}
private void PlayerHitEffect()
{
if (_currentAction != _attackAction || !_attackAction.IsActive)
PlayerAnimator.SetTrigger("GetHit");
hitEffectController.PlayHitEffect();
cameraShake.Shake();
}
#endregion
}