TIL (63) | 2023.10.25 | Enemy상태머신

kjg5370·2023년 10월 25일
0

TIL

목록 보기
62/91
post-thumbnail

들어가기 앞서

오늘은 enemy의 상태 머신을 구현해보았습니다. 상태머신이 어떤식으로 동작하는지 조금밖에 이해못하는 상태였는데 오늘 새로운 상태를 만들어보면서 좀 더 잘 이해할 수 있었습니다.

오늘 배운 것

  • enemy 상태 머신
    enemy의 상태는 현재 총 4가지가 있으며
    이 상태들은 EnemyBaseState를 기본적으로 상속받습니다. EnemyBaseState는 enemy의 이동이나 방향등의 함수를 가지고 있습니다.

    • EnemyIdleState

      public class EnemyIdleState : EnemyBaseState
      {
          public EnemyIdleState(EnemyStateMachine ememyStateMachine) : base(ememyStateMachine)
          {
          }
      
          public override void Enter()
          {
              stateMachine.MovementSpeedModifier = 0f;
      
              base.Enter();
              StartAnimation(stateMachine.Enemy.AnimationData.GroundParameterHash);
              StartAnimation(stateMachine.Enemy.AnimationData.IdleParameterHash);
          }
      
          public override void Exit()
          {
              base.Exit();
              StopAnimation(stateMachine.Enemy.AnimationData.GroundParameterHash);
              StopAnimation(stateMachine.Enemy.AnimationData.IdleParameterHash);
          }
      
          public override void Update()
          {
              if (IsInChaseRange())
              {
                  stateMachine.ChangeState(stateMachine.ChasingState);
                  return;
              }
          }
      }

      EnemyIdleState는 주변에 아무것도 없이 기본적으로 움직이지 않는 상태 입니다. 만약 주변에 플레이어가 접근한다면 EnemyBaseState의 IsInChaseRange() 함수를 통해 플레이어를 추적하게 됩니다.

    • EnemyChasingState

       public class EnemyChasingState : EnemyBaseState
      {
          public EnemyChasingState(EnemyStateMachine ememyStateMachine) : base(ememyStateMachine)
          {
          }
          public override void Enter()
          {
              stateMachine.MovementSpeedModifier = 1;
              base.Enter();
              StartAnimation(stateMachine.Enemy.AnimationData.GroundParameterHash);
              StartAnimation(stateMachine.Enemy.AnimationData.RunParameterHash);
          }
      
          public override void Exit()
          {
              base.Exit();
              StopAnimation(stateMachine.Enemy.AnimationData.GroundParameterHash);
              StopAnimation(stateMachine.Enemy.AnimationData.RunParameterHash);
          }
      
          public override void Update()
          {
              base.Update();
              if (!IsInChaseRange())
              {
                  stateMachine.ChangeState(stateMachine.IdlingState);
                  return;
              }
              else if (IsInAttackRange())
              {
                  stateMachine.ChangeState(stateMachine.AttackState);
                  return;
              }
              else if (IsOutReturnRange())
              {
                  stateMachine.ChangeState(stateMachine.ReturnState);
                  return;
              }
          }
      
          private bool IsInAttackRange()
          {
              // if (stateMachine.Target.IsDead) { return false; }
      
              float playerDistanceSqr = (stateMachine.Target.transform.position - stateMachine.Enemy.transform.position).sqrMagnitude;
      
              return playerDistanceSqr <= stateMachine.Enemy.Data.AttackRange * stateMachine.Enemy.Data.AttackRange;
          }
          private bool IsOutReturnRange()
          {
      
              float playerDistanceSqr = (stateMachine.Enemy.originalPosition.position - stateMachine.Enemy.transform.position).sqrMagnitude;
      
              return playerDistanceSqr >= stateMachine.Enemy.Data.ReturnDistance * stateMachine.Enemy.Data.ReturnDistance;
          }
      }

      플레이어를 추적하는 중 공격 사거리에 들어오면 AttackState로 상태를 변경하고 원래 위치에서 특정 거리 이상을 추적하게 되면 원래 위치로 되돌아갑니다.

    • EnemyAttackState

        public class EnemyAttackState : EnemyBaseState
      {
          public EnemyAttackState(EnemyStateMachine ememyStateMachine) : base(ememyStateMachine)
          {
          }
      
          public override void Enter()
          {
              stateMachine.MovementSpeedModifier = 0;
              base.Enter();
              StartAnimation(stateMachine.Enemy.AnimationData.AttackParameterHash);
              StartAnimation(stateMachine.Enemy.AnimationData.BaseAttackParameterHash);
          }
      
          public override void Exit()
          {
              base.Exit();
              StopAnimation(stateMachine.Enemy.AnimationData.AttackParameterHash);
              StopAnimation(stateMachine.Enemy.AnimationData.BaseAttackParameterHash);
          }
      
          public override void Update()
          {
              base.Update();
      
              ForceMove();
      
              float normalizedTime = GetNormalizedTime(stateMachine.Enemy.Animator, "Attack");
              if (normalizedTime >= 1f)
              {
                  if (IsInChaseRange())
                  {
                      stateMachine.ChangeState(stateMachine.ChasingState);
                      return;
                  }
                  else
                  {
                      stateMachine.ChangeState(stateMachine.IdlingState);
                      return;
                  }
              }
          }
      }

      Attack 태그를 가진 애니메이션을 실행하고 플레이어가 다른 곳으로 이동을 하게 되면 다시 추적하거나 너무 멀어지면 기본 상태로 되돌아갑니다.

    • EnemyReturnState

        public class EnemyReturnState : EnemyBaseState
      {
          public EnemyReturnState(EnemyStateMachine ememyStateMachine) : base(ememyStateMachine)
          {
          }
          public override void Enter()
          {
              stateMachine.MovementSpeedModifier = 1;
              base.Enter();
              StartAnimation(stateMachine.Enemy.AnimationData.GroundParameterHash);
              StartAnimation(stateMachine.Enemy.AnimationData.RunParameterHash);
          }
      
          public override void Exit()
          {
              base.Exit();
              StopAnimation(stateMachine.Enemy.AnimationData.GroundParameterHash);
              StopAnimation(stateMachine.Enemy.AnimationData.RunParameterHash);
          }
      
          public override void Update()
          {
              if(!IsInReturnPosition())
              {
                  ReturnToOriginalPosition();
                  return;
              }else
              {
                  stateMachine.ChangeState(stateMachine.IdlingState);
                  return;
              }
          }
          private bool IsInReturnPosition()
          {
              float playerDistanceSqr = (stateMachine.Enemy.originalPosition.position - stateMachine.Enemy.transform.position).sqrMagnitude;
      
              return playerDistanceSqr <= 0.1;
          }
      }

      enemy가 추적 도중 원래 위치에서 너무 멀어졌을 때 다시 원래 위치로 되돌아가는 상태입니다.

기억 할 것

  • EnemyAttackState
    이 상태 스크립트에서 Update부분에서 다른 상태로 넘어가지 않는 현상을 발견했는데 원인은 attack 애니메이션의 태그를 지정해주지 않아
    GetNormalizedTime()이 자꾸 0으로 초기화 되었기 때문이였습니다.

=> 이런 부분은 오류 메시지가 나타나지 않아서 디버깅이 어려울 때도 있지만
코드 리뷰와 디버깅 도구를 적극적으로 활용하고
주석을 추가하며 나중에 동일한 문제가 발생할 때
같은 실수를 반복하지 않도록 하겠습니다.

내일 할 일

  • enemy의 감시 동작 추가
    • enemy가 지정된 위치의 주변을 감시하며 움직임 => 플레이어 발견 시 추적
profile
학생입니다

0개의 댓글