이번에 소개할 Combat Class는 플레이어의 상태 Bool타입으로 관리하는 클래스입니다.
그런데 왜 Bool 타입으로 상태 클래스를 설계했는지 질문 하신다면,
정확한 지적입니다. 이 클래스는 잘못 설계되었습니다.
지금까지 RPG 게임을 제작해보지 않았던 저에겐
Bool 타입으로 상태를 관리한다는게 어떤 후폭풍을 낳는지 몰랐습니다.
먼저 Combat 클래스에 대해 설명하겠습니다.
이 클래스는 플레이어의 전투 상태(Combat)를 관리하는 클래스입니다.
왼쪽 마우스를 입력하면 기본 공격
오른쪽 마우스를 입력하면 무기 던지기
왼쪽 쉬프트를 입력하면 Bash를 시전합니다.
Combat Class는 3가지 행동을 모두 구현하는 것이 아닌
공격과 Bash를 Trigger하는 클래스입니다.
Bash , 기본 공격 , 무기 던지기는 각각의 Behavior를 담당하는 3개의 클래스로 선언됩니다.
3개의 클래스들은 Combat Class를 인스턴스화 하여 상태를 파악하고 행동을 취합니다.
정리하면 Key Input Class가 Combat Class에 영향을 주고
Combat Class는 플레이어의 행동의 Trigger를 발동합니다.
public class Combat : MonoBehaviour{
public bool HoldInProgress { get; private set; } = false;
public bool ThrowInProgress { get; private set; } = false;
public bool ThrowTrigger { get; private set; } = false;
public bool BashTrigger { get; private set; } = false;
private bool lastHoldBool = false;
private PlayerInput _playerInput;
private void CheckThrowProgress()
{
if (_playerInput.IsSpecialKeyHold) // 누르고 있는지 검사
{
//HoldInProgress = true;
}
else
{
HoldInProgress = false;
}
if (!HoldInProgress) // 눌렀다가 뗐을 때 던짐
{
if (lastHoldBool)
{
lastHoldBool = false;
ThrowTrigger = true;
ThrowInProgress = true;
AnimThrow();
}
}
}
private void CheckThrowEnd()
{
if (ThrowInProgress)
{
if (BashTrigger)
{
ThrowInProgress = false; //던지기 작업중 배쉬 발동시 던지기 작업 끝내기
}
if (_animator.GetCurrentAnimatorStateInfo(0).IsName("RFA_Movement"))
{
ThrowInProgress = false;
}
}
}
}
위 코드는 ComBat Class에서 무기 던지기 상태를 관리하는 함수입니다.
인스턴스로 생성된 PlayerInput의 Bool을 파악하여
Combat 클래스 내부에 있는 Private bool을 Set합니다.
그리고 무기 던지기 운동을 관리하는 클래스(아래 Manage Weapon Class)는
Combat 클래스에 선언 되어있는 Bool을 Get하여 동작합니다.
public class ManageWeapon : MonoBehavior{
[SerializeField] private Transform _firePos;
private GameObject copiedWeapon;
private Combat _combat;
private PlayerInput _playerInput;
private void Start()
{
_combat = GetComponent<Combat>();
_playerInput = GetComponent<PlayerInput>();
}
private void LateUpdate()
{
if (_combat.ThrowTrigger)
{
OnThrow();
_combat.ChangeBoolThrowTrigger();
}
}
private void OnThrow()
{
CreateWeapon();
_isHaveWeapon = !_isHaveWeapon;
}
private void CreateWeapon()
{
copiedWeapon = Instantiate(_weapon, _firePos.position, _firePos.rotation);
copiedWeapon.transform.rotation = _firePos.rotation;
copiedWeapon.GetComponent<Weapon>().ChangeBoolThrown();
}
}
Manage Weapon 클래스는 무기를 관리하는 클래스로
무기 던지기 행동을 담당하는 클래스입니다.
이 클래스는 Combat Class의 ThrowTrigger를 Get하여 무기를 생성합니다.
즉 무기를 던지는 행위는 PlayerInput 클래스로부터 키 입력 Bool을 Get하고
Combat 클래스의 Bool을 Get하여 발동할 수 있습니다.
이처럼 클래스를 3단계로 나눈 이유는
각각의 상태를 관리하는 클래스를 통해
클래스 사이의 참조가 직관적이며 행동의 과정을 독립적으로 선언할 수 있기 때문입니다.
또한 Unity의 플로우 차트에 존재하는
Update의 선후 관계를 직관적으로 나타낼 수 있습니다.
public class Combat : MonoBehaviour{
private PlayerInput _playerInput;
private ManageWeapon _manageWeapon;
public Animator _animator;
private bool lastHoldBool = false;
private bool lastBashBool = false;
public bool AttackInProgress { get; private set; } = false;
public bool HoldInProgress { get; private set; } = false;
public bool ThrowInProgress { get; private set; } = false;
public bool ThrowTrigger { get; private set; } = false;
public bool BashHold { get; private set; } = false;
public bool BashInProgress { get; private set; } = false;
public bool BashTrigger { get; private set; } = false;
private void Start()
{
_playerInput = GetComponent<PlayerInput>();
_manageWeapon = GetComponent<ManageWeapon>();
HoldInProgress = false;
}
private void Update()
{
if (_playerInput.AttackInput && !AttackInProgress)
{
AnimAttack();
}
else if (_playerInput.SpecialAttackInput && _manageWeapon.IsHaveWeapon && !HoldInProgress && !ThrowInProgress && !BashHold) // 처음 던지기 눌렀을 때
{
HoldInProgress = true;
lastHoldBool = true;
AnimHoldStart();
}
else if (_playerInput.BashInput && !HoldInProgress && !BashHold)
{
BashHold = true;
lastBashBool = true;
}
CheckBashHold();
CheckThrowProgress();
CheckThrowEnd(); // 던지기 동작 끝나는지 체크
}
}
위는 Combat Class 코드의 Update 구문 일부입니다.
길게 나열되어 있는 PlayerInput Class의 Bool 변수를 보니 현기증이 나는군요.
저는 Bool 타입으로 "HoldInProgress" , "ThrowInProgress"와 같이
진행상태를 기록하는 Bool을 사용하여 현재의 상태를 나누었습니다.
하지만 다들 예상하듯이 Bool 타입으로 상태를 관리하는 방법은
새로운 상태를 추가하는 작업이 굉장히 어려워지며
상태 전환의 기준점이 되는 지점을 관리하기가 어려워 추천되지 않는 방식입니다.
플레이어의 공격 상태 , 무기 던지기 상태까지 2개의 상태를 만드는 것은 수월했으나
3번째 상태 즉 Bash 상태를 추가하는 일은 여간 힘든 일이 아니었습니다.
그렇기에 Combat Class는 제가 감추고 싶은 코드이자, 발전이 필요한 미성숙한 클래스입니다.
만약 이런 Bool 타입 State 클래스에 새로운 상태를 추가해야한다면?
끔찍하겠죠 다른 방법을 찾아야합니다.
그래서 저는 이 클래스에 추가적인 기능을 작성하는 것을 그만두었습니다.
지금은 플레이어의 상태를 나누는 공격 , 무기 던지기 , Bash 상태까지 구현되어있으므로,
더 추가할 상태는 없긴하지만 새롭게 작성될 클래스는 이러한 형태여선 절대 안됩니다.
이러한 생각을 통해 Class에 대한 공부를 다시 하고 State Machine에 대한 연구로 이어지지만
이는 나중의 얘기입니다.
'개발 프로젝트 > Unity - WeaponGameProject' 카테고리의 다른 글
WeaponGameProject - Weapon 클래스 설계 (2) (Bash와 상호작용하는 Weapon의 상태 구현) (0) | 2023.11.28 |
---|---|
WeaponGameProject - Weapon 클래스 설계 (1) (SphereCast를 활용하는 감지 클래스) (1) | 2023.11.27 |
WeaponGameProject - Player 클래스 설계 (2) (Spring Arm Camera 구현) (1) | 2023.11.23 |
WeaponGameProject - Player 클래스 설계 (1) (Key Input을 클래스로 만들자) (1) | 2023.11.23 |
WeaponGameProject - 목차 (0) | 2023.11.23 |