유니티_일기/3D_RPG!

3D RPG 만들기! (5) 공격 만들기 - 레이어마스크, 애니메이션 레이어

휴대용치즈 2023. 8. 9. 20:36

이동관련이라 (3)에 쓰려다가 어찌저찌...하다가 (4)를 건너 (5)에 쓰게됐다.

 

유니티3D에서 몸의 일부분만 동작을 하거나, 여러 동작을 가중치를 둬서 섞어 사용하는 등 여러가지 방법이 있다.

2D에서도 사용하는 경우도 있고, 이걸로 더 자연스러운 애니메이션을 연출할 수 있다.

 

일반 이동

 

공격하면서 이동

이동하면서 공격키를 누르면 원하는 부위만 공격하는 동작을 실행할 수 있다.

그리고 동작이 끝나고 원래 동작으로 넘어오는 과정도 가중치를 통해 자연스럽게 넘어올 수 있다.

 

1. 레이어마스크

2. IK, M 

https://docs.unity3d.com/kr/2021.1/Manual/AnimationLayers.html

 

애니메이션 레이어(Animation Layers) - Unity 매뉴얼

Unity는 서로 다른 신체 부위의 복잡한 상태 머신을 관리하기 위해 Animation Layers 를 사용합니다. 예를 들면, 하체 레이어는 뛰거나 걷는 것을 관리하고, 상체 레이어는 오브젝트를 던지거나 사격하

docs.unity3d.com

 

 

처음 유니티짱을 import하면 애니메이션에 BaseLayer와 face라는 2개의 애니메이션이 있다.

(Attack는 만들어 넣은거)

 

BaseLayer에는 움직임들이 있고 face는 얼굴표정만 있다.

위처럼 애니메이션이 2개이상이라면 우선순위는 이렇게 결정된다.

1. 가중치가 더 높은 애니메이션을 실행한다.

2. 가중치가 같다면 더 아래에 있는 애니메이션을 실행한다.

 

이 가중치를 설정하려면 애니메이션 옆에 톱니바퀴를 누르면 된다

이렇게 나타나는데 모든 애니메이션을 끌수는 없기때문에 가장위의 애니메이션은 가중치가 1로 고정된다.

 그리고 Mask와 Blending등 설정도 있다.

 

 

Mask

아바타 마스크를 통해 신체의 일부분만 애니메이션을 적용할 수 있다.

아바타 마스크는 우클릭 - Create - Avatar Mask 로 생성가능하다.

 

이렇게 만든 아바타 마스크는 인스펙터는 아래와 같이 표시된다.

Humanoid에 맞춰진 오브젝트는 각 부위를 지정가능하다.

각 부위를 클릭하면 켜고 끌 수 있다.

Humanoid가 아니라면 각 Transform을 선택하는 방법도 있다.

 

 

IK?

(자세한 설명은 전공책에서 보는게 맞다고 본다.)

위 Humanoid에서 초록색은 활성화가 되어있는 상태고, 빨간색이 꺼진상태

그리고 IK의 뜻은 역운동학이다.

 

순운동학은 첫 기준 뼈대를 시작으로 목표지점까지 계산해나가는 방법이고,

역운동학은 목표지점부터 첫기준 뼈대인 반대로 계산해나가는 방법이다.

 

캐릭터가 어떤 물체를 든다고 하면 가정하자

위 좌표에서 원점이 몸통이고 Θ3이 물건이 있는 위치며, 물건을 들어야 하는 상태다.

 

원점인 몸통에서 물건을향해 뻗어나가면서 손이  Θ3의 물건을 잡는것보다,

손이 Θ3의 물건을 잡았다는 가정하에 원점인 몸통의 위치를 계산하는것이 더 자연스럽게 결과가 나온다고한다.

 

그래픽이나 로봇공학에서 많이 쓰는 방법이고, 유니티에서도 IK로 계산한다.

 

대학 3학년때 컴퓨터그래픽스란 학과에서 배웠는데... 자세한 설명이 좀 복잡하다.

잘 기억 안나서 설명하기 힘든것도 있고...

 

https://docs.unity3d.com/kr/560/Manual/InverseKinematics.html

 

역운동학(IK) - Unity 매뉴얼

대부분의 애니메이션은 스켈레톤의 조인트 각도를 미리 정해진 값으로 회전하여 만듭니다. 자식 조인트의 포지션은 부모의 회전에 따라 변하므로 조인트 체인의 끝 점은 체인에 포함된 각 조인

docs.unity3d.com

 

 

Mask 적용

아무튼 위에서 만든 Mask를 적용해보자

설정창에 Mask에 만든 마스크를 넣으면 된다.

 

나는 공격모션이 상체전부에 적용하도록 만들었다.

 

이 마스크를 넣으려면 드래그 앤 드롭으로는 안되고 직접 골라야 들어가진다.

나한테만 있는 버그일지도 모르는데 드래그 앤 드롭으로 하면 창이 자꾸 꺼지더라ㅜㅜ

이렇게 마스크를 적용하고나면 아래처럼 M이 붙는다

 

 

 

참고로 _faceOnly라는 마스크는 유니티짱에 표정을 관리하기위해 기본적으로 있는데,

여기 클릭해보면 Humanoid에 설정이 없다.

얼굴표정만 바꾸는거라 여기에 숨어있다.

 

 

 

공격 만들기

공격 애니메이션은 에셋스토어에서 무료제품중 하나를 주어왔다.

가져온게 하도 많아서 뭐였는진 기억이 안난다.ㅜㅜ

 

애니메이터는 이렇게 설정했다.

공격을 시도하면 좌클릭을 우선으로 날리고, 일정 시간 후 다시 클릭하면 우클릭을 공격하도록 만들었다.

Transiton Duration은 자연스럽게 넘어가도록 하려다보니 0.4가 적당했다.

 

 

스크립트

    // 공격
    private IEnumerator Punch()
    {
        if (isLeftPunch || isRightPunch) yield break;  // 공격이 아닌경우 왼손 공격 실행

        bool giveDamage = false;
        isLeftPunch = true;

        // 2번째 레이어의 애니메이션 실행
        animator.Play("LeftPunch", 2, 0);   
        animator.SetLayerWeight(2, 1);

        float punchTime = 0f;

        while (true)
        {
            // 죽거나 맞거나 등 공격불가상태
            if ((int)curState >= 5)
            {
                animator.SetLayerWeight(2, 0);
                break;
            }

            punchTime += Time.deltaTime;
            if (punchTime > 0.4f && !giveDamage)
            {
                //0.4초때 적을 공격
                giveDamage = true;
                Hit(unityChan);
            }
            else if (punchTime > 0.5f && Input.GetMouseButtonDown(0))
            {
                // 0.5초부터 오른손 공격 가능
                isRightPunch = true;
                punchTime = 0;
                giveDamage = false;
            }

            //왼손 공격중 한번 더 클릭시 오른손 공격으로 변경
            if (animator.GetCurrentAnimatorStateInfo(2).normalizedTime > 0.55f && !animator.IsInTransition(2)) 
            {
                if (isLeftPunch && isRightPunch)        // 왼손 공격중 오른손 공격이 실행되면
                {
                    isLeftPunch = false;        // 왼손 공격은 종료
                    temp_punch = 1;     // 가중치 계산을 위한 변수 
                    animator.SetLayerWeight(2, temp_punch);
                    animator.SetBool("IsRightPunch", isRightPunch);
                }
                else if (temp_punch >= 0)        // punch 애니메이션의 가중치를 0까지 낮춤
                {
                    temp_punch -= Time.deltaTime * 3;
                    animator.SetLayerWeight(2, temp_punch);
                }
                else
                {
                    break;
                }

            }
            yield return null;
        }

        //초기화
        animator.SetLayerWeight(2, 0);
        temp_punch = 1;
        isLeftPunch = false;
        isRightPunch = false;
        animator.SetBool("IsRightPunch", false);

        yield return null;
    }

 

 

왼손공격 후 0.5초후에 오른손공격이 가능하도록 했고, 가중치값을 deltatime*3만큼 줄어들게 해서 자연스럽게 만들었다.

애니메이션의 레이어를 지정해서 다룰 수 있기때문에 공격애니메이션만 실행하고 가중치를 변경했다.

 

 

그리고 이 경우 상태를 공격상태로 바꿔버리면, 다리는 상태가 고정되어버린다.

그래서 공격의 경우는 ChageState가 아닌 StartCoroutine을 써서 구현했다.

 

    // 앉기
    public IEnumerator SitDown()
    {
        while (true)
        {
            if (Input.GetMouseButtonDown(0))
            {
                StartCoroutine("Punch");
            }

            ...
            
            yield return null;
        }
    }