wan의 개발일기
RoomCraft #6. 카테고리별 동적 모델 생성 — 팩토리 패턴으로 가구 형태 조합 본문
소스코드 → github.com/Jywan/RoomCraft
들어가며
지금까지 모든 가구가 그냥 Cube였다. 책상이든 침대든 소파든 전부 네모 상자. 치수는 맞는데 뭐가 뭔지 구분이 안 된다.
이번에는 카테고리에 따라 서로 다른 3D 형태를 생성하는 팩토리를 만든다. 여러 개의 Cube를 조합해서 가구 실루엣을 표현하는 방식.
팩토리 패턴
카테고리별로 다른 형태를 만들어야 하니까, FurnitureModelFactory라는 static 클래스를 하나 두고 switch문으로 분기했다.
public static GameObject Create(FurnitureData data)
{
switch (data.category)
{
case FurnitureCategory.Bed:
return CreateBed(data);
case FurnitureCategory.Desk:
return CreateDesk(data);
case FurnitureCategory.Chair:
return CreateChair(data);
case FurnitureCategory.Sofa:
return CreateSofa(data);
default:
return CreateBox(data);
}
}
구조는 간단하다. 빈 root GameObject 아래에 Cube 파츠들을 자식으로 붙여서 가구 형태를 만든다. 정교한 모델링이 아니라 “실루엣” 수준이지만, 뭐가 책상이고 뭐가 침대인지는 구분된다.
카테고리별 구현
침대 — 본체 + 헤드보드
납작하고 넓은 직육면체(매트리스) + 뒤쪽에 세운 얇은 판(헤드보드).
// 본체 매트리스
CreatePart("Body", root,
new Vector3(0f, size.y * 0.4f, 0f),
new Vector3(size.x, size.y * 0.8f, size.z));
// 헤드보드
CreatePart("Headboard", root,
new Vector3(0f, size.y * 0.75f, size.z / 2f - 0.04f),
new Vector3(size.x, size.y * 0.5f, 0.04f));
책상 — 상판 + 다리 4개
위쪽에 넓고 얇은 상판, 네 모서리에 가는 다리.
// 상판
CreatePart("Top", root,
new Vector3(0f, size.y - topThickness / 2f, 0f),
new Vector3(size.x, topThickness, size.z));
// 다리 4개 (모서리에 배치)
CreatePart("Leg_FL", root,
new Vector3(-xOff, legHeight / 2f, -zOff),
new Vector3(legWidth, legHeight, legWidth));
의자 — 좌석 + 등받이 + 다리 4개
소파 — 좌석 + 등받이 + 팔걸이
파츠 생성 유틸
모든 카테고리에서 공통으로 쓰는 CreatePart() 메서드. Cube를 만들고 부모에 붙이고, Collider는 제거한다.
private static GameObject CreatePart(string name, GameObject parent,
Vector3 localPos, Vector3 scale)
{
GameObject part = GameObject.CreatePrimitive(PrimitiveType.Cube);
part.name = name;
part.transform.parent = parent.transform;
part.transform.localPosition = localPos;
part.transform.localScale = scale;
// 파츠에는 Collider 불필요 (root의 BoxCollider만 사용)
Object.Destroy(part.GetComponent<Collider>());
return part;
}
파츠 각각에 Collider를 두면 물리 충돌로 다른 가구가 사라지는 버그가 생겼다. root에 하나의 BoxCollider만 두고, 파츠 Collider는 전부 제거하는 게 정답이었다.
삽질 기록
가구가 공중에 뜸
MoveTo()에서 Y를 size.y / 2f로 올리고 있었는데, 팩토리가 이미 파츠를 바닥 기준으로 배치해놨다. 이중으로 올려져서 공중에 떴다.
해결: MoveTo()의 Y를 0으로 고정. 팩토리가 파츠 Y를 잡아주니까 root는 바닥(Y=0)에 있으면 된다.
가구 클릭 안 됨
Raycast가 파츠의 Collider에 맞는데, FurnitureObject는 root에만 붙어있어서 GetComponent로 못 찾았다.
해결: GetComponentInParent<FurnitureObject>()를 추가해서 부모에서도 찾도록 수정.
가구가 벽 속으로 파고듦
헤드보드/등받이가 BoxCollider 경계 밖으로 튀어나와 있었다. 그리고 SetRoomSize()에 height를 넘겨서 방 세로 경계가 잘못 잡혀있었다.
해결: 파츠 위치를 BoxCollider 안쪽으로 조정 + SetRoomSize(width, depth)로 수정 + 벽 두께 보정.
정리
| 기능 | 핵심 구현 |
|---|---|
| 팩토리 패턴 | FurnitureModelFactory.Create() — switch로 카테고리 분기 |
| 파츠 조합 | CreatePart() — Cube를 자식으로 붙여 형태 구성 |
| BoxCollider | root에 하나만 — data.GetSizeInMeters()로 크기 직접 설정 |
| 색상 적용 | GetComponentsInChildren<Renderer>()로 전체 파츠에 일괄 적용 |
다음 편에서는 프로젝트 저장/불러오기(JSON 직렬화)를 구현할 예정이다.
'1인 게임개발 활동 > 가구배치 시뮬레이터' 카테고리의 다른 글
| RoomCraft #8. 그리드 스냅 + 치수 표시 — 정렬 도우미와 가구 정보 UI (0) | 2026.06.28 |
|---|---|
| RoomCraft #7. 프로젝트 저장/불러오기 — JSON 직렬화 (0) | 2026.06.28 |
| RoomCraft #5. 가구 생성 UI — 카테고리/치수/색상 입력 팝업 (0) | 2026.06.28 |
| RoomCraft 개발일지 #4. 충돌 감지 — 벽 밖 제한 + 겹침 경고 (0) | 2026.06.27 |
| RoomCraft 개발일지 #3. 가구 드래그 앤 드롭 — 선택, 이동, 회전, 삭제 (0) | 2026.06.26 |