본문 바로가기

Unity/루빅스 큐브 개발 일지

[Unity] 큐브 위치 확인 알고리즘

요약

루빅스 큐브 층 회전 이론을 통해 Raycast 순서를 서로 다른 층에 다르게 적용하기 위해 서로 다른 좌표를 가진 배열 2개를 만들었다. 그 후 Raycast() 속성의 layerMask를 통해 불필요한 충돌을 방지했다. 충돌된 오브젝트는 cubeState.cs에 저장되어 활용된다.


알고리즘

 

Ray를 그림과 같이 생성하기 위해 다음과 같은 코드를 작성하였다

ReadCube.cs

public class ReadCube : MonoBehaviour
{
    // Ray의 위치를 할당 받을 변수
    public Transform tUp;
    public Transform tDown;
    public Transform tLeft;
    public Transform tRight;
    public Transform tFront;
    public Transform tBack;

    // 복제된 Ray를 저장할 변수
    private List<GameObject> frontRays = new List<GameObject>();
    private List<GameObject> backRays = new List<GameObject>();
    private List<GameObject> upRays = new List<GameObject>();
    private List<GameObject> downRays = new List<GameObject>();
    private List<GameObject> leftRays = new List<GameObject>();
    private List<GameObject> rightRays = new List<GameObject>();

    private int layerMask = 1 << 8;
    CubeState cubeState;
    public GameObject emptyGo;
    int[,] XY = { { -1, 0 }, { -1, 1 }, { 0, 1 }, { 1, 1 }, { 1, 0 }, { 1, -1 }, { 0, -1 }, { -1, -1 }, { 0, 0 } };
    int[,] mirrorXY = { { 1, 0 }, { 1, 1 }, { 0, 1 }, { -1, 1 }, { -1, 0 }, { -1, -1 }, { 0, -1 }, { 1, -1 }, { 0, 0 } };

    void Start()
    {
        SetRayTransforms();
        cubeState = FindObjectOfType<CubeState>();
        ReadState();
    }

해당 코드에서 눈에 띄는 부분은 layerMask와 mirrorXY 배열일 것이다. layerMask는 Raycast() 속성을 통해 앞에 있는 물체가 가려도 해당 layerMask에만 충돌하도록 설정할 수 있다. mirrorXY 배열은 루빅스 큐브 층의 회전 방향을 맞추기 위해 서로 다른 소환 위치를 정의한 것이다.

 

    // 업데이트 된 위치에서 인식하여 색상을 읽어오는 함수
    public void ReadState()
    {
        cubeState = FindObjectOfType<CubeState>();

        cubeState.up = ReadFace(upRays, tUp);
        cubeState.down = ReadFace(downRays, tDown);
        cubeState.left = ReadFace(leftRays, tLeft);
        cubeState.right = ReadFace(rightRays, tRight);
        cubeState.front = ReadFace(frontRays, tFront);
        cubeState.back = ReadFace(backRays, tBack);
    }

    // Ray의 방향과 위치를 확인하여 BuildRays통해 복제하는 함수
    void SetRayTransforms()
    {
        upRays = BuildRays(tUp, new Vector3(90, 90, 90), true);
        downRays = BuildRays(tDown, new Vector3(270, 90, 90));
        leftRays = BuildRays(tLeft, new Vector3(0, 180, 90), true);
        rightRays = BuildRays(tRight, new Vector3(0, 0, 270));
        frontRays = BuildRays(tFront, new Vector3(0, 90, 0));
        backRays = BuildRays(tBack, new Vector3(0, 270, 0), true);
    }

Start()에서 보이는 함수들은 Ray를 배치하고 색상을 가져와 CubeState 클래스에 저장하는 역할을 하고 있다. 해당 클래스는 특별한 기능은 없으며, 단순히 데이터만 저장한다.

 

    //Ray를 복제하는 함수
    List<GameObject> BuildRays(Transform rayTransform, Vector3 direction, bool mirror = false)
    {
        int rayCount = 0;
        List<GameObject> rays = new List<GameObject>();
        // -1, 1 | 0, 1 | 1, 1
        // -1, 0 | 0, 0 | 1, 0
        // -1,-1 | 0,-1 | 1,-1
        // 위의 방식으로 큐브를 생성
        // 순서는 XY배열로 생성됨

        // 거울처럼 반대로 되게 하기 위해여 mirror배열순서로 생성함
        if (mirror)
        {
            for (int i = 0; i < 9; i++)
            {
                Vector3 startPos = new Vector3(rayTransform.localPosition.x + mirrorXY[i, 0], rayTransform.localPosition.y + mirrorXY[i, 1], rayTransform.localPosition.z);
                GameObject rayStart = Instantiate(emptyGo, startPos, Quaternion.identity, rayTransform);
                rayStart.name = rayCount.ToString();
                rays.Add(rayStart);
                rayCount++;
            }
        }
        else
        {
            for (int i = 0; i < 9; i++)
            {
                Vector3 startPos = new Vector3(rayTransform.localPosition.x + XY[i, 0], rayTransform.localPosition.y + XY[i, 1], rayTransform.localPosition.z);
                GameObject rayStart = Instantiate(emptyGo, startPos, Quaternion.identity, rayTransform);
                rayStart.name = rayCount.ToString();
                rays.Add(rayStart);
                rayCount++;
            }
        }

        // 방향에 맞게 Ray을 돌림
        rayTransform.localRotation = Quaternion.Euler(direction);
        return rays;
    }

먼저 BuildRays를 통해 생성하는 순서에 맞춰 Ray 위치를 생성하고 있는 것이 보인다. 다만, 회전과 반대 방향에 해당하는 Ray들은 그에 맞는 mirrorXY 배열을 통해 생성되는 것을 볼 수 있다.

 

    // Ray를 통해 색상을 읽어 내는 함수
    public List<GameObject> ReadFace(List<GameObject> rayStarts, Transform rayTransform)
    {
        List<GameObject> facesHit = new List<GameObject>();

        // Ray가 위치한 앞부분에 색상값(타일)을 인식하는 역할
        foreach (GameObject reyStart in rayStarts)
        {
            Vector3 ray = reyStart.transform.position;
            RaycastHit hit;

            // Ray를 통해 facesHit에 색상을 넣어줌
            if (Physics.Raycast(ray, rayTransform.forward, out hit, Mathf.Infinity, layerMask))
            {
                Debug.DrawRay(ray, rayTransform.forward * hit.distance, Color.yellow);
                facesHit.Add(hit.collider.gameObject);
            }
            else
            {
                Debug.DrawRay(ray, rayTransform.forward * 1000, Color.green);
            }
        }
        return facesHit;
    }
}

충돌된 오브젝트를 반환하는 역할을 한다. 특이한 점은 충돌이 발생하지 않는 경우 색상을 통해 이를 알려주는 역할도 하고 있다는 것이다.