Posted in: Unity, 프로젝트 When the Spring comes

24. 플레이어 애니메이션 추가 & 수정

기획자님이 스코페이아가 맞았을 때 스프라이트를 주셨습니다. 사실 받은지는 꽤 되었는데 다른 일이 많아서 최근에 적용했습니다.

8방향 2D 게임에서는 애니메이션 스테이트가 악랄하게 많아집니다. 3D 게임 오브젝트처럼 블렌더 트리를 사용할 수도 없는 노릇이죠.

저 상태에서 Hit 애니메이션을 추가하려면 지옥이 될 것 같아서 코드로 작업하기로 하였습니다. 근접 공격 몬스터도 코드로 작동되게 만들었죠.

우선 트랜지션을 모두 삭제하고 보기 좋게 분류별로 모았습니다. 가장 위에있는 분류는 필요 없을거라 생각되지만 바로 삭제하기가 애매하여 일단 나뒀습니다.

Idle, MovingReady, Moving, Attack1타, Attack2타, Hit 애니메이션들 8방향입니다. Dash, Dodge, Die는 아직 스프라이트가 없습니다. 다음에 만들어지겠지요..

// Idle
const string Idle_UpLeft = "Player_IdleUpLeft";
const string Idle_Up = "Player_IdleUp";
const string Idle_UpRight = "Player_IdleUpRight";
const string Idle_Left = "Player_IdleLeft";
const string Idle_Right = "Player_IdleRight";
const string Idle_DownLeft = "Player_IdleDownLeft";
const string Idle_Down = "Player_IdleDown";
const string Idle_DownRight = "Player_IdleDownRight";

// MovingReady
const string MovingReady_UpLeft = "Player_MovingUpLeftReady";
const string MovingReady_Up = "Player_MovingUpReady";
const string MovingReady_UpRight = "Player_MovingUpRightReady";
const string MovingReady_Left = "Player_MovingLeftReady";
const string MovingReady_Right = "Player_MovingRightReady";
const string MovingReady_DownLeft = "Player_MovingDownLeftReady";
const string MovingReady_Down = "Player_MovingDownReady";
const string MovingReady_DownRight = "Player_MovingDownRightReady";

// Moving
const string Moving_UpLeft = "Player_MovingUpLeft";
const string Moving_Up = "Player_MovingUp";
const string Moving_UpRight = "Player_MovingUpRight";
const string Moving_Left = "Player_MovingLeft";
const string Moving_Right = "Player_MovingRight";
const string Moving_DownLeft = "Player_MovingDownLeft";
const string Moving_Down = "Player_MovingDown";
const string Moving_DownRight = "Player_MovingDownRight";

// Hit
const string Hit_UpLeft = "HitUpLeft";
const string Hit_Up = "HitUp";
const string Hit_UpRight = "HitUpRight";
const string Hit_Left = "HitLeft";
const string Hit_Right = "HitRight";
const string Hit_DownLeft = "HitDownLeft";
const string Hit_Down = "HitDown";
const string Hit_DownRight = "HitDownRight";

// BasicAttack 1
const string Attack1_UpLeft = "AttackUpLeft_1";
const string Attack1_Up = "AttackUp_1";
const string Attack1_UpRight = "AttackUpRight_1";
const string Attack1_Left = "AttackLeft_1";
const string Attack1_Right = "AttackRight_1";
const string Attack1_DownLeft = "AttackDownLeft_1";
const string Attack1_Down = "AttackDown_1";
const string Attack1_DownRight = "AttackDownRight_1";

// BasicAttack 2
const string Attack2_UpLeft = "AttackUpLeft_2";
const string Attack2_Up = "AttackUp_2";
const string Attack2_UpRight = "AttackUpRight_2";
const string Attack2_Left = "AttackLeft_2";
const string Attack2_Right = "AttackRight_2";
const string Attack2_DownLeft = "AttackDownLeft_2";
const string Attack2_Down = "AttackDown_2";
const string Attack2_DownRight = "AttackDownRight_2";

void ChangeAnimationState(string newState)
{
    if(currentState == newState) return;

    animator.Play(newState);

    currentState = newState;
}

근접 공격 몬스터 만들었을 때와 비슷합니다. const로 선언한 string 변수들은 모두 애니메이션 상태와 이름이 동일합니다.

ChangeAnimationState 함수에 의해 출력되는 애니메이션이 결정됩니다. 만약 실행되고 있는 애니메이션과 같은 상태가 매개변수로 들어온다면 함수는 종료됩니다.

private void Update()
{
    /*
        * Idle 상태일 때
        */
    if(!GameManager.GetInstance().playerIsMove &&
        !GameManager.GetInstance().playerIsHit  &&
        !GameManager.GetInstance().playerIsBasicAttack)
        TestIdleAnime();

    /*
        * 움직일 때
        */
    if(GameManager.GetInstance().playerIsMove &&
        !GameManager.GetInstance().playerIsHit &&
        !GameManager.GetInstance().playerIsBasicAttack)
        TestMovingAnime();

    /*
        * 피해를 입을 때
        */
    if(GameManager.GetInstance().playerIsHit)
        TestHitAnime();

    /*
        * 기본 근접 공격 중일 때
        */
    if(GameManager.GetInstance().playerIsBasicAttack &&
        GameManager.GetInstance().playerIsHit == false)
        TestAttackAnime();
}

조건에 따라 그에 맞는 애니메이션을 출력합니다. 조건은 게임매니저를 통해 제어하였습니다. 게임매니저는 게임 세상에 하나만 존재하므로 이런 상황에 사용하기에 좋습니다.

public void TestMovingAnime()
{

    // UpLeft
    if(GameManager.GetInstance().playerDirection == new Vector3(-1.14f, 0, 1.14f))
    {
        if(doNotMovingStateChange == false)
        {
            ChangeAnimationState(MovingReady_UpLeft);
        }
        if(animator.GetCurrentAnimatorStateInfo(0).IsName("Player_MovingUpLeftReady") &&
            animator.GetCurrentAnimatorStateInfo(0).normalizedTime >= 1.0f)
        {
            doNotMovingStateChange = true;

            checkDirection = new Vector3(-1.14f, 0, 1.14f);

            ChangeAnimationState(Moving_UpLeft);
        }
    }

    // Up
    if(GameManager.GetInstance().playerDirection == new Vector3(0, 0, 2.0f))
    {
        if(doNotMovingStateChange == false)
        {
            ChangeAnimationState(MovingReady_Up);
        }
        if(animator.GetCurrentAnimatorStateInfo(0).IsName("Player_MovingUpReady") &&
            animator.GetCurrentAnimatorStateInfo(0).normalizedTime >= 1.0f)
        {
            doNotMovingStateChange = true;

            checkDirection = new Vector3(0, 0, 2.0f);

            ChangeAnimationState(Moving_Up);
        }
    }

    // UpRight
    if(GameManager.GetInstance().playerDirection == new Vector3(1.14f, 0, 1.14f))
    {
        if(doNotMovingStateChange == false)
        {
            ChangeAnimationState(MovingReady_UpRight);
        }
        if(animator.GetCurrentAnimatorStateInfo(0).IsName("Player_MovingUpRightReady") &&
            animator.GetCurrentAnimatorStateInfo(0).normalizedTime >= 1.0f)
        {
            doNotMovingStateChange = true;

            checkDirection = new Vector3(1.14f, 0, 1.14f);

            ChangeAnimationState(Moving_UpRight);
        }
    }

    // Left
    if(GameManager.GetInstance().playerDirection == new Vector3(-2.0f, 0, 0))
    {
        if(doNotMovingStateChange == false)
        {
            ChangeAnimationState(MovingReady_Left);
        }
        if(animator.GetCurrentAnimatorStateInfo(0).IsName("Player_MovingLeftReady") &&
            animator.GetCurrentAnimatorStateInfo(0).normalizedTime >= 1.0f)
        {
            doNotMovingStateChange = true;

            checkDirection = new Vector3(-2.0f, 0, 0);

            ChangeAnimationState(Moving_Left);
        }
    }

    // Right
    if(GameManager.GetInstance().playerDirection == new Vector3(2.0f, 0, 0))
    {
        if(doNotMovingStateChange == false)
        {
            ChangeAnimationState(MovingReady_Right);
        }
        if(animator.GetCurrentAnimatorStateInfo(0).IsName("Player_MovingRightReady") &&
            animator.GetCurrentAnimatorStateInfo(0).normalizedTime >= 1.0f)
        {
            doNotMovingStateChange = true;

            checkDirection = new Vector3(2.0f, 0, 0);

            ChangeAnimationState(Moving_Right);
        }
    }

    // DownLeft
    if(GameManager.GetInstance().playerDirection == new Vector3(-1.14f, 0, -1.14f))
    {
        if(doNotMovingStateChange == false)
        {
            ChangeAnimationState(MovingReady_DownLeft);
        }
        if(animator.GetCurrentAnimatorStateInfo(0).IsName("Player_MovingDownLeftReady") &&
            animator.GetCurrentAnimatorStateInfo(0).normalizedTime >= 1.0f)
        {
            doNotMovingStateChange = true;

            checkDirection = new Vector3(-1.14f, 0, -1.14f);

            ChangeAnimationState(Moving_DownLeft);
        }
    }

    // Down
    if(GameManager.GetInstance().playerDirection == new Vector3(0, 0, -2.0f))
    {
        if(doNotMovingStateChange == false)
        {
            ChangeAnimationState(MovingReady_Down);
        }
        if(animator.GetCurrentAnimatorStateInfo(0).IsName("Player_MovingDownReady") &&
            animator.GetCurrentAnimatorStateInfo(0).normalizedTime >= 1.0f)
        {
            doNotMovingStateChange = true;

            checkDirection = new Vector3(0, 0, -2.0f);

            ChangeAnimationState(Moving_Down);
        }
    }

    // DownRight
    if(GameManager.GetInstance().playerDirection == new Vector3(1.14f, 0, -1.14f))
    {
        if(doNotMovingStateChange == false)
        {
            ChangeAnimationState(MovingReady_DownRight);
        }
        if(animator.GetCurrentAnimatorStateInfo(0).IsName("Player_MovingDownRightReady") &&
            animator.GetCurrentAnimatorStateInfo(0).normalizedTime >= 1.0f)
        {
            doNotMovingStateChange = true;

            checkDirection = new Vector3(1.14f, 0, -1.14f);

            ChangeAnimationState(Moving_DownRight);
        }
    }


    // 이동 방향이 달라지면
    // 다른 애니메이션 출력되게 하자
    //////////////////////////////
    if(checkDirection != GameManager.GetInstance().playerDirection)
        doNotMovingStateChange = false;
}

애니메이션을 출력하는 함수는 보통 비슷하므로 제일 복잡한 경우인 Moving 애니메이션을 보겠습니다.

우선 플레이어가 움직이는 조작에 따라 8방향에 해당하는 각 방향을 받아오고 준비 동작 애니메이션을 출력한 후 이동 애니메이션을 출력합니다.

중요한 것은 준비 동작 애니메이션이 반복해서 계속 출력되지 않게 하는 것이므로 bool 변수로 확실히 제어하는 게 중요합니다.

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 항목은 *(으)로 표시합니다