[내배캠] 유니티 숙련 팀프로젝트#4

Sungchan Ahn(안성찬)·2024년 11월 6일

내일배움캠프

목록 보기
56/104

상태 변경 로직 Refactoring 진행

AggressiveAnimal.cs
PassiveAnimal.cs
NonattackAnimal.cs
위의 상태 변경을 담당하는 클래스 3개는 Update문 내에서 if 문을 사용하여 상태 변경 조건들을 체크하여 상태를 변경하는 방식으로 작성했다.
Update문 내에는 현재 상태를 체크하여 그 상태에 해당하는 업데이트 메서드를 호출하는 switch문만 남기고 상태 변경 로직은 각 상태의 업데이트 메서드를 만들어 그 안에서 변경 조건을 판단해 상태를 변경하는 방식으로 코드를 개선해보았다.

예시) PassiveAnimal.cs

  • 기존 Update문 코드

if (playerDistance <= data.detectDistance)
{
    if (!isAttacked && IsPlayerInFieldOfView())
    {
        isAlerted = true;
        ChangeState(CreatureState.Alerted);
    }
    else if (isAttacked)
    {
        isAlerted = true;
        isChasing = true;
        if (currentHealth < data.health * 0.1f)
        {
            isEscaping = true;
            isChasing = false;
            ChangeState(CreatureState.Escape);
        }
        else if (playerDistance < data.attackDistance && IsPlayerInFieldOfView())
        {
            ChangeState(CreatureState.Attack);
        }
        else
        {
            NavMeshPath path = new NavMeshPath();
            if (agent.CalculatePath(PlayerManager.Instance.Player.transform.position, path))
            {
                ChangeState(CreatureState.Chase);
            }
        }
    }
}
else
{
    isAlerted = false;
    isChasing = false;
    isEscaping = false;
    isAttacked = false;

    if (creatureState == CreatureState.Wander && agent.remainingDistance < 0.5f)
    {
        ChangeState(CreatureState.Idle);
        wanderRate = Random.Range(data.minWanderWaitTime, data.maxWanderWaitTime);
    }
    else if (Time.time - lastWanderTime > wanderRate)
    {
        lastWanderTime = Time.time;
        ChangeState(CreatureState.Wander);
    }
}
  • 수정 후
protected override void Update()
{
    base.Update();

    switch (creatureState)
    {
        case CreatureState.Idle:
        case CreatureState.Wander:
            IdleWanderUpdate();
            break;
        case CreatureState.Alerted:
            AlertedUpdate();
            break;
        case CreatureState.Chase:
        case CreatureState.Attack:
            CombatUpdate();
            break;
        case CreatureState.Escape:
            EscapeUpdate();
            break;
        case CreatureState.Dead:
            return;
    }

    stateContext.currentState.OnStateUpdate();
}

private void IdleWanderUpdate()
{
    if (creatureState == CreatureState.Wander && agent.remainingDistance < 0.5f)
    {
        ChangeState(CreatureState.Idle);
        wanderRate = Random.Range(data.minWanderWaitTime, data.maxWanderWaitTime);
    }
    else if (Time.time - lastWanderTime > wanderRate)
    {
        lastWanderTime = Time.time;
        ChangeState(CreatureState.Wander);
    }

    if (playerDistance <= data.detectDistance)
    {
        if (!isAttacked && IsPlayerInFieldOfView())
        {
            isAlerted = true;
            ChangeState(CreatureState.Alerted);
            return;
        }
        else if (isAttacked)
        {
            isAlerted = true;
            if (currentHealth < data.health * 0.1f)
            {
                isEscaping = true;
                isChasing = false;
                ChangeState(CreatureState.Escape);
                return;
            }
            else
            {
                isChasing = true;
                ChangeState(CreatureState.Chase);
            }
        }
    }
}

private void AlertedUpdate()
{
    if (currentHealth < data.health * 0.1f)
    {
        isEscaping = true;
        ChangeState(CreatureState.Escape);
        return;
    }
    else
    {
        if (isAttacked)
        {
            isChasing = true;
            ChangeState(CreatureState.Chase);
            return;
        }
    }

    if (playerDistance > data.detectDistance)
    {
        isAlerted = false;
        isChasing = false;
        isEscaping = false;
        isAttacked = false;
        ChangeState(CreatureState.Idle);
    }
}

private void CombatUpdate()
{
    if (currentHealth < data.health * 0.1f)
    {
        isEscaping = true;
        isChasing = false;
        ChangeState(CreatureState.Escape);
        return;
    }
    else
    {
        if (playerDistance < data.attackDistance && IsPlayerInFieldOfView())
        {
            ChangeState(CreatureState.Attack);
            return;
        }
        else
        {
            if (playerDistance <= data.detectDistance)
            {
                if (CanChase())
                {
                    isChasing = true;
                    ChangeState(CreatureState.Chase);
                    return;
                }
                else
                {
                    isChasing = false;
                    isAlerted = false;
                    ChangeState(CreatureState.Wander);
                    return;
                }
            }
            else
            {
                isAlerted = false;
                isChasing = false;
                isEscaping = false;
                isAttacked = false;
                ChangeState(CreatureState.Idle);
            }
        }
    }
}

private void EscapeUpdate()
{
    if (playerDistance > data.detectDistance)
    {
        isAlerted = false;
        isChasing = false;
        isEscaping = false;
        isAttacked = false;
        ChangeState(CreatureState.Idle);
    }
}

기존의 코드는 모든 조건을 한 번에 판단해서 처리하려다보니 예상치 못한 상황에 대한 대처가 쉽지 않고 수정도 쉽지 않다.
수정 한 코드는 현재 상태에서 전환 가능한 상태들에 대한 조건만 판단하기 때문에 조건을 설정하는 데 있어 보다 수월하다. 상태가 전환되는 조건들이 많고 각 상태별로 업데이트 메서드를 만들었기 때문에 코드는 더 길어졌지만 수정은 보다 쉽게 할 수 있게 개선된 것 같다.

profile
게임 개발 기록

0개의 댓글