wan의 개발일기

RoomCraft 개발일지 #1 — 프로젝트 설정 + 방 생성 시스템 본문

1인 게임개발 활동/가구배치 시뮬레이터

RoomCraft 개발일지 #1 — 프로젝트 설정 + 방 생성 시스템

wan_ 2026. 6. 25. 22:03
소스코드 → github.com/Jywan/RoomCraft

들어가며

C# 공부하면서 네트워크까지 찍먹하고 나니까, 이제 뭔가 만들어보고 싶어졌다. 그래서 Unity를 처음 열어봤다.

뭘 만들까 고민하다가, 이사할 때마다 겪는 문제가 떠올랐다. “이 책상이 내 방에 들어가긴 하는 건가?” 줄자로 재고 머릿속으로 그려보는데, 막상 옮기면 안 맞아서 짜증났던 기억.

그래서 실제 치수를 입력하면 3D로 배치를 확인할 수 있는 시뮬레이터를 만들어보기로 했다. 이름은 RoomCraft.

Unity 완전 처음이라 삽질도 많이 할 것 같은데, 그 과정을 기록으로 남겨두려 한다.


뭘 만들 건지

핵심은 간단하다. “내 가구가 이 방에 들어갈까?”를 3D로 확인하는 도구.
  • 방 크기를 m 단위로 입력하면 3D 방이 생성됨
  • 가구를 cm 단위로 만들어서 방 안에 드래그로 배치
  • 위에서 내려다보는 탑뷰 ↔ 3D 자유시점 전환
  • 예쁜 그래픽이 목적이 아니라, 치수 정확성이 핵심

기술 스택

구분기술
엔진Unity 6 (6000.3.x)
언어C#
렌더링URP
3D 모델링ProBuilder
카메라Cinemachine
입력New Input System
저장JSON

프로젝트 세팅

Unity Hub에서 3D (URP) 템플릿으로 생성했다. 나중에 모바일이랑 WebGL로 빌드할 생각이라 URP로 갔다.

프로젝트 만들고 나서 Package Manager에서 패키지 3개를 추가로 설치했다.

패키지왜 필요한지
ProBuilder가구 모양을 코드로 만들려고
Cinemachine카메라 시점 전환용
Input System터치/마우스 입력을 하나로 처리하려고

그리고 폴더 구조를 미리 잡아뒀다. 나중에 파일 많아지면 찾기 힘드니까.

Assets/
├── Scripts/
│   ├── Room/          ← 방 생성
│   ├── Furniture/     ← 가구
│   ├── Camera/        ← 카메라
│   ├── UI/            ← UI
│   ├── Data/          ← 데이터 모델
│   └── Input/         ← 입력 처리
├── Prefabs/
├── Materials/
└── Scenes/

방 생성 시스템

일단 방부터 만들어야 가구를 넣든 말든 하니까, 방 생성을 먼저 구현했다.

구조는 단순하다. 가로/세로/높이를 받아서 바닥 하나 + 벽 4개를 생성하는 거다.

RoomData — 방 정보 클래스

일단 방 치수를 담을 데이터 클래스부터 만들었다. [Serializable] 붙여놓으면 나중에 JSON 저장할 때 편하다길래 미리 붙여둠.

using UnityEngine;
using System;

namespace RoomCraft.Data
{
    [Serializable]
    public class RoomData
    {
        public float width;     // 가로 (m)
        public float depth;     // 세로 (m)
        public float height;    // 높이 (m)
        public string roomName;

        public RoomData(float width, float depth, float height, string roomName = "새 방")
        {
            this.width = width;
            this.depth = depth;
            this.height = height;
            this.roomName = roomName;
        }
    }
}

RoomBuilder — 실제로 방을 만드는 놈

이게 핵심이다. BuildRoom()을 호출하면 바닥이랑 벽 4면이 뿅 하고 생긴다.

접근 방식은 간단한데, Unity의 CreatePrimitive(Cube)로 큐브를 하나 만들고, scale을 방 치수에 맞게 늘리는 거다. 정교한 모델링이 아니라 치수 확인이 목적이니까 이 정도면 충분하다.

using RoomCraft.Data;
using UnityEngine;

namespace RoomCraft.Room
{
    public class RoomBuilder : MonoBehaviour
    {
        [Header("Room Settings")]
        [SerializeField] private Material floorMaterial;
        [SerializeField] private Material wallMaterial;

        private GameObject currentRoom;

        public void BuildRoom(RoomData data)
        {
            // 기존 방이 있으면 날리고 새로 만듦
            if (currentRoom != null)
                Destroy(currentRoom);

            currentRoom = new GameObject($"Room_{data.roomName}");

            CreateFloor(data);
            CreateWalls(data);
        }

        private void CreateFloor(RoomData data)
        {
            GameObject floor = GameObject.CreatePrimitive(PrimitiveType.Cube);
            floor.name = "Floor";
            floor.transform.parent = currentRoom.transform;

            // 바닥: 가로 x 세로, 두께는 0.1m로 얇게
            floor.transform.localScale = new Vector3(data.width, 0.1f, data.depth);
            floor.transform.localPosition = new Vector3(0f, -0.05f, 0f);

            if (floorMaterial != null)
                floor.GetComponent<Renderer>().material = floorMaterial;

            floor.tag = "Floor";
        }

        private void CreateWalls(RoomData data)
        {
            float wallThickness = 0.1f;

            CreateWall("Wall_Back", data,
                new Vector3(0f, data.height / 2f, data.depth / 2f),
                new Vector3(data.width, data.height, wallThickness));

            CreateWall("Wall_Front", data,
                new Vector3(0f, data.height / 2f, -data.depth / 2f),
                new Vector3(data.width, data.height, wallThickness));

            CreateWall("Wall_Left", data,
                new Vector3(-data.width / 2f, data.height / 2f, 0f),
                new Vector3(wallThickness, data.height, data.depth));

            CreateWall("Wall_Right", data,
                new Vector3(data.width / 2f, data.height / 2f, 0f),
                new Vector3(wallThickness, data.height, data.depth));
        }

        private void CreateWall(string name, RoomData data, Vector3 position, Vector3 scale)
        {
            GameObject wall = GameObject.CreatePrimitive(PrimitiveType.Cube);
            wall.name = name;
            wall.transform.parent = currentRoom.transform;
            wall.transform.localPosition = position;
            wall.transform.localScale = scale;

            if (wallMaterial != null)
                wall.GetComponent<Renderer>().material = wallMaterial;

            wall.tag = "Wall";
        }
    }
}
바닥은 Y를 -0.05로 내려서 윗면이 딱 Y=0에 오게 했고, 벽은 Y를 height/2로 올려서 바닥 위에 서게 했다. 처음에 이거 안 맞아서 벽이 바닥 밑으로 빠지는 걸 보고 당황했었다.

실행 결과

테스트로 4m × 3m × 2.5m 방을 생성해봤다.

RoomData testRoom = new RoomData(4f, 3f, 2.5f, "테스트방");
builder.BuildRoom(testRoom);

Play 누르니까 방이 생겼다.

Game 뷰 — 방이 보인다! 아직 못생겼지만 치수는 정확하다
Hierarchy — Room_테스트방 아래에 Floor + Wall 4개가 잘 붙어있다

아직 Material을 안 넣어서 못생기긴 한데, 일단 구조는 맞다. 벽 4면 + 바닥 1개. 나중에 색 입히면 그럴싸해질 거다.


삽질 기록

Rider 자동완성이 안 됨

코드 치는데 자동완성이 하나도 안 떴다. MonoBehaviour를 치는데 빨간줄이 뜨길래 한참 헤맸다.

원인: Rider를 먼저 켜놓고 Unity에서 스크립트를 열면 .sln 연결 없이 파일만 열린다.
해결: Rider에서 RoomCraft_Unity.sln을 직접 열거나, Rider 닫고 Unity에서 더블클릭으로 열면 된다.

Tag 등록 안 하면 에러

floor.tag = "Floor" 했더니 “Tag Floor is not defined” 에러가 떴다. 코드에서 태그를 쓰려면 Unity에서 먼저 등록해야 한다는 걸 몰랐다.

해결: Edit → Project Settings → Tags and Layers에서 수동 추가. Unity 초보 실수 1호.

정리

기능핵심 구현
방 데이터RoomData — [Serializable] 클래스로 치수 저장
바닥 생성CreatePrimitive(Cube) + localScale로 크기 조절
벽 4면CreateWall() — 위치/크기 파라미터화해서 반복 호출
방 리셋Destroy(currentRoom) 후 새로 생성

다음 편에서는 카메라 시스템을 만든다. 위에서 내려다보는 탑뷰랑, 자유롭게 돌려볼 수 있는 3D 뷰를 전환하는 기능을 구현해보려고 한다.