본문 바로가기
개발/Unity

유니티 - 사각형 안에 있는 점 판단하기 (How to Check If a Point Is Inside a Rectangle)

by 피로물든딸기 2022. 5. 13.
반응형

Unity 전체 링크

 

참고

- 삼각형 안에 있는 점 판단하기

- 다각형 안에 있는 점 판단하기

 

평면에 포함되는 사각형에 점이 포함되어 있는지 알아보자.

아래와 같이 마우스가 향하는 평면이 검은색 라인의 사각형 안에 있다면

오브젝트를 파란색으로, 그렇지 않다면 빨간색으로 변경하도록 해보자.

 

또한 평면에서 벗어나는 경우에도 마찬가지의 결과를 나타낸다.


먼저 아래의 평면 내부에 사각형을 그리도록 해보자.

평면을 만드는 4개의 점은 각각 A, B, C, D라고 하자.

 

평면의 중심으로 부터 위를 향하는 선이 deltaZ, 왼쪽을 향하는 선이 deltaX라고 하자.

아래와 같이 drawSquare 함수를 이용해 평면에 그림을 그릴 수 있다.

원하는 너비와 높이를 설정하여 Update에서 호출하면 된다.

    void drawSquare(float deltaX, float deltaZ)
    {
        Vector3 center = this.transform.position;
        Vector3 A, B, C, D;

        A = center - this.transform.right * deltaX + this.transform.forward * deltaZ;
        B = center + this.transform.right * deltaX + this.transform.forward * deltaZ;
        C = center - this.transform.right * deltaX - this.transform.forward * deltaZ;
        D = center + this.transform.right * deltaX - this.transform.forward * deltaZ;

        Debug.DrawLine(A, B, Color.black);
        Debug.DrawLine(A, C, Color.black);
        Debug.DrawLine(B, D, Color.black);
        Debug.DrawLine(C, D, Color.black);
    }

 

위에서 그린 사각형의 넓이 S = deltaX * deltaZ * 4 가 된다.

평면 내부에 점이 있다면, 그 점을 기점으로 4개의 점 A, B, C, D로 만들어진 삼각형의 합은 S가 된다.

 

위와 같이 평면의 중심에 점이 있지 않더라도, 4개의 삼각형의 합은 반드시 S가 된다.

 

아래와 같이 경계를 벗어나면 4개의 삼각형의 합이 S보다 크다.

이것은 애초에 평면에 있지 않은 점, 평면을 벗어난 점도 같은 결과를 낸다.

 

먼저 삼각형의 넓이를 구해보자.

삼각형의 넓이는 세 점을 알면 구할 수 있다.

    float getAreaOfTriangle(Vector3 dot1, Vector3 dot2, Vector3 dot3)
    {
        Vector3 a = dot2 - dot1;
        Vector3 b = dot3 - dot1;
        Vector3 cross = Vector3.Cross(a, b);

        return cross.magnitude / 2.0f;
    }

 

따라서 점 4개의 평면 안에 점이 포함되어 있는지는 아래와 같이 판단할 수 있다.

float의 덧셈이기 때문에 0.1f 정도 오차를 허용하였다.

    bool checkSquareInPoint(float deltaX, float deltaZ, Vector3 point)
    {
        Vector3 center = this.transform.position;
        Vector3 A, B, C, D;
        float t1, t2, t3, t4;
        float area = deltaX * deltaZ * 4;

        A = center - this.transform.right * deltaX + this.transform.forward * deltaZ;
        B = center + this.transform.right * deltaX + this.transform.forward * deltaZ;
        C = center - this.transform.right * deltaX - this.transform.forward * deltaZ;
        D = center + this.transform.right * deltaX - this.transform.forward * deltaZ;

        t1 = getAreaOfTriangle(point, A, B);
        t2 = getAreaOfTriangle(point, A, C);
        t3 = getAreaOfTriangle(point, B, D);
        t4 = getAreaOfTriangle(point, C, D);
        
        return (t1 + t2 + t3 + t4) < area + 0.1f;
    }

 

평면에 점을 움직이기 위해 public으로 contact를 선언한다.

checkSquareInPoint가 true / false에 따라 material을 변경하여 구분하도록 하자.

    Renderer rd;

    public Material red, blue;
    public GameObject contact;

    void Start()
    {
        rd = contact.GetComponent<Renderer>();
    }

 

평면에 마우스를 드래그하면서 검사하기 위해 아래의 코드를 추가한다.

contact 오브젝트의 위치를 평면에 Raycast를 쏴서 구한 좌표로 바꾼다.

평면에만 맞추기 위해 LayerMask를 사용하였다.

    void OnMouseDrag()
    {
        RaycastHit hit;
        Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);

        Debug.DrawRay(ray.origin, ray.direction * 100.0f, Color.red);

        int layer = 1 << LayerMask.NameToLayer("Plane");
        if(Physics.Raycast(ray, out hit, Mathf.Infinity, layer))
        {
            contact.transform.position = hit.point;
        }
    }

 

Update에서 사각형을 그리고 checkSquareInPoint에 따라 material을 변경한다.

    void Update()
    {
        drawSquare(3, 4);

        if (checkSquareInPoint(3, 4, contact.transform.position)) rd.material = blue;
        else rd.material = red;
    }

 

이제 Plane에 CheckInSquare.cs를 추가한 후, Material과 점을 표시할 오브젝트를 추가하면 된다.

 

CheckInSquare.cs의 전체 코드는 아래와 같다.

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

public class CheckInSquare : MonoBehaviour
{
    Renderer rd;

    public Material red, blue;
    public GameObject contact;

    void Start()
    {
        rd = contact.GetComponent<Renderer>();
    }

    float getAreaOfTriangle(Vector3 dot1, Vector3 dot2, Vector3 dot3)
    {
        Vector3 a = dot2 - dot1;
        Vector3 b = dot3 - dot1;
        Vector3 cross = Vector3.Cross(a, b);

        return cross.magnitude / 2.0f;
    }

    bool checkSquareInPoint(float deltaX, float deltaZ, Vector3 point)
    {
        Vector3 center = this.transform.position;
        Vector3 A, B, C, D;
        float t1, t2, t3, t4;
        float area = deltaX * deltaZ * 4;

        A = center - this.transform.right * deltaX + this.transform.forward * deltaZ;
        B = center + this.transform.right * deltaX + this.transform.forward * deltaZ;
        C = center - this.transform.right * deltaX - this.transform.forward * deltaZ;
        D = center + this.transform.right * deltaX - this.transform.forward * deltaZ;

        t1 = getAreaOfTriangle(point, A, B);
        t2 = getAreaOfTriangle(point, A, C);
        t3 = getAreaOfTriangle(point, B, D);
        t4 = getAreaOfTriangle(point, C, D);
        
        return (t1 + t2 + t3 + t4) < area + 0.1f;
    }

    void drawSquare(float deltaX, float deltaZ)
    {
        Vector3 center = this.transform.position;
        Vector3 A, B, C, D;

        A = center - this.transform.right * deltaX + this.transform.forward * deltaZ;
        B = center + this.transform.right * deltaX + this.transform.forward * deltaZ;
        C = center - this.transform.right * deltaX - this.transform.forward * deltaZ;
        D = center + this.transform.right * deltaX - this.transform.forward * deltaZ;

        Debug.DrawLine(A, B, Color.black);
        Debug.DrawLine(A, C, Color.black);
        Debug.DrawLine(B, D, Color.black);
        Debug.DrawLine(C, D, Color.black);

        //Debug.DrawLine(contact.transform.position, A, Color.black);
        //Debug.DrawLine(contact.transform.position, B, Color.black);
        //Debug.DrawLine(contact.transform.position, C, Color.black);
        //Debug.DrawLine(contact.transform.position, D, Color.black);
    }

    void OnMouseDrag()
    {
        RaycastHit hit;
        Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);

        Debug.DrawRay(ray.origin, ray.direction * 100.0f, Color.red);

        int layer = 1 << LayerMask.NameToLayer("Plane");
        if(Physics.Raycast(ray, out hit, Mathf.Infinity, layer))
        {
            contact.transform.position = hit.point;
        }
    }

    void Update()
    {
        drawSquare(3, 4);

        if (checkSquareInPoint(3, 4, contact.transform.position)) rd.material = blue;
        else rd.material = red;
    }
}

 

Unity Plus:

 

Easy 2D, 3D, VR, & AR software for cross-platform development of games and mobile apps. - Unity Store

Have a 2D, 3D, VR, or AR project that needs cross-platform functionality? We can help. Take a look at the easy-to-use Unity Plus real-time dev platform!

store.unity.com

 

Unity Pro:

 

Unity Pro

The complete solutions for professionals to create and operate.

unity.com

 

Unity 프리미엄 학습:

 

Unity Learn

Advance your Unity skills with live sessions and over 750 hours of on-demand learning content designed for creators at every skill level.

unity.com

반응형

댓글