본문 바로가기

코딩 이야기/유니티

유니티 2D RPG 강좌 #4. Blend Tree 애니메이션


저번 강좌에서 플레이어 스프라이트를 사용해서 간단한 애니메이션을 만들었습니다. 하지만 저번 강좌에서 만든데로 애니메이션을 설정하면 나중에 공격, 스펠 캐스팅, 등 더 많은 애니메이션이 필요할 때 너무 복잡해지게 됩니다. 게임은 물론, 소프트웨어를 만들 때도 제일 중요한 것 중 하나가 바로 최적화인데요, 불필요한 작업들을 최소화해서 게임이나 프로그램이 최대한 가볍게 만드는 겁니다. 유니티에서도 애니메이션 최적화를 위해서 Blend Tree Animation이라는 기능을 제공하는데요, 이 기능을 사용해서 더 발전된(?) 애니메이션을 만들어 봅시다.





유니티 2D RPG 강좌 #3 - 플레이어 애니메이션 적용

유니티 2D RPG 강좌 #2 - 플레이어가 움직이도록 해보자!

유니티 2D RPG 강좌 #1 - 스프라이트 설정하기






저번에 만들었던 애니메이션은 방향키를 때면 다시 아래를 바라보면서 Idle 상태가 되었는데요, 이것 보다는 움직이던 방향을 그대로 보고있으면서 Idle 상태가 되는게 더 좋을것 같아서 Blend Tree Animation를 적용시키면서 같이 만들어 보겠습니다. 그러기 위해서는 4개의 애니메이션이 더 필요합니다. 저번에 만들었던 Idle 애니메이션과 비슷하게 상하좌우를 바라보고 있는 Idle 상태의 애니메이션을 만들겁니다. Player_FaceUp, Player_FaceDown, Player_FaceLeft, Player_FaceRight이란 이름으로 4개를 만든 후 Idle 애니메이션과 같이 각 방향을 바라보는 스프라이트 1개만 애니메이션에 적용시켜 주세요. 저번에 어떻게 했는지 기억하시죠?



Animator 패널을 열면 방금 만든 4개의 애니메이션이 추가되어 있을겁니다. Blend Tree Animation은 이 애니메이션들을 직접 연결할 필요가 없으니 Player_Idle을 제외한 모든 애니메이션 블럭들을 제거해 줍시다. 



파라미터도 3개를 더 만들어 줄겁니다. 일단 2개는 저번과 같이 Float로 만들건데요, LastMoveX, LastMoveY라고 이름을 설정해 주세요. 



다음은 bool 입니다. Bool 변수는 참, 혹은 거짓으로만 이루어져 있는데요, 무언가를 하고 있는지 확인할 때 주로 사용합니다. 저희같은 경우는 움직이고 있는지 확인할 때 사용할것입니다. Bool 형식의 파라미트를 만든 후 이름은 PlayerMoving으로 설정해 줍시다.






이제 Blend Tree 애니메이션을 만들어 봅시다. 빈 공간을 우클릭한 뒤 "Create State > From New Blend Tree"를 클릭해 줍시다. 이름은 "Player Movement"로 설정해 주세요.



일반 애니메이션 블럭과 비슷하게 생겼죠? Player Movement 블럭을 두번 클릭해 봅시다.



그러면 Blend Tree라고 적혀있는 블럭이 보이고, 오른쪽 Inspector 패널에서는 여러 설정을 할 수 있습니다. 우리는 상하, 그리고 좌우로 움직여야 하기 때문에 2개의 방향이 있어야 하겠죠. Blend Type를 2D Simple Directional로 설정한 뒤 Parameters를 MoveX, MoveY로 설정해 줍시다. 



기본 설정이 끝났으면 상하좌우로 움직이는 애니메이션들을 추가해야겠죠. Motion 부분의 "+" 버튼을 누른 후 "Add Motion Field"를 클릭해 줍시다. 



.




그럼 모션이 생성되는데요, 첫번째 칸을 클릭해 주세요.



우리가 만든 애니메이션 리스트가 뜹니다. 먼저 아래로 움직이는 애니메이션을 설정해 볼까요? Player_Move_Down을 클릭해 주세요.



이런 방법으로 위, 왼쪽, 그리고 오른 쪽 모션도 만들어 줍시다. 그 다음 언제 애니메이션이 실행되어야 하는지도 설정해 줘야겠죠. 저번에 설정했었던 값들을 기억하시나요? 아래로 내려가는건 Y가 -1. 올라가는건 Y가 +1, 왼쪽은 X가 -1, 그리고 오른쪽은 X가 +1입니다. 알맞게 값을 지정해 주세요.



설정이 완료되면 Animator 패널에 이런 식으로 확장이 되어있을겁니다. 



Player Movement 애니메이션 블럭을 기본으로 설정한 뒤 게임을 테스트 해 봅시다. 방향키를 누르면 그 방향으로 잘 움직입니다. 하지만 방향키를 때도 애니메이션이 계속 플레이되죠? 아직 Idle 상태를 연결하지 않아서 그렇습니다. 이번에는 Idle 상태의 Blend Tree 애니메이션을 만들어 봅시다. 






"Idle_Face"라는 이름의 Blend Tree 애니메이션을 새로 만들어 주신 후 Blend Type을 2D Simple Directional, 그리고 Parameters를 LastMoveX, LastMoveY로 설정해 주세요. 



아까와 같은 방식으로 상하좌우를 바라보고 있는 Idle 애니메이션들을 연결시켜 줍시다. 



이제 트랜지션으로 두 애니메이션을 연결시켜 줘야겠죠. Player Movement에서 IdleFace로 넘어가는 트랜지션을 만든 후 조건을 PlayerMoving이 False일때로 설정해 줍시다. 만약 플레이어가 움직이지 않고 있다면 Idle 상태로 넘어가야겠죠. 



이번에는 IdleFace에서 PlayerMovement로 넘어오는 트랜지션을 만들고 조건을 PlayerMoving이 True일때로 설정해 주세요. 플레이어가 움직이고 있으면 PlayerMovement 애니메이션이 실행될 겁니다. 



저번 강좌에서 트랜지션 사이에 딜레이가 있었던걸 기억하시나요? 이번에도 역시 딜레이가 있으면 안되니 저번과 같이 위 사진처럼 설정해 주세요. 


애니메이션 설정은 완료되었는데요, 마지막 코딩 작업이 남아있습니다. 아까 만들었던 3개의 새로은 파라미터들에게 값을 지정해 줘야겠죠. PlayerController.cs 스크립트에 아래 코드를 추가해 줍시다. 


using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PlayerController : MonoBehaviour {

        public float moveSpeed; // 플레이어 속도

        private Animator anim; // Animator를 불러오기 위한 변수

        bool playerMoving; // 플레이어가 움직이는지 확인
        Vector2 lastMove; // 마지막 움직임이 어느 방향이었는지 확인하기 위한 변수

	// Use this for initialization
	void Start () {

        // anim 변수 선언
        anim = GetComponent<animator>();
	}
	
	// Update is called once per frame
        void Update () {

        playerMoving = false; // 따로 방향키를 누르지 않으면 플레이어는 움직이지 않음으로 설정 

        // 왼쪽, 오른쪽으로 움직이기
        if (Input.GetAxisRaw("Horizontal") > 0f || Input.GetAxisRaw("Horizontal") < 0f) { 
            transform.Translate(new Vector3(Input.GetAxisRaw("Horizontal") * moveSpeed * Time.deltaTime, 0f, 0f));
            playerMoving = true; // 플레이어가 움직이고 있다
            lastMove = new Vector2(Input.GetAxisRaw("Horizontal"), 0f); // lastMove의 X 값을 Horizontal로 설정
        }

        // 위, 아래로 움직이기
        if (Input.GetAxisRaw("Vertical") > 0f || Input.GetAxisRaw("Vertical") < 0f) { 
            transform.Translate(new Vector3(0f, Input.GetAxisRaw("Vertical") * moveSpeed * Time.deltaTime, 0f));
            playerMoving = true; // 플레이어가 움직이고 있다
            lastMove = new Vector2(0f, Input.GetAxisRaw("Vertical")); // lastMove의 Y 값을 Vertical로 설정
        }

        // 에니메이션 MoveX, MoveY 
        anim.SetFloat("MoveX", Input.GetAxisRaw("Horizontal"));
        anim.SetFloat("MoveY", Input.GetAxisRaw("Vertical"));
        anim.SetBool("PlayerMoving", playerMoving); // 애니메이션 PlayerMoving을 playerMoving과 같도록 설정
        anim.SetFloat("LastMoveX", lastMove.x); // LastMoveX를 lastMove의 X값과 같게 설정
        anim.SetFloat("LastMoveY", lastMove.y); // LastMoveY를 lastMove의 Y값과 같게 설정
	}
}

먼저 플레이어가 움직이고 잇는지 확인하기 위한 playerMoving 변수와, 플레이어가 마지막으로 바라보고 있었던 방향을 확인하기 위한 lastMove 변수를 만들었습니다. Vector3 변수가 xyz 세개의 값을 담을 수 있다면 Vector2 변수는 xy 두개의 값만 담을 수 있습니다. 지금 우리는 x값과 y값만 필요하기 때문에 굳이 Float 변수 두개를 만드는 것 보다는 Vector2 변수 하나로 처리하는게 훨씬 더 간단합니다. 


Update 함수 처음에 "playerMoving = false"라고 선언했는데요, C# 스크립트는 위에서부터 아래쪽으로 차례대로 코드를 실행시키기 때문에 제일 위쪽에 적혀있는 코드가 제일 먼저 실행됩니다. 즉, Update 함수가 실행되면 플레이어는 움직이지 않는 것으로 처리됩니다. 하지만 만약 플레이어가 방향키를 눌렀다면, 그 다음 줄에서 playerMoving이 True가 됩니다. 그럼 아까 선언되었던 false 값이 true로 바뀌는거죠. 굳이 이렇게 안하고 또다른 if문을 사용해도 되겠지만 이런 방법으로 사용되는 코드를 최소화시키면 게임이 완성될 쯤에는 더 최적화 되어있겠죠? 티끌모아 태산입니다. 


그 다음은 Vector2! 아까 얘기했듯이 플레이어가 마지막으로 바라보았던 방향을 확인하기 위해서 만든 변수인데요, X값(Horizontal)과 Y값(Vertical)을 위한 Float 변수 2개를 만드는 것 보다는 Vector2 변수 하나로 처리하는게 훨씬 더 좋습니다. Vector2(x값, y값) 형식으로 되어있으니 참고하시길 바랍니다.


anim.SetFloat는 저번에 설명했었죠? 비슷하게 anim.SetBool은 Bool 형식의 파라미터에 값을 지정하기 위한 코드입니다. 그 다음,  LastMoveX와 LastMoveY의 값을 지정해 주는 부분을 잘 살펴보면 lastMove.x, lastMove.y가 보이실겁니다. 전에는 보지 못했던 형식이죠? 아까 설명했듯이 Vector2는 Vector2(x값, y값) 형식으로 되어잇는데요, lastMove.x는 Vector2에서 x값을, lastMove.y는 Vector2에서 y값을 불러옵니다. Vector3에서는 vector3.z라고 입력해서 z값을 불러오는 것도 가능합니다. 



코드를 다 입력하셨으면 이제 테스트를 해봅시다. 플레이 버튼을 누른 후 방향키를 누르면 움직이다가 같은 방향을 보고 멈추게 됩니다. 


드디어 애니메이션 관련 강좌가 끝났네요 ㅎㅎ 다음 강좌에서는 카메라를 설정해 보도록 하겠습니다. 





유니티 2D RPG 강좌 #3 - 플레이어 애니메이션 적용

유니티 2D RPG 강좌 #2 - 플레이어가 움직이도록 해보자!

유니티 2D RPG 강좌 #1 - 스프라이트 설정하기