using UnityEngine; // 유니티 엔진의 기본 기능을 불러올거에요 -> UnityEngine을 using System.Collections.Generic; // 리스트 기능을 사용할거에요 -> System.Collections.Generic을 public class PlayerRangeManager : MonoBehaviour // 클래스를 선언할거에요 -> 거리 기반 최적화 컴포넌트인 PlayerRangeManager를 { [Header("--- 대상 설정 ---")] // 제목을 표시할거에요 -> --- 대상 설정 --- 을 [SerializeField] private Transform environmentParent; // 변수를 선언할거에요 -> 맵 오브젝트들이 있는 부모 폴더를 [Header("--- 최적화 설정 ---")] // 제목을 표시할거에요 -> --- 최적화 설정 --- 을 [SerializeField] private float renderRange = 40f; // 변수를 선언할거에요 -> 표시 사거리인 40을 renderRange에 [SerializeField] private float checkInterval = 0.2f; // 변수를 선언할거에요 -> 검사 주기인 0.2를 checkInterval에 private struct RenderGroup // 구조체를 정의할거에요 -> 렌더링 그룹 데이터 구조를 { public Vector3 actualCenter; // 변수를 선언할거에요 -> 실제 메쉬의 중심 위치를 public Renderer[] renderers; // 배열을 선언할거에요 -> 포함된 렌더러 목록을 } private List _childGroups = new List(); // 리스트를 생성할거에요 -> 하위 그룹들을 관리할 목록을 private Transform _player; // 변수를 선언할거에요 -> 플레이어 위치 정보를 담을 변수를 private void Start() // 함수를 실행할거에요 -> 시작 시 Start를 { // 태그로 플레이어를 정확히 찾습니다. GameObject playerObj = GameObject.FindGameObjectWithTag("Player"); // 찾을거에요 -> "Player" 태그를 가진 오브젝트를 if (playerObj != null) _player = playerObj.transform; // 조건이 맞으면 저장할거에요 -> 플레이어 위치를 else _player = transform; // 조건이 틀리면 저장할거에요 -> 내 위치를 대신 if (environmentParent != null) RefreshChildList(); // 조건이 맞으면 실행할거에요 -> 환경 오브젝트 목록 갱신을 InvokeRepeating(nameof(UpdateCulling), 0f, checkInterval); // 반복 실행할거에요 -> 컬링 업데이트 함수를 주기적으로 } public void RefreshChildList() // 함수를 선언할거에요 -> 자식 목록을 새로고침하는 RefreshChildList를 { if (environmentParent == null) return; // 조건이 맞으면 중단할거에요 -> 부모 폴더가 없다면 _childGroups.Clear(); // 리스트를 비울거에요 -> 기존 목록을 foreach (Transform child in environmentParent) // 반복할거에요 -> 부모 내의 모든 자식 오브젝트에 대해 { Renderer[] rs = child.GetComponentsInChildren(true); // 배열을 가져올거에요 -> 자식 내부의 모든 렌더러들을 if (rs.Length > 0) // 조건이 맞으면 실행할거에요 -> 렌더러가 존재한다면 { // ⭐ [수정] 피벗(position)이 아니라, 실제 모델링의 중심(bounds.center)을 가져옵니다. // 이래야 "가까이 갔는데 꺼지는" 현상이 완벽히 해결됩니다! Vector3 center = rs[0].bounds.center; // 위치를 가져올거에요 -> 첫 번째 렌더러의 실제 경계 중심점을 _childGroups.Add(new RenderGroup { actualCenter = center, renderers = rs }); // 리스트에 추가할거에요 -> 새로운 그룹 정보를 생성해서 } } } private void UpdateCulling() // 함수를 선언할거에요 -> 실제 컬링 로직인 UpdateCulling을 { if (_player == null) return; // 조건이 맞으면 중단할거에요 -> 플레이어가 없다면 Vector3 playerPos = _player.position; // 값을 가져올거에요 -> 현재 플레이어의 위치를 foreach (var group in _childGroups) // 반복할거에요 -> 모든 관리 그룹에 대해 { // ⭐ [핵심] 실제 몸통 중심점과의 거리를 계산합니다. float dist = Vector3.Distance(playerPos, group.actualCenter); // 거리를 계산할거에요 -> 플레이어와 그룹 중심 사이의 bool shouldShow = dist <= renderRange; // 상태를 결정할거에요 -> 범위 이내인지 여부를 foreach (Renderer r in group.renderers) // 반복할거에요 -> 그룹 내 모든 렌더러에 대해 { if (r != null && r.enabled != shouldShow) r.enabled = shouldShow; // 조건이 맞으면 상태를 바꿀거에요 -> 렌더러의 활성화 여부를 } } } }