본문 바로가기
Study

Unity, 백터 내적과 외적

by Client. DJ 2024. 6. 12.
반응형

기즈모를 통한 내적 외적 확인하기
VectorMath.unitypackage
0.01MB

백터를 이용한 계산

백터의 내적과 외적을 이용하면 어렵게 수식을 작성하지 않고, 물체의 앞뒤좌우를 쉽게 파악할 수 있습니다.

1. 내적 (Dot Product, A·B)

cosθ에 해당하는 스칼라값을 구할 수 있다.

두 개의 백터의 곱으로 스칼라값을 가져올 수 있습니다. 스칼라값을 역cosθ에 반영하면 사이각을 가져올 수 있습니다.

공식과 이해

스크립트

using UnityEditor;
using UnityEngine;

/// <summary>
/// 백터 내적 테스트
/// 설명: 두 개의 백터의 사이각을 가져올 수 있다. 해당 사이각을 통해서 스칼라를 가져올 수 있고, 스칼라는 cosθ에 해당, 스칼라 값을 통해서 앞뒤를 구분하는데 사용할 수 있다.
/// 예시: 내적을 통해 Onwer가 바라보는 방향(forward)을 기준으로 Target이 앞에 있는지 뒤에 있는지 구분을 할 수가 있다.
///       Target이 앞에 있을 경우 경우 양수(+), 뒤에 있을 경우 음수(-)를 출력한다.
/// 키워드: Vector의 forward, Vector.Dot(내적), 앞뒤 구분 가능(좌우 구분에 대해서는 명확하지 않다)
/// </summary>
public class VectorDotProduct : MonoBehaviour
{
    private const float DISTANCE = 1.5f;

    #region Inspector

    public Transform owner;
    public Transform target;

    #endregion

    private float _duration = 0f;

    private void OnDrawGizmos()
    {
        if (!owner || !target) return;

        Gizmos.color = Color.white;
        Gizmos.DrawWireSphere(owner.position, 0.1f);
        Gizmos.color = Color.red;
        Gizmos.DrawWireSphere(target.position, 0.1f);
        Gizmos.DrawLine(owner.position, target.position);
        Gizmos.color = Color.white;

        // 내적
        float dot = Vector3.Dot(owner.forward, target.position);
        Handles.color = Color.white;
        Handles.Label(transform.position + Vector3.up * 0.5f, $"Dot: {dot}");
        Handles.Label(owner.position + Vector3.down * 0.5f, "내적, 2개 백터의 사잇각을 구할 수 있다.\n" +
                                                            "이를 cosθ을 통해 스칼라 값을 구할 수 있고, 스칼라 값이 양수냐 음수냐에 따라 앞뒤 구분하는데 주로 사용한다.");
        
        Handles.DrawLines(new Vector3[] { owner.position, owner.position + owner.forward });
        Handles.Label(owner.position + owner.forward + Vector3.up * 0.2f, $"Forward");
    }

    private void Update()
    {
        // Target 시계 방향으로 로테이트 애니메이션
        _duration += Time.deltaTime * 0.5f;
        if (_duration > 1f) _duration = 0f;
        Quaternion quaternion = Quaternion.AngleAxis(360f * _duration, owner.up);
        target.position = this.transform.position + quaternion * owner.forward * DISTANCE;
    }
}

참고로
float dot = Vector3.Dot(A, B)를 통해서 구한 값(dot)으로

1. 라디안 = Mathf.Acos(dot)

2. 디그리(각도) = Mathf.Acos(dot) * Mathf.Rad2Deg

위와 같이 사이각을 사용할 수 있습니다.

2. 외적 (Cross product, A×B)

a백터와 b백터 간의 직교되는 Result백터를 구할 수 있다.

두 개의 백터 간 90도로 직교되는 백터를 가져올 수 있습니다. 외적은 2개의 점에서 90도로 직교되는 점을 가져와야하기 때문에 3D에서만 사용할 수 있습니다.

공식과 이해

v백터와 u백터의 직교하는 백터

스크립트

using UnityEditor;
using UnityEngine;

/// <summary>
/// 백터 외적 테스트
/// 설명: 두 개의 백터가 90도로 직교되는 백터를 가져올 수 있다. 외적은 2개의 점에서 90도로 직교되는 점을 가져와야하기 때문에 3D에서만 사용할 수 있다.
/// 예시: 외적을 통해 Onwer가 바라보는 방향(forward)을 기준으로 Target이 왼쪽에 있는지 오른쪽에 있는지 구분을 할 수가 있다.
///       2개의 백터가 90도로 직교되는 위 또는 아래의 축을 가져옵니다. 좌표계에 따라 왼손좌표계는 아래 축을, 오른손좌표계는 위를 향하는 축을 가져온다. (좌표계에 따라 시작 값이 다르다.)
///       이를 통해 가져오는 축의 값이 양수(+)인지 음수(-)인지에 따라 좌우를 구분할 수 있다.
///       일반적인 3D게임에서 x,z축으로 움직이는 경우, 외적을 통한 직교되는 y축의 값으로 좌우를 구분할 수 있다.
/// 키워드: Vector의 forward, Vector.Cross(외적), 좌우 구분 가능
/// </summary>
public class VectorCrossProduct : MonoBehaviour
{
    private const float DISTANCE = 1.5f;
    
    #region Inspector

    public Transform owner;
    public Transform target;

    #endregion

    private float _duration = 0f;

    private void OnDrawGizmos()
    {
        if (!owner || !target) return;

        Gizmos.color = Color.white;
        Gizmos.DrawWireSphere(owner.position, 0.1f);
        Gizmos.color = Color.red;
        Gizmos.DrawWireSphere(target.position, 0.1f);
        Gizmos.DrawLine(owner.position, target.position);
        Gizmos.color = Color.white;

        Vector3 cross = Vector3.Cross(owner.forward, target.localPosition);
        Handles.color = Color.yellow;
        Handles.DrawLine(owner.position, owner.position + cross);
        Handles.color = Color.white;
        Handles.Label(owner.position + Vector3.up * 0.5f, $"x: {cross.x}, y: {cross.y}, z: {cross.z}");
        Handles.Label(owner.position + Vector3.down * 0.5f, "외적, 2개 백터가 직교하는 90도 축을 가져온다.\n" +
                                                            "이를 통해 축의 값이 양수냐 음수냐에 따라 좌우 구분하는데 주로 사용된다.");

        Handles.DrawLines(new Vector3[] { owner.position, owner.position + owner.forward });
        Handles.Label(owner.position + owner.forward + Vector3.up * 0.2f, $"Forward");
    }

    private void Update()
    {
        // Target 시계 방향으로 로테이트 애니메이션
        _duration += Time.deltaTime * 0.5f;
        if (_duration > 1f) _duration = 0f;
        Quaternion quaternion = Quaternion.AngleAxis(360f * _duration, owner.up);
        target.position = this.transform.position + quaternion * owner.forward * DISTANCE;
    }
}

 

마무리

게임을 제작하게되면 가장 많이 접하게 되는 백터 연산입니다. 캐릭터의 앞뒤좌우를 구분하거나, 타겟의 방향에 따라 행동 방식 결정하기 또는 유도 미사일등 다양하게 사용되고 있습니다. 저 역시 공부 중에 기록을 남기고자 작성하였으며, 많은 분들께 도움되길 바랍니다. :)


참고(내적) : https://velog.io/@nacfson/%EC%88%98%ED%95%99Unity-%EB%82%B4%EC%A0%81

참고(외적) : https://velog.io/@nacfson/%EC%88%98%ED%95%99Unity-%EC%99%B8%EC%A0%81

참고(유니티 공식 문서) : https://docs.unity3d.com/kr/530/Manual/UnderstandingVectorArithmetic.html

참고(유투브 참고 영상) : https://www.youtube.com/watch?v=BHhndhjcTsw

반응형

댓글