Coding Feature.

[Unity 2D] Portal 같은 게임 만들기 #3 포탈 매커니즘 구현하기 2(속도 변환) 본문

Toy Project/mini-portal [Unity2D]

[Unity 2D] Portal 같은 게임 만들기 #3 포탈 매커니즘 구현하기 2(속도 변환)

codingfeature 2024. 1. 5. 19:54

이전까지 포탈에 들어갈 때 플레이어의 위치만 변경했다면 이제는 플레이어가 들어간 속도 그대로 나오도록 구현해보겠습니다.

 

포탈에는 관성을 이용해서 퍼즐을 클리어해야 하는 구간이 많습니다.

출처 - 위키피디아

 

위 그림처럼 중력을 이용해 포탈에 빠르게 들어가서 빠르게 나오면서 멀리 가도록 하게 구현해야 합니다.

 

또한 포탈에도 각도가 생겨서 들어간 각도대로 나올 수 있도록 해야 합니다.

 

우선 플레이어의 속도를 제어할 것이므로 이전에 플레이어를 움직일 때 사용했던 Transform.Translate는 좋은 플레이어 컨트롤 방법이 아닙니다.

 

대신 플레이어의 속도를 조절하도록 변경하여 물리적 특성을 더 잘 반영할 수 있도록 해줬습니다.

...
    if (Input.GetKey(KeyCode.A)){
        m_rb.velocity += new Vector2(-1 * playerSpeed * Time.deltaTime, 0);
    }

    if (Input.GetKey(KeyCode.D))
    {
        m_rb.velocity += new Vector2(1 * playerSpeed * Time.deltaTime, 0);
    }
...

 

그 다음 사용자가 포탈에 들어갈 때의 속도에서, 속도의 크기(속력)은 그대로 두고, 속도의 방향만 알맞게 바꿀 수 있도록 해줘야 합니다.

 

 

아직 유니티에서 각도를 활용하는 방법에는 미숙해서, 저의 미천한 그림실력으로 식을 구해봤습니다.. ㅎㅎ

 

위 식의 각도는 모두 절대각도를 받아서 사용합니다.

결국 다른 포탈에 나오는 각도는 360 - (플레이어가 포탈에 들어가는 속도의 각도) + (오렌지 포탈 각도) + (블루 포탈 각도) 임을 구했습니다.

 

플레이어가 포탈에 들어가는 속도의 각도는 다음 코드를 사용해서 구했습니다.

PlayerVelocityAngle = Mathf.Atan2(playerVelocity.y, playerVelocity.x) * Mathf.Rad2Deg;

 

atan(y/x) = 각도 (라디안)

Rad2Deg = 라디안 값을 Degree로 바꿀 시 사용되는 상수값 (57.29578)

 

그 다음 앞서 구한 식으로 플레이어가 포탈에 나올때의 각도를 구합니다.

newPlayerVelocityAngle = 360 - PlayerVelocityAngle + orangePortalRotation + bluePortalRotation;

 

 

이제 구한 각도를 2차원 벡터(코드에서는 3차원) 값으로 변환합니다.

Vector3 desiredDirection = new Vector3(Mathf.Cos(newPlayerVelocityAngle * Mathf.Deg2Rad), Mathf.Sin(newPlayerVelocityAngle * Mathf.Deg2Rad), 0);

 

이때 x 성분과 y 성분을 각각 Cos, Sin 함수를 사용해서 구합니다.

Deg2Rad는 Degree를 라디안 값으로 바꾸는 상수 값입니다!

 

이제 새로 만들어진 속도의 방향 벡터를 구했으니 이 벡터를 정규화(크기가 1)하고 속력을 곱하면 속력이 같지만 방향이 다른 속도가 계산됩니다!

 

player.GetComponent<Rigidbody2D>().velocity = desiredDirection.normalized * playerVelocity.magnitude;

 

벡터에 normalized를 통해 정규화하고, magnitue를 통해 플레이어의 속력을 구하게 됩니다.

 

위 유니티 코드를 작성하는데에는 아래 쓰레드가 매우 큰 도움이 되었습니다! 더 자세한 내용은 참고바랍니다!

change velocity direction and not magnatude - Unity Forum

 

change velocity direction and not magnatude

Which component of a velocity Vector3 controls the direction. And how would I change it without effecting the magnitude? Thanks.

forum.unity.com

 

 

위 내용을 정리해서 다시 전체 코드를 작성해보면 다음과 같습니다!

 

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

public class PortalScript : MonoBehaviour
{
    public GameObject player;
    public GameObject orangePortal;
    public GameObject bluePortal;

    Vector3 m_playerVelocity; // 플레이어가 포탈에 들어갈 때의 속도.

    bool m_EnteredPortal; // 플레이어가 포탈에 들어간 경우를 확인하는 Boolean.
    bool isTouchingOrange, isTouchingBlue; // 플레이어가 포탈에 닿은 경우를 확인하는 Boolean.

    float m_PlayerVelocityAngle; // 플레이어가 포탈에 들어갈 때의 속도의 각도 Degree
    float m_newPlayerVelocityAngle; // 플레이어가 포탈에 나갈 때의 속도의 각도 Degree

    float m_orangePortalRotation;
    float m_bluePortalRotation;

    // Start is called before the first frame update
    void Start()
    {
        m_EnteredPortal = false;

        m_orangePortalRotation = orangePortal.transform.rotation.eulerAngles.z;
        m_bluePortalRotation = bluePortal.transform.rotation.eulerAngles.z;
    }

    // Update is called once per frame
    void Update()
    {
        m_playerVelocity = player.GetComponent<Rigidbody2D>().velocity;

        m_PlayerVelocityAngle = Mathf.Atan2(m_playerVelocity.y, m_playerVelocity.x) * Mathf.Rad2Deg; //  포탈을 들어갈 때 각도 Degree
        m_newPlayerVelocityAngle = 360 - m_PlayerVelocityAngle + m_orangePortalRotation + m_bluePortalRotation; // 포탈을 나갈때 각도 Degree

        // 포탈을 나갈 때 방향 벡터.
        Vector3 desiredDirection = new Vector3(Mathf.Cos(m_newPlayerVelocityAngle * Mathf.Deg2Rad), Mathf.Sin(m_newPlayerVelocityAngle * Mathf.Deg2Rad), 0);


        // 플레이어가 포탈에 닿았는가 확인.
        isTouchingOrange = Physics2D.IsTouching(player.GetComponent<BoxCollider2D>(), orangePortal.GetComponent<BoxCollider2D>());
        isTouchingBlue = Physics2D.IsTouching(player.GetComponent<BoxCollider2D>(), bluePortal.GetComponent<BoxCollider2D>());

        if (isTouchingOrange && !m_EnteredPortal) // 오렌지 포탈에 닿은 경우 + 이미 들어간 적이 없는 경우.
        {
            m_EnteredPortal = true;
            player.transform.position = bluePortal.transform.position; //  오렌지 포탈로 위치 변환.
            player.GetComponent<Rigidbody2D>().velocity = desiredDirection.normalized * m_playerVelocity.magnitude; // 포탈을 나갈때 속도의 방향 변환.
        }

        if (isTouchingBlue && !m_EnteredPortal) // 블루 포탈에 닿은 경우 + 이미 들어간 적이 없는 경우.
        {
            m_EnteredPortal = true;
            player.transform.position = orangePortal.transform.position; //  블루 포탈로 위치 변환.
            player.GetComponent<Rigidbody2D>().velocity = desiredDirection.normalized * m_playerVelocity.magnitude; // 포탈을 나갈때 속도의 방향 변환.
        }

        if (!isTouchingBlue && !isTouchingOrange)
        {
            m_EnteredPortal = false;
        }
    }
}

 

아래는 몇 가지 테스팅한 내용입니다.

 

 

 

다음에 할 일을 다음과 같이 설정했습니다.

 

- 포탈건 구현 (사용자가 포탈 직접 만들 수 있도록)

- 캐릭터 애니메이션

- 파티클 및 빛 효과

- 플레이어 속도, 점프 속도, 마찰 등 UX 개선

- 퍼즐 스테이지 구성 및 Scene 관리

- 음악, 효과음 추가

- UI 구현 (메뉴 등)

- QA (버그 수정)