using UnityEngine; using Unity.Netcode; public class UndergroundGenerator : NetworkBehaviour { [Header("Generation Range")] [SerializeField] private Vector3Int generationRange = new Vector3Int(20, 30, 10); [SerializeField] private float noiseScale = 0.12f; [Header("Thresholds (0 to 1)")] [SerializeField, Range(0, 1)] private float hollowThreshold = 0.35f; [SerializeField, Range(0, 1)] private float baseResourceThreshold = 0.8f; [Header("Depth Settings")] [SerializeField] private float depthFactor = 0.005f; // 깊어질수록 임계값 감소 (자원 증가) [Header("Prefabs")] [SerializeField] private GameObject normalBlockPrefab; [SerializeField] private GameObject resourceBlockPrefab; [Header("Organization")] [SerializeField] private string containerName = "UndergroundBlocks"; private Transform _blockContainer; // 블록들을 담을 부모 오브젝트 private float _seedX, _seedY, _seedZ; private void Awake() { _seedX = Random.Range(0f, 99999f); _seedY = Random.Range(0f, 99999f); _seedZ = Random.Range(0f, 99999f); // Hierarchy 정리를 위한 컨테이너 생성 _blockContainer = new GameObject(containerName).transform; } public override void OnNetworkSpawn() { if (IsServer) GenerateFromGeneratorPivot(); } private void GenerateFromGeneratorPivot() { Vector3Int originGrid = BuildManager.Instance.WorldToGrid3D(transform.position); for (int x = 0; x < generationRange.x; x++) { for (int y = 0; y > -generationRange.y; y--) { for (int z = 0; z < generationRange.z; z++) { Vector3Int targetGridPos = originGrid + new Vector3Int(x, y, z); float noise = Get3DNoise(targetGridPos.x, targetGridPos.y, targetGridPos.z); if (noise < hollowThreshold) continue; float currentThreshold = baseResourceThreshold + (y * depthFactor); GameObject prefab = (noise > currentThreshold) ? resourceBlockPrefab : normalBlockPrefab; SpawnBlock(prefab, targetGridPos); } } } } private void SpawnBlock(GameObject prefab, Vector3Int gridPos) { if (prefab == null) return; Vector3 worldPos = BuildManager.Instance.GridToWorld(gridPos); // 1. 생성 시 부모(Container)를 지정하여 Hierarchy 정리 GameObject block = Instantiate(prefab, worldPos, Quaternion.identity); // 2. 네트워크 스폰 (Netcode for GameObjects) block.GetComponent().Spawn(); } private float Get3DNoise(int x, int y, int z) { // 대칭 방지를 위한 10000 오프셋 및 시드 적용 float xCoord = (x + _seedX + 10000f) * noiseScale; float yCoord = (y + _seedY + 10000f) * noiseScale; float zCoord = (z + _seedZ + 10000f) * noiseScale; float ab = Mathf.PerlinNoise(xCoord, yCoord); float bc = Mathf.PerlinNoise(yCoord, zCoord); float ac = Mathf.PerlinNoise(xCoord, zCoord); return (ab + bc + ac) / 3f; } private void OnDrawGizmosSelected() { BuildManager bm = BuildManager.Instance; if (bm == null) bm = FindFirstObjectByType(); // 에러 방지 안전장치 if (bm == null) return; Vector3Int originGrid = bm.WorldToGrid3D(transform.position); for (int x = 0; x < generationRange.x; x += 2) { for (int y = -generationRange.y; y < 0; y += 2) { for (int z = 0; z < generationRange.z; z += 2) { Vector3Int targetGridPos = originGrid + new Vector3Int(x, y, z); float noise = Get3DNoise(targetGridPos.x, targetGridPos.y, targetGridPos.z); if (noise < hollowThreshold) continue; Gizmos.color = (noise > baseResourceThreshold) ? Color.yellow : new Color(0.5f, 0.5f, 0.5f, 0.2f); Vector3 pos = bm.GridToWorld(targetGridPos); Gizmos.DrawSphere(pos, 0.2f); } } } } }