[패스트캠퍼스 수강 후기] 올인원 패키지 : 유니티 포트폴리오 완성 100% 환급 챌린지 10회차 미션 시작합니다.



저번에 이어지는 적 캐릭터를 위한 AI 구현입니다.



사실 구현 내용이 대부분이라 캡쳐 화면도 필요없었지만 너무 없어서 ㅋㅋ 제일 유용한 부분을 넣었습니다.
IdleState -> MoveState -> IdleState가 언제 어떻게 발생하는지, 그리고 그러한 이유때문에 스크립트 소스코드를 그렇게 짠 것을 이해하기 위함입니다.

 



EnemyController_New.cs

public LayserMask targetaMask; // targer Layer를 체크하기 위함.
public float viewRadius; // 적이 접근해 있는 반경 체크하기
public Transform target; // 적에 대한 위치
public float attackRange;

public bool IsAvailableAttack
{
    get
    {
        if (!target) return false;
        float distance = Vector3.Distance(transform.position, target.position);
        return (distance <= attackRange);
    }
}

 

public Transform SearchEnemy()
{
    target = null;
    Collider[] targetInViewRadius = Physics.OverlapSphere(transform.position, viewRadius, targetMask);
    if (targetInViewRadius.Length > 0)
    {
        target = targetInViewRadius[0].transform;
    }
    return target;
}

// 적의 시야 반경과 공격 거리를 디버깅 해보기 위해서 그려줍니다.
private void OnDrawGizmos()
{
    Gizmos.color = Color.red;
    Gizmos.DrawWireSphere(transform.position, viewRadius);

    Gizmos.color = Color.green;
    Gizmos.DrawWireSphere(transform.position, attackRange);
}

 




적의 시야 반경과 공격 거리를 각각 빨강색과 초록색으로 그리고 있는 것을 확인할 수 있습니다.


Physics.OverlapSphere()를 사용하여 특정 Object가 특정 반경내에 있는지를 체크합니다.
적이 있다고 판단이 되었을 때 공격 거리 안에 있는지를 검사하도록 합니다.


이건 왜 이렇게 하냐면, 캐릭터마다 적이 있다고 판단하는 거리는 동일하지만 공격이 가능한지는 다른 이슈라는 것입니다. 예를 들어 근접 공격 유닛이 있다면 적이 있다고 판단은 했지만 공격은 할 수 없으므로 적에게 이동하여 공격을 해야할 것이고, 원거리 공격 유닛이라면 적이 있다고 판단되었을 때 바로 화살 등을 쏴서 공격할 수 있을 것이기 때문입니다.

 

여기서는 캐릭터의 시야 거리는 동일하다고 보는 것입니다. 대신 공격 거리만 차이가 있다고 생각하고 프로그래밍을 하는 것이지요. 원거리 유닛은 바로 attackState로 transform되어 공격 상태로 이전합니다. 반면 근거리 유닛은 moveState로 transform되어 이동 상태로 전이되는 것입니다.

 

 


MoveState_New.cs

public class MoveState_New : State_New<EnemyController_New>
{
    private Animator animator;
    private CharacterController controller;
    private NavMeshAgnet agent;

    private int hashMove = Animator.StringToHash("Move");
    private int hashMoveSpeed = Animator.StringToHash("MoveSpeed");

    public override void OnInitialized()
    {
        animator = context.GetComponent<Animator>();
        controller = context.GetComponent<CharacterController>();
        agent= context.GetComponent<NavMeshAgnet>();
    }


    public override void OnEnter()
    {
        agent?.SetDestination(context.target.position);
        animator?.SetBool(hashMove, true);
    }

    public override void Update(float deltaTime)
    {
        Transform enemy = context.SearchEnemy(); // 적에게 지속 접근
        if (enemy) // 적이 지속 존재한다면
        {
            agent.SetDestination(context.target.position);
            if (agent.remainingDistance > agent.stoppingDistance) // 해당 거리만큼 지속 이동
            {
                controller.Move(agnet.velocity * deltaTime);
                animator.SeFloat(hashMoveSpeed, agnet.velocity.magnitude / agent.speed, 1f, deltaTime);
            }
        }

        if (!enemy && agent.remainingDistance <= agnet.stoppingDistance) // 적이 시야에서 벗어났다면
        {
            stateMachine.ChangeState<IdleState_New>(); // IdleState로 전환
        }
    }

    public override void OnExit()
    {
        animator?.SetBool(hashMove, false);
        animator?.SetFloat(hashMoveSpeed, 0f);
        agent.ResetPath(); // 길찾기 더이상 하지 않도록 초기화
    }
}


AttackState_New도 비슷한 루틴으로 구현되겠죠.. 하지만 오히려 지속 이동이 아니라 공격을 하는 애니메이션이 주가 되므로 Animation 처리가 주 업무가 됩니다. MoveState 보다 구현할 내용이 간단하다는 것입니다 ^^

 



AttackState_New.cs

public class AttackState_New : State_New<EnemyController_New>
{
    private Animator animator;
    private int hashAttack = Animator.StringToHash("Attack");
    public override void OnInitialized()
    {
        animator = context.GetComponent<Animator>();
    }

    public override void OnEnter()
    {
        if (context.IsAvailableAttack)
        {
            animator?.SetTrigger(hashAttack);
        }
        else
        {
            stateMachine.ChangeState<IdleState_New>();
        }
    }

    public override void Update(float deltaTime)
    {
    }
}




실행을 해보면 적이 시야에 인지가 되었을 때 이동해 오는 것을 확인할 수 있고, 공격거리 내에 진입하게 되면 공격 애니메이션이 구동되는 것을 확인할 수 있습니다.

여기서 한가지 문제점은 AttackState 상태에서 다시 IdleState로 전환해주는 것이 필요한데 이를 위해서는 스크립트를 하나 추가로 작성해 주어야 합니다.

 


EndOfAttackStateMachineBehavior.cs

public class EndOfAttackStateMachineBehavior : StateMachineBehaviour
{
    public override void OnStateExit(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
    {
        animator.GetCompoent<EnemyController_New>()?.StateMachine.ChangeState<IdleState_New>();
    }
}


animator와 자신이 구현한 FSM을 연동할 때 정보가 한쪽 방향으로만 흐르도록 해야한다는 것에 주의해야 합니다.
요구사항에 의해 이렇게도 저렇게도 구현되어야 한다면 로직이 꼬이게 되는 경우가 많고 버그도 많이 발생할 수 있기 때문이지요.




아래 내용은 교육과 무관한 미션 제출 관련 내용이에요. ㅎㅎ
지금까지 진행해보니 미션 제출할 때 불편한 점이 몇개 있습니다.
패캠에서 보실지는 모르지만 작성해 둘께요. 보신다면 개선해주시면 좋을 듯합니다.

1. 학습통계 - 이게 하나도 맞질 않아요. ㅋㅋ 왜 있는건지.. 2~3시간을 들어도 20, 30분으로 나와있는게 대부분 -_-;
2. 미션2개 - 강의 2개를 듣고 작성하면 되는데, 오늘 어떤 강의를 들었는지 확인할 수 있는게 없어요. 1개를 들었는지 2개를 들었는지..
3. 제출시 - 시작일이 정해져 있는 것이라 회차가 분명한데, 몇회차를 제출하는지 알수가 없어요.

10회차까지 오니 어떤 강의를 언제 했는지 헛갈리기 시작하네요 ㅠ.,ㅜ; 물론 지난 작성한거 보고 확인도 하고, 캡쳐해놓은것 보고 확인 또 재차 확인하고 있지만, 조금만 직관적이면 좋을텐데.. 라는 아쉬움에 남깁니다. 확인하는데도 시간이 걸리니 ㅎㅎ 그래도 많은 내용 공부할 수 있어 너무 좋고 강추합니다.



패스트캠퍼스 - 올인원 패키지 : 유니티 포트폴리오 완성 bit.ly/2R561g0

 

유니티 게임 포트폴리오 완성 올인원 패키지 Online. | 패스트캠퍼스

게임 콘텐츠 프로그래머로 취업하고 싶다면, 포트폴리오 완성은 필수! '디아블로'와 '배틀그라운드' 게임을 따라 만들어 보며, 프로그래머 면접에 나오는 핵심 개념까지 모두 잡아 보세요!

www.fastcampus.co.kr

 

+ Recent posts