using UnityEngine; #if UNITY_EDITOR using UnityEditor; using UnityEditor.SceneManagement; #endif public class BookShelfAutoFill : MonoBehaviour { [Header("책 프리팹들")] public GameObject[] bookPrefabs; [Header("책장 배치 설정")] public int rows = 4; public int booksPerRow = 12; [Tooltip("첫 번째 책 위치. 이 오브젝트 기준 로컬 좌표")] public Vector3 startLocalPosition = new Vector3(-1.0f, 0f, 0f); [Tooltip("책 사이 간격. X는 옆으로, Y는 위 칸으로")] public Vector3 spacing = new Vector3(0.13f, 0.42f, 0f); [Header("책 랜덤 느낌")] public Vector2 randomXOffset = new Vector2(-0.015f, 0.015f); public Vector2 randomYOffset = new Vector2(-0.015f, 0.015f); public Vector2 randomZOffset = new Vector2(-0.01f, 0.01f); public Vector2 randomHeightScale = new Vector2(0.85f, 1.15f); public Vector2 randomWidthScale = new Vector2(0.9f, 1.1f); public Vector2 randomDepthScale = new Vector2(0.9f, 1.05f); public Vector2 randomYRotation = new Vector2(-3f, 3f); public Vector2 randomZRotation = new Vector2(-2f, 2f); [Header("기본 회전")] public Vector3 baseRotation = Vector3.zero; [Header("기타")] public bool clearBeforeGenerate = true; public int seed = 10; private const string GeneratedParentName = "Generated_Books"; [ContextMenu("책 자동 배치")] public void GenerateBooks() { if (bookPrefabs == null || bookPrefabs.Length == 0) { Debug.LogWarning("[BookShelfAutoFill] Book Prefabs에 책 프리팹을 넣어줘."); return; } Random.InitState(seed); if (clearBeforeGenerate) { ClearBooks(); } GameObject parent = new GameObject(GeneratedParentName); parent.transform.SetParent(transform); parent.transform.localPosition = Vector3.zero; parent.transform.localRotation = Quaternion.identity; parent.transform.localScale = Vector3.one; #if UNITY_EDITOR if (!Application.isPlaying) { Undo.RegisterCreatedObjectUndo(parent, "Create Generated Books Parent"); } #endif for (int r = 0; r < rows; r++) { float currentX = startLocalPosition.x; for (int c = 0; c < booksPerRow; c++) { GameObject prefab = bookPrefabs[Random.Range(0, bookPrefabs.Length)]; if (prefab == null) continue; GameObject book = CreateBook(prefab, parent.transform); if (book == null) continue; float offsetX = Random.Range(randomXOffset.x, randomXOffset.y); float offsetY = Random.Range(randomYOffset.x, randomYOffset.y); float offsetZ = Random.Range(randomZOffset.x, randomZOffset.y); book.transform.localPosition = new Vector3( currentX + offsetX, startLocalPosition.y + r * spacing.y + offsetY, startLocalPosition.z + offsetZ ); book.transform.localRotation = Quaternion.Euler( baseRotation.x, baseRotation.y + Random.Range(randomYRotation.x, randomYRotation.y), baseRotation.z + Random.Range(randomZRotation.x, randomZRotation.y) ); Vector3 originalScale = book.transform.localScale; book.transform.localScale = new Vector3( originalScale.x * Random.Range(randomWidthScale.x, randomWidthScale.y), originalScale.y * Random.Range(randomHeightScale.x, randomHeightScale.y), originalScale.z * Random.Range(randomDepthScale.x, randomDepthScale.y) ); currentX += spacing.x; } } #if UNITY_EDITOR if (!Application.isPlaying) { EditorUtility.SetDirty(gameObject); EditorSceneManager.MarkSceneDirty(gameObject.scene); } #endif Debug.Log("[BookShelfAutoFill] 책 자동 배치 완료. 마음에 들면 Ctrl+S 저장 후 이 스크립트 컴포넌트는 삭제해도 됨."); } [ContextMenu("생성된 책 삭제")] public void ClearBooks() { Transform old = transform.Find(GeneratedParentName); if (old == null) { Debug.Log("[BookShelfAutoFill] 삭제할 Generated_Books가 없음."); return; } #if UNITY_EDITOR if (!Application.isPlaying) { // Inspector가 삭제될 오브젝트를 보고 있으면 에러가 나서 선택을 부모로 돌림 if (Selection.activeGameObject != null) { Transform selected = Selection.activeGameObject.transform; if (selected == old || selected.IsChildOf(old)) { Selection.activeGameObject = gameObject; } } Undo.DestroyObjectImmediate(old.gameObject); EditorUtility.SetDirty(gameObject); EditorSceneManager.MarkSceneDirty(gameObject.scene); Debug.Log("[BookShelfAutoFill] 생성된 책 삭제 완료."); return; } #endif Destroy(old.gameObject); } private GameObject CreateBook(GameObject prefab, Transform parent) { #if UNITY_EDITOR if (!Application.isPlaying) { GameObject obj = PrefabUtility.InstantiatePrefab(prefab, parent) as GameObject; if (obj != null) { Undo.RegisterCreatedObjectUndo(obj, "Create Book"); } return obj; } #endif return Instantiate(prefab, parent); } }