3. Sudoku

#1 시작화면 (Hinge Joint)

DOlpa_ 2026. 8. 3. 21:48
728x90

원래는 체스 게임을 만들려고 했었는데 배열 관리가 이렇게 어려운건줄 몰랐다

프로그래밍 처음 배울때도 배열이 참 어려웠었는데...그런 의미에서!

게임개발에 사용되는 배열개념을 배우기 딱 좋은 스도쿠를 제작해보도록 하겠다 

 

가장 먼저 시작화면부터 만들어보려 했는데

 

좋은 아이디어가 생각나서 시작화면에서 스도쿠 판을 하나 깔아서 만들려고 했다

그래서 이걸 어떻게 하냐로 별 방법을 다 썻던것 같은데...

 

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class MenuSudoku : MonoBehaviour
{
    private int[,] board;


    void Start()
    {
        CreateBoard();
        DrawGridOnCanvas();
    }

    private void CreateBoard()
    {
        board = new int[9, 9];

        for (int y = 0; y < 9; y++)
        {
            for (int x = 0; x < 9; x++)
            {
                board[y, x] = (x + y) % 2 == 0 ? 1 : 0;
            }
        }

        board[0, 0] = 9;
        board[4, 4] = 5;
        board[8, 8] = 7;
    }


    private void DrawGridOnCanvas()
    {
        int cellSize = 60;
        int width = cellSize * 9;
        int height = cellSize * 9;

        // Canvas에 UI Image 생성
        GameObject canvas = GameObject.Find("Canvas");
        if (canvas == null)
        {
            Debug.LogError("Canvas not found!");
            return;
        }

        GameObject gridImage = new GameObject("GridImage");
        gridImage.transform.SetParent(canvas.transform, false);

        Image image = gridImage.AddComponent<Image>();
        image.color = new Color(1, 1, 1, 0); // 투명 배경
        image.rectTransform.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, width);
        image.rectTransform.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, height);
        image.rectTransform.anchoredPosition = Vector2.zero;

        // 선을 그릴 GameObject 생성
        for (int y = 0; y < 9; y++)
        {
            for (int x = 0; x < 9; x++)
            {
                // 셀의 중심 위치 계산
                float cellX = x * cellSize + cellSize / 2;
                float cellY = y * cellSize + cellSize / 2;

                // 셀의 중심에 텍스트 추가
                GameObject cellText = new GameObject("CellText");
                cellText.transform.SetParent(gridImage.transform, false);
                Text text = cellText.AddComponent<Text>();
                text.text = board[y, x].ToString();
                text.fontSize = 24;
                text.color = Color.black;
                text.rectTransform.anchoredPosition = new Vector2(cellX, cellY);
            }
        }

        // 선을 그릴 GameObject 생성
        for (int i = 0; i < 9; i++)
        {
            // 세로선
            GameObject verticalLine = new GameObject("VerticalLine");
            verticalLine.transform.SetParent(gridImage.transform, false);
            LineRenderer lineRenderer = verticalLine.AddComponent<LineRenderer>();
            lineRenderer.positionCount = 2;
            lineRenderer.SetPosition(0, new Vector3(i * cellSize, 0));
            lineRenderer.SetPosition(1, new Vector3(i * cellSize, height));

            // 가로선
            GameObject horizontalLine = new GameObject("HorizontalLine");
            horizontalLine.transform.SetParent(gridImage.transform, false);
            LineRenderer lineRenderer2 = horizontalLine.AddComponent<LineRenderer>();
            lineRenderer2.positionCount = 2;
            lineRenderer2.SetPosition(0, new Vector3(0, i * cellSize));
            lineRenderer2.SetPosition(1, new Vector3(width, i * cellSize));
        }

        // 두꺼운 선 추가 (3x3 격자 구분)
        for (int i = 0; i < 3; i++)
        {
            // 세로선
            GameObject verticalLine = new GameObject("VerticalLine");
            verticalLine.transform.SetParent(gridImage.transform, false);
            LineRenderer lineRenderer = verticalLine.AddComponent<LineRenderer>();
            lineRenderer.positionCount = 2;
            lineRenderer.SetPosition(0, new Vector3(i * 3 * cellSize, 0));
            lineRenderer.SetPosition(1, new Vector3(i * 3 * cellSize, height));

            // 가로선
            GameObject horizontalLine = new GameObject("HorizontalLine");
            horizontalLine.transform.SetParent(gridImage.transform, false);
            LineRenderer lineRenderer2 = horizontalLine.AddComponent<LineRenderer>();
            lineRenderer2.positionCount = 2;
            lineRenderer2.SetPosition(0, new Vector3(0, i * 3 * cellSize));
            lineRenderer2.SetPosition(1, new Vector3(width, i * 3 * cellSize));
        }

        // 크기 조정
        transform.localScale = new Vector3(1.05f, 1.05f, 1f);
        transform.position = Vector3.zero;
    }
}

 

결국 코드로 그리는 방법을 선택했다

 

이미 완성된 이미지를 가져오기 > 불투명해서 숫자가 안채워짐

직접 선을 그리기 > 최적화 최악, 불편함 고트

 

이런건 코드로 그리는게 투명함에 최적화도 좋다

 

원래는 완성된 스도쿠 판 위에 숫자 박스를 떨어뜨리고 싶었는데 위 이미지처럼 만들고 싶다는 아이디어가 떠올랐다

가장 윗줄은 박스가 떨어지는 충격을 흡수해서 받아내는? 지붕 역할을 하게 만들고 싶었다

 

그래서 코드를 수정하여 맨 윗줄만 지우고

 

 

사전에 이렇게 미리 윗줄 부분만 만들어놓았다. 일자로 보이지만 사실 6개의 긴 직사각형을 이어붙인것

그리고 내가 원하는 조건은 3가지였는데 이걸 다 만족시키는 방법을 찾느라 시간이 좀 걸렸다

 

1. 사각형의 한쪽 축은 고정이 될것

2. 다른 물체와 충돌이 있을때 부드럽게 일정 각도 이상으로 넘어가지 않고 돌아갈 것

3. 돌아가더라도 자연스럽게 처음 상태였던 제자리를 찾아 돌아올 것

 

이럴땐 어떤게 가장 좋다고 알려줄 사람이 없으니 이것 역시 별 방법을 다 써보고 폐기했었다

그렇게 찾은 가장 좋은 방법은!

 

Hinge Joint 2D

 

사실 딱 이럴때 쓰는 전용 기능인데 이제서야 찾았다

말 그대로 오브젝트에 축을 꽂아주는 컴포넌트

 

 

이렇게 위치를 조절하여 내가 원하는곳에 축을 정해준다. 이러면 x,y,z축 고정을 다 풀어놔도 상관없다. 축이 고정해주니까!

 

using UnityEngine;

[RequireComponent(typeof(Rigidbody2D))]
[RequireComponent(typeof(HingeJoint2D))]
public class BarController : MonoBehaviour
{
    [Header("Spring Return Settings")]
    [Tooltip("원래 각도로 돌아가려는 복원 힘 (토크)")]
    public float returnStrength = 50f;
    [Tooltip("돌아올 때 흔들림을 잡아주는 저항(감쇠)")]
    public float damping = 5f;

    private Rigidbody2D rb;
    private HingeJoint2D hinge;
    private float initialAngle;

    void Start()
    {
        rb = GetComponent<Rigidbody2D>();
        hinge = GetComponent<HingeJoint2D>();
        
        // 게임 시작 시점의 Z축 회전값을 원래 각도로 기억
        initialAngle = transform.eulerAngles.z;
    }

    void FixedUpdate()
    {
        // 현재 각도와 원래 각도의 차이(오차) 계산
        float currentAngle = transform.eulerAngles.z;
        float angleDifference = Mathf.DeltaAngle(currentAngle, initialAngle);

        // 오차가 존재할 때 원래 자리로 되돌아가려는 회전력(토크)을 계산하여 적용
        // (현재 회전 속도에 저항(damping)을 주어 무한정 핑글돌지 않고 멈추게 함)
        float restoringTorque = (angleDifference * returnStrength) - (rb.angularVelocity * damping);
        
        rb.AddTorque(restoringTorque * Mathf.Deg2Rad, ForceMode2D.Force);
    }
}

 

하지만 충돌한 뒤 다시 원상태로 돌아오는건 코드가 필요하다

그에 맞는 코드만 간단하게 짜주면 된다

 

숫자 직접 써넣음ㅋㅋ

 

그리고 위에서 떨어질 프리펩 만들고 랜덤으로 떨궈주면 된다

 

 

그럼 이제 이런식의 연출이 가능한 것이다 

728x90
반응형

'3. Sudoku' 카테고리의 다른 글

#2 스도쿠 문제 제작 & 활성화 (Grid Layout Group)  (0) 2026.08.04