쿼터니언, 아크탄젠트, 마우스 따라 회전하는 무기, 적용하기

유승아·2024년 5월 10일

내일배움캠프

목록 보기
40/69

어제 배웠던 내용을 토대로 맵을 만들었다.
일단 땅과 벽만 만들었고, 다른 기능들 구현을 좀 한 다음에 맵을 더 꾸며줄 것이다.


조준(Aim) 시스템

ArcTangent? 라는 것이 어떤 느낌인지... 알아보자...😵‍💫

1. 쿼터니언(Quaternion)

4차원 복소수를 이용한 회전 표현 방법, (x, y, z, w) 형태로 표현한다.
직접 값을 변경하거나 계산하는 대신, 내장 함수를 활용하여 조작하는 것이 좋다.
(Quaternion.Euler, QuaternionLookRotation, Quaternion.Slerp 등)

Quaternion.Euler: 오일러각을 쿼터니언으로 변경, 세 축(x, y, z)을 변환 해줌
QuaternionLookRotation: 앞이랑 위를 알려주고 회전값을 받음
머리를 회전하는 것처럼, 앞과 위를 특정한 방향으로 하는 회전 쿼터니언을 만듦
Quaternion.Slerp: 회전의 중간값 받기
쿼터니언과 다른 쿼터니언 사이에 내분점을 알 수 있음
구형 보간(Spherical Linear Interpolation)기법을 활용해서 쿼터니언에 쓰면 더 정확함
두 쿼터니언 Q1, Q2가 있을 때 30% 지점은 어디일까? 이런 문제 풀 때 유용

쿼터니언은 애초에 직관적인 수체계를 사용하지 않는다.
직접 값을 사용하려면 회전이 없다는 의미의 Quaternion.Identity만 사용하기

Quaternion.Identity: 회전이 없는 상태를 나타내는 쿼터니언

쿼터니언을 사용하는 이유

Vector3(XYZ) 회전을 쓰는 경우 *짐벌락 문제가 있다.
(극단적인 각도로 회전할 경우 한 축으로의 회전을 잃어버림)

짐벌락(Gimval Lock)

한 가지 축이 사라지면서 회전이 고장나는 것
(세 축으로만 회전하는 경우 발생하는 문제)

2. 아크탄젠트(ArcTangent)

삼각함수

각도를 통해 삼각형들의 각 변의 비율을 알아낼 수 있는 함수

각도를 통해서 좌표 알기(각도 → 비율)
나 각도 아는데 비율(좌표) 뭐야?

탄젠트(tan)는 밑변(x)과 높이(y)의 비율!

사인(sin), 코사인(cos), 탄젠트(tan)
sin = 높이/빗변
cos = 밑변/빗변,
tan = 높이/밑변

아크탄젠트

삼각함수에서 아크(arc)-가 붙으면 삼각함수의 역함수인 역삼각함수를 말한다.
탄젠트의 역함수 = 역삼각함수

좌표를 통해서 각도 알기(비율 → 각도)
나 좌표 아는데 각도 몇 도야?

가로와 세로의 비에 따른 각도를 계산하는 데 사용된다.

2D에서 점 또는 벡터에 대한 각도를 찾는 데 아크탄젠트 함수가 종종 사용된다.
비율말고도 x, y 자체가 양수인지 음수인지를 통해 360도를 다 구분할 수 있게 했다.
원래는 180도 각도 범위에서만 알아낼 수 있었음)
이러한 구현을 Atan2라고 이름 붙이고, 밑변/높이였던 탄젠트의 기본 개념을 적용해 y, x순으로 인수를 넣는다.

이를 통해 점 (x, y)에서, Math.Atan2(y, x) 함수를 사용하여 벡터가 x축과 이루는 각도를 [π,π][-\pi , \pi]까지의 값으로 얻을 수 있다.
(각도를 π\pi가 180도가 되도록 표현하는 방법을 호도법, 그리고 그 단위를 라디안이라고 부른다.)

Atan2를 활용해서 객체가 특정 방향을 향하도록 만들거나 두 점 사이의 각도를 계산할 때 유용하다.

Atan2는 각도를 알기 위해 y와 x의 비율(좌표)을 통해 각을 알아내는 함수이고, [π,π][-\pi,\pi]범위에서 결과가 나온다.


3. 마우스를 따라 회전하는 무기 만들기

WeaponPivot
Weapon 기준점

WeaponSprite
Weapon 이미지

BulletSpawnPoint
Bullet(화살) 생성 위치

using System;
using UnityEngine;

public class TopDownAimRotation : MonoBehaviour
{
    //Arm Renderer라고 해서 드래그 앤 드랍으로 연결해 줄 것임 - 바꿔야 할 것들이 좀 있어서...
    [SerializeField] private SpriteRenderer armRenderer; //화살 뒤집기, 지금은 사용 X 몬스터가 가지고 있는 것을 뒤집어줘야 함
    [SerializeField] private Transform armPivot; //WeaponPivot 으로 바꾼다??

    [SerializeField] private SpriteRenderer characterRenderer; //에임에 따라서 캐릭터 뒤집기 위함

    private TopDownController controller; //함수를 등록해주기 위해서, 이벤트를 등록하기 위해서 추가함

    private void Awake()
    {
        controller = GetComponent<TopDownController>();
    }

    private void Start()
    {
        //start에서 OnLookEvent에 OnAim 함수 등록해주기

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

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

    private void RotateArm(Vector2 direction)
    {
        //이 Radian을 Euler에 넣을 것이기 때문에 Mathf.Rad2Deg 곱해주기
        float rotZ = Mathf.Atan2(direction.y, direction.x) * Mathf.Rad2Deg; //y, x 순으로 넣어주기
                                                                            //→ 캐릭터에서 마우스를 바라보는 각도를 얻음

        //캐릭터 뒤집기
        characterRenderer.flipX = Mathf.Abs(rotZ) > 90f; //Abs = Absolute Value, 절대값이 90보다 클 때
                                                         //-90 ~ 90도에서는 오른쪽을 바라보고, -90도 미만 90도 초과라면 왼쪽 바라보기

        //팔 회전 시켜주기
        // 팔을 돌릴 때는 나온 각도를 그대로 적용하는데, 이때 유니티 내부에서 사용하는 쿼터니언으로 변환한다.
        // 쿼터니으로 변형하는 방법 두 가지
        // 1) Vector3를 Quaternion으로 변환해서 넣는 방법
        //    Quaternion.Euler(x 회전, y 회전, z 회전) : 오일러 각 기준으로 값을 넣으면 쿼터니언으로 변환됨
        armPivot.rotation = Quaternion.Euler(0, 0, rotZ); //z축으로 회전

        // 2) eulerAngles를 통해 자동으로 변환되게 하는 방법 - rotation이랑 비슷하게 변환이 되어서 들어간다고 생각하면 됩니다.
        //    Transform.eulerAngles을 변경
        // (2번 방법으로 하면) armPivot.eulerAngles = new Vector3(0, 0, rotZ);
    }
}

TopDownAimRotation 스크립트 작성(받아쓰기)

코드보다 주석이 더 많은 것 같다...

찍고 보니 마우스가 안 보인다!
하지만 기능은 잘 작동하고 있다. 😅


4. 적용하기

아직 무기 사용은 예정이 없어서 마우스 바라보는 것만 적용했다.
캐릭터 움직임에 따라 카메라도 따라가게 했다.

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

public class CameraController : MonoBehaviour
{
    public float cameraSpeed = 20.0f; //카메라 이동속도 조절

    public GameObject player;

    // Update is called once per frame
    void Update()
    {
        //플레이어의 위치와 카메라의 현재 위치를 기반으로 방향 벡터를 계산
        Vector3 dir = player.transform.position - this.transform.position;

        //카메라가 이동해야 할 벡터를 계산
        //플레이어 쪽으로 이동해야 하므로 방향 벡터에 카메라 이동 속도와 Time.deltaTime 값을 곱하여 이동 거리를 조절
        Vector3 moveVecter = new Vector3(dir.x * cameraSpeed * Time.deltaTime, dir.y * cameraSpeed * Time.deltaTime, 0.0f);

        //계산된 이동 벡터(moveVecter)를 사용하여 카메라의 위치를 업데이트
        //Translate 함수를 사용하여 현재 위치에서 상대적으로 이동시킴
        this.transform.Translate(moveVecter);
    }
}

카메라가 플레이어 따라가게 만들기 👉 https://wikidocs.net/91307

기본 동작 애니메이션을 적용했다.
숨 쉬는 것 같아서 귀엽다...💘

기본 상태 애니메이션과 걷는 상태 애니메이션을 따로 적용할 예정이다.

0개의 댓글