나 개발자 진짜 되냐?

[ Unity 뱀서라이크 게임을 만들어보자 4 ] 삼각함수를 이용하여 조준 구현 본문

유니티를 공부해봐요!/초급이에요!

[ Unity 뱀서라이크 게임을 만들어보자 4 ] 삼각함수를 이용하여 조준 구현

Snow Rabbit 2024. 10. 9. 02:22

 

 

(❁´◡`❁)

 

수학적인 부분이 많이 나와서

당황했다..

하지만 나

이과생이었다

 

잊지말자

 

몇년전인지는 모르지만..

 

삼각함수에 대해 공부하고

활을 쥐어줘서 활을 쏘게해보자!!

 


 

삼각함수를 알기전에 알아야 할 

쿼터니언

 

왜 이 친구가 필요한가 하면

 

유니티에서는 vector3을 쓴다.

 

하지만 vector3을 쓰다보면

짐벌 락(Gimbal Lock) 문제가 발생한다.

z축과 x축이 겹치는 순간이 오기 때문에

맛탱이 가버린다.

 

그래서!

쿼터니언이라는 친구를 써서 짐벌락을 막는다.

 

쿼터니언에는 자주쓰는 메소드 3개가 있다.

 

Quaternion.Euler

오일러각을 쿼터니언으로 변경할 수 있다.

오일러각이라..이따가 설명하겠다!

Quaternion.LookRotation

앞과 위를 특정한 방향으로 회전하는 친구이다.

Quaternion.Slerp

쿼터니언과 다른 쿼터니언 사이에 내분점을 알 수 있다.

회전의 중앙값을 구할때 사용하는 편이다.

 


 

자,

이제 삼각함수에 대해 공부해보자

 

삼각함수를 사용하는 이유는 아까 위에서 말한 오일러 각 때문이다.

 

오일러각은 삼각함수 후에 설명하겠다.

 

일단 우리가 아는 삼각함수

 

 

사인

코사인

탄젠트

 

세종류가 있다.

 

sin = 높이/빗변         cos = 밑변/빗변          tan = 높이/밑변

 

그 중에서도 우리는 이

세타값을 알아야하는데

이 세타값을 알려면 구해야하는 함수가

바로 b/c 

탄젠트이다.

 

탄젠트

세타를 구하기 위한 함수이고

b와 c의 비율을 통해 세타 값을 구할 수있다.

 

 

삼각함수를 드디어 겨우 이해했는데

 

다음에 아크탄젠트 라는 친구가 나옵니다.

 

이친구는 역삼각함수로

우리는 비율을 보고 각도를 구했다면

이제 각도를 보고 비율을 찾을 수 있는 친구지요.

 

또한 

우리는 더 편하기 쓰기위해

비율을 찾을수 있는 이외에

 

x,y가 양수인지 음수인지를 통해

360도를 다 구분할 수 있게 했다.

 

 

이렇게 해주면 오른쪽에 있는 친구는 -90 < a < 90

왼쪽은 90< a< 180 && -90< a < 0

 

이렇게 해서 왼쪽오른쪽이 구분이 가능하고

 

뭐 0이상 90이하는 1사분면

90이상 180이하는 2사분면

180이상 270이하는 3사분면

270이상 360이하는 4사분면

 

이렇게도 분류가 된다!

그래서 우리는 이런 계산식을

Atan2라고 이름 붙이고,

 

밑변 / 높이였던 탄젠트의 기본 개념을 적용해

y,x 순으로 인수를 넣는다.

 

보통 x,y여서 x부터 넣는경우가 많은데

x가 아니라 y부터 넣어준다.

 

자,

다음에 알아야하는건

우리 중학교때 배우는

라디안 이라는 친구이다.

 

라디안은 호도법이라고 하며

파이로 표현된 각도를 180도가 되도록 하는 방법이다.

 

 

점 (x, y)에서, Math.Atan2(y, x) 함수를 사용하여

벡터가 x축과 이루는 각도를 [-\pi , \pi]까지의 값으로 얻을 수 있다.

 

그 값으로 내 시야를 조정할 수 있고,

조준 할 위치도 선정이 가능하고

다양한 각도를 설정할 수 있게 된다.


 

자 개념을 익혔다면!

 

코드를 짜보자!

 

먼저

오브젝트를 추가하자

 

 

WeaponPivot

얘를 기준으로 무기가 움직인다.

그리고 그 안에 

WeaponSprite 와

( 무기 모양 )

 

BulletSpawnPoint

( 화살이 생성되는 위치 )

을 넣어준다.

 

포지션 크기를 조절한다.

 

여기서는

스프라이트 렌더러를 통해 그림도 넣어주고

화살이 위에 있어야하니 

오더 인 레이어도 6으로 설정!

 

이렇게 설정해주면

 

 

활든 친구 완성!

 

자! 이제 스크립트를 짜보자

 

스크립트 이름은

TopDownAimRotation

먼저 선언부

정규화를 위해 시리얼리즈필드 추가해준다.

 

 

우리는 움직이게하는 컨트롤러가 필요하기때문에 만들어주고

그 값을

awake 함수를 통해 가져오게 된다.

 

다음 start에서는

onAim 값을 가져올거고

 

onAim값은 또

RotateArm이라는 함수에서 값을 가져오게 된다.

 

 

주석을 길게 적어두었는데..

 

먼저 float rotZ 값을 만들고

y 에서 x값으로 아크탄젠트로 각도를 구하고

 

라디안에서 디그리로 바꿔주는 값을 곱해준다.

 

그다음 

 

Quaternion.Euler를 통해서 각도를 지정해줘라 라는 뜻이 된다.

 

중간에 이제

 

 characterRenderer.flipX = Mathf.Abs(rotZ) > 90f;

 

이친구가 나오는데

이건 아까 그림을 다시 가져와보겠다

 

보면 아까도 말했지만 90도씩 바뀔때마다 사분면이 바뀌듯

잘 보면 패턴이 있다

 

2,3분면에 있는 친구들은 +90보다 크고 - 90보다 크다

 

1,4 분면은 절대값으로 따졌을때 90 이하고

2,3 사분면은 절대값으로 따질때 90 이상이다.

 

그래서!

 

Mathf.Abs(rotZ) > 90f;

abs 즉 절대값을 의미하며

90보다 크면 뒤집어라! 라는 뜻이 된다.

 

자 코드를 다 짰다면!

연결해주어야겠지요

 

player 밑에다가 스크립트 추가하면

우리가 아까 시리어라이즈필드 했던 친구 3개가 뜬다.

 

이렇게 연결해주면 진짜 끝!

 

< TopDownAimRotation.cs >

using UnityEngine;

public class TopDownAimRotation : MonoBehaviour
{
    //화살이 뒤집히는것
    [SerializeField] private SpriteRenderer armRenderer;
    //에임이 어디있는가, 무기에 따른 피봇
    [SerializeField] private Transform armPivot;
    //에임에 따라서 사람이 움직여야한다.
    [SerializeField] private SpriteRenderer characterRenderer;

    //이벤트를 등록해주어야하기 때문에 컨트롤러가 있어주어야한다.
    private TopDownController controller;

    private void Awake()
    {
        //컨트롤러를 컴포넌트를 가져온다.
        controller = GetComponent<TopDownController>();
    }

    void Start()
    {
        // 마우스의 위치가 들어오는 OnLookEvent에 등록하는 것
        // 마우스의 위치를 받아서 팔을 돌리는 데 활용할 것임.
        controller.onLookEvent += OnAim;
    }

    public void OnAim(Vector2 direction)
    {
        // OnLook
        RotateArm(direction);
    }

    private void RotateArm(Vector2 direction)
    {
        // Atan2는 직각삼각형이 있다고 할 때 세로가 y, 가로가 x일 때 그 각도를 라디안 [-Pi,Pi]로 나타내는 함수
        // 라디안의 -Pi는 -180도, Pi는 180도 이므로 Mathf.Rad2Deg는 약 57.29임 (180 / 3.14)
        // y에서 x로 가야 각도를 구할 수 있다. 라디안값을 디그리로 바꿔주는게 Rad2Deg
        float rotZ = Mathf.Atan2(direction.y, direction.x) * Mathf.Rad2Deg;

        // [1. 캐릭터 뒤집기]
        // 이때 각도는 오른쪽(1,0 방향)이 0도이므로,
        // -90~90도에서는 오른쪽을 바라보는 게 맞지만, -90도 미만 90도 초과라면 왼쪽을 바라보는 것임.
        characterRenderer.flipX = Mathf.Abs(rotZ) > 90f;

        // [2. 팔 돌리기]
        // 팔을 돌릴 때는 나온 각도를 그대로 적용하는데, 이때 유니티 내부에서 사용하는 쿼터니언으로 변환한다.
        // 쿼터니으로 변형하는 방법 두 가지
        // 1) Vector3를 Quaternion으로 변환해서 넣는 방법
        //    Quaternion.Euler(x 회전, y 회전, z 회전) : 오일러 각 기준으로 값을 넣으면 쿼터니언으로 변환됨
        // 2) eulerAngles를 통해 자동으로 변환되게 하는 방법 - rotation이랑 비슷하게 변환이 되어서 들어간다고 생각하면 됩니다.
        //    Transform.eulerAngles을 변경
        armPivot.rotation = Quaternion.Euler(0, 0, rotZ);
        // (2번 방법으로 하면) armPivot.eulerAngles = new Vector3(0, 0, rotZ);
    }
}