오늘 한 일
- 알고리즘 문제 풀기 (LeetCode 786 문제)
- 스파르타 강의 진도 나가기 완료!
- 개인 프로젝트 진행하기
일단 모르는 내용은 살짝 미뤄둬고 당장 해야할 프로젝트를 진행하면서 캐릭터가 바라보는 방향을 다르게 해보는 코드를 짜보려한다.
목표
이번에 이 친구가 마우스를 따라 상하좌우를 바라보게 하고, 움직이는 모습도 그에 맞춰서 코드를 짜볼 생각이다.
해당 에셋을 다운 받을 수 있는 곳(itch.io)
스프라이트 시트
- 해당 에셋 캐릭터 이미지를 보면 이와 같이 되어있을 것이다.
- 사용하기 전에 전처리 과정을 해보도록 하자!
Import Settings
- 해당 스프라이트 시트를 가지고 와서 누르면 Inspector에서 이와 같은 설정이 나올텐데
여기서 Sprite Mode를 Multiple로 바꾸고, Filter Mode를 Point로 해주자
필터 모드를 바꾸지 않으면 이미지가 흐릿하게 보인다.
Sprite Editor
- 그 다음에 Sprite Editor로 들어가면 이와 같은 창이 나오게 될텐데
- Slice에서 아래와 같은 설정을 만진 후 이미지들이 격자 무늬 안에 잘 들어가는지 확인하고 Slice를 해주자
Type : Grid By Cell Sice && Pixel : Size 16, 16
- Slice를 완료하면 이와 같이나온다.
- 자르기를 완료한 후에는 오른쪽 아래창에 작업하기 편한 이름을 붙여주고, Position값을 맞춰가면서 캐릭터의 스프라이트가 어긋나지 않도록 설정해주자.
- 상단바 오른쪽 부분의 Apply를 눌러주면 작업한 것이 Asset에 반영된다!
- 작업이 완료된 스프라이트 시트의 모습
! 주의할 점
- Apply는 작업의 Save와 같은 역할을 해주니까. Apply를 누르는 것을 잊지 말자
- 반드시 이미지 사용 이전에 (렌더러에 붙이기, Animation Clip 활용) 이 작업을 완료하자
벡터 계산하기
마우스의 위치를 월드 좌표로 변환 후 캐릭터에서부터 마우스 좌표까지의 벡터를 구한다.
주요 코드
Vector2 newAim = value.Get<Vector2>(); Vector2 worldPos = camera.ScreenToWorldPoint(newAim); newAim = (worldPos - (Vector2)transform.position).normalized;
왜 벡터의 값을 구할 때 왜 순서가 중요한 건가요?
설명해주자면 좌표 간의 거리만 구한다면 '스칼라'라고 하고,
그 스칼라에서 방향을 추가한다면 '벡터'라고 한다.
(그 좌표 간의 거리를 크기라고 부릅니다)
벡터는 단순 거리 뿐만 아닌 '방향'이라는 요소도 존재하기 때문에
A에서 B의 벡터와 B에서 A의 벡터는 전혀 다른 것이다.
이해가 안된다면 아래의 설명을 보며 들어가보자사진을 예시로 들어 계산
- 캐릭터 좌표는 (0, 0) 마우스 좌표는 (2, 2)라고 치자.
- 사진처럼 캐릭터 좌표에서 마우스 좌표까지의 벡터가 존재하는데 사실 단순하게 생각만 해봐도 캐릭터 좌표에 얼마를 더하면 마우스 좌표가 되는지를 생각해보면 되는 것이다. 그래서 마우스 좌표에서 캐릭터 좌표를 뺀다면 "(2, 2) - (0, 0) = (2, 2)"가 나오고 이를 캐릭터 좌표에 대입해주면 마우스 좌표가 나오는 것이다. 그러니까 정확히 말해주자면 벡터는 좌표 간의 거리를 구한다기 보다는 해당 좌표에서 '이러한 값'을 더한다면 이 좌표는 이쪽으로 이동한다라는 것인데, 그 '이러한 값'이라는 게 바로 벡터이다. (위치값을 말하는 게 아니라 위치를 이동시키는 값이다.)
- 그래서 방향을 인지 못하고 잘못 계산해서 (0, 0) - (2, 2)로 계산해버린다면 그 벡터는 캐릭터 좌표에서 마우스 좌표의 벡터가 아니라 그 반대 방향의 벡터가 되어버린다. 캐릭터 좌표에서 -(2, 2)를 더한다면 아래처럼 되어버린다.
+ 추가적으로 그러한 벡터를 normalized해버리면 단위벡터가 된다. (크기가 1인 벡터를 단위 벡터라 부름)
- 주로 벡터의 방향만을 구하고자 할 때 normalized를 해주는 편이다.
이해에 도움된 동영상
아크 탄젠트로 각(Degree) 구하기
아직 컴퓨터를 위한 각도를 구한 것은 아니니 아크탄젠트를 활용해 각도를 구해주자
주요 코드
float rotZ = Mathf.Atan2(direction.y, direction.x) * Mathf.Rad2Deg;
아크탄젠트는 탄젠트의 역함수로 높이(y)/밑변(x)을 통해서 얻은 탄젠트 값을 각도로 산출해내는 역삼각함수이다.
(직접 계산해볼 생각은 하지마라; 탄젠트 1값으로 산출되는 45도 말고는 다른 각도들을 구하는 건 수포자에겐 힘든 계산이다;)
- Mathf.Atan2는 라디안을 반환하므로 Mathf.Rad2Deg를 통해 각도로 환산해줘야한다.
3. 마우스를 직접 움직여서 각도의 위치 구하기
그럼 이제 직접 마우스를 움직이면서 어디에 위치하냐에 따라서 각도가 반환되는지 알아보자!
구분시키고 싶은 범위
- 위 사진과 같이 대각선으로 구분지어 4가지의 범위로 구분하여 지정하고자 한다.
구하는 과정
- 테스트의 원활함을 위해 가로 세로 표시선 및 대각선으로 선을 긋고 UI Text에서 각의 값을 나오게 해보았다. 삼각형은 마우스 역할을 해주는 친구이다.
- 우쪽
- 우측 상단
- 좌측
- 하단
- 테스트 GIF
위 사진을 보면 알게되는 사실이 캐릭터가 좌표평면의 0, 0에 해당하고 마우스의 좌표까지의 벡터로 역삼각함수를 산출해내는 것을 알 수 있다.
위 결과로 알아내게 된 마우스의 위치에 따라서 나오는 각도값
! 이제 위 값들을 이용해 조건들을 세우고 애니메이션을 전환시켜보자!
조건문을 세우고 애니메이터를 만져보자
1. 애니메이터 조건 설정
매개변수를 int로 지정해준 다음, 아래와 같이 애니메이션 흐름을 짜고 조건을 자기의 번호에 해당하면 변환하게 해주자.
- direction : 0 = 뒤쪽 ||| 1 = 앞쪽 ||| 2 = 왼쪽 ||| 3 = 오른쪽이다.
2. 조준 스크립트에 애니메이션 조건문 만들기
if (rotZ < 135 && rotZ > 45) { animator.SetInteger("direction", 0); } else if (rotZ < -45 && rotZ > -135) { animator.SetInteger("direction", 1); } else if (Mathf.Abs(rotZ) > 135) { animator.SetInteger("direction", 2); } else if (rotZ < 45 && rotZ > -45) { animator.SetInteger("direction", 3); }
결과
- 의도된 대로 잘 돌아간다.
방향에 따라서 뛰는 애니메이션도 달리해보자
1. 애니메이터 조건 설정
- 달리는 애니메이션은 보는 방향 뿐만 아니라 추가적인 조건이 존재함으로 그 점을 잘 신경써줘야한다.
- 각각의 바라보는 방향에다가 일치하는 달리기들을 붙여준다.
- 달릴 때는 달리는 애니메이션을 설정해주고
- 달리기가 풀릴 때는 두 개의 Transitions를 붙여서 각각 조건을 바라보는 방향이 달라지거나, 움직이지 않을 때로 설정한다.
! Transition를 하나로 해서 조건을 2개 붙이면 어떻게 되나요?
- Transitions 하나에 조건을 2개 붙이게 된다면 그것은 if 조건문에서 &&에 해당하는 조건이다. 만약에 조건을 합쳐서 붙인다면 바라보는 방향대로 움직이고 있다가 멈추면 계속해서 달리기 애니메이션이 실행되게 되는 것이다. 그래서 Transition을 2개 나눠서 각각의 조건을 붙이면 if 조건문의 ||에 해당하게 되므로 달리기를 멈추거나 방향을 바꾸면 달리기 애니메이션을 탈출하게 되는 것이다.
Transitions 하나에 조건 2개를 붙인다면;
- 움직임을 멈췄을 때 달리기 애니메이션을 끝내지 않고, 방향까지 바꿔야 비로소 애니메이션을 멈춘다;
코드 짜기
- 새 스크립트를 짠 후에 InputSystem을 활용하여 Move 버튼을 누르고 있을 때 참을 할당하게 설정해주었다!
using UnityEngine.InputSystem; public class PlayerAnimation : MonoBehaviour { private Animator animator; void Awake() { animator = GetComponent<Animator>(); } private void OnMoveAnimation(InputValue value) // 매우 중요한 부분! { animator.SetBool("Move", value.isPressed); } }
결과
- 의도된 대로 잘 움직여진다.
- 굳이 부자연스러운 점 한가지가 있다면 마우스를 빙빙 돌릴 때 따로 처리를 해줘야할 애니메이션이 있다면 더 부드러워질 것이다.
Vector2.magnitude
벡터의 크기를 반환해준다. (x^2+y^2) 제곱근으로 계산해준다.
제곱근의 과정없이 더 빠르게 계산해주는 친구로는 Vector2.sqrMagnitude가 있다.
이 sqrMagnitude 같은 경우에는 두 벡터의 크기를 비교하는 경우에는 이 sqrMagnitude를 사용하는 것이 좋다.
한번 보고 따라쳐본 코드를 내가 이 코드를 알았다. 내가 이 기능을 넣을 수 있다는 능력을 얻었다라고는 보기 힘든 것 같다. 분명 내가 본 내용임에도 불구하고 이거 어떻게 해야되지하면서 다시 강의 자료를 찾아보며 코드를 짜면서 이해과정을 거치게 되었다. 아무래도 개인 프로젝트를 진행하면서 내가 강의에서 봤던 코드들을 다시 한번 이해해보면서 내 것으로 만드는 과정을 잘 거쳐줘야 비로소 강의 내용들이 내 능력이 될 수 있음을 깨닫게 되었다.