Projext/Assets/Scripts/Systems/Optimization/PlayerRangeManager.cs
2026-02-13 00:23:25 +09:00

158 lines
11 KiB
C#

using UnityEngine; // 유니티 엔진의 기본 기능을 불러올거에요 -> UnityEngine을
using System.Collections.Generic; // 리스트 기능을 사용할거에요 -> System.Collections.Generic을
namespace GameSystems.Optimization // 최적화 네임스페이스를 정의할거에요 -> GameSystems.Optimization으로
{
[DisallowMultipleComponent] // 기능을 제한할거에요 -> 중복 부착을 방지하도록
[AddComponentMenu("Game Systems/Optimization/Player Range Manager")] // 메뉴를 추가할거에요 -> 컴포넌트 추가 경로에
public sealed class PlayerRangeManager : MonoBehaviour // 클래스를 선언할거에요 -> 최적화 관리자인 PlayerRangeManager를
{
#region Serialized Fields
[Header("=== 필수 설정 ===")] // 제목을 표시할거에요 -> === 필수 설정 === 을
[SerializeField, Tooltip("최적화 설정 에셋")] // 필드를 직렬화할거에요 -> 인스펙터 노출을 위해
private RenderOptimizationConfig _config; // 변수를 선언할거에요 -> 설정 에셋을 담을 _config를
[SerializeField, Tooltip("환경 오브젝트들이 들어있는 부모 폴더 리스트")] // 툴팁을 추가할거에요 -> 설명 문구를
private List<Transform> _environmentParents = new List<Transform>(); // 리스트를 생성할거에요 -> 부모 폴더들을 관리할 목록을
#endregion
#region Private Fields
private Transform _playerTransform; // 변수를 선언할거에요 -> 플레이어의 위치 정보를 저장할 변수를
private readonly List<RenderGroup> _renderGroups = new List<RenderGroup>(512); // 리스트를 생성할거에요 -> 렌더 그룹들을 담을 목록을
private bool _isInitialized; // 변수를 선언할거에요 -> 초기화 완료 여부를
private bool _isPaused; // 변수를 선언할거에요 -> 시스템 일시정지 여부를
private int _totalObjectCount; // 변수를 선언할거에요 -> 관리 대상 총 개수를
private int _visibleObjectCount; // 변수를 선언할거에요 -> 화면에 표시 중인 개수를
private int _lastFrameChangedCount; // 변수를 선언할거에요 -> 지난 프레임의 상태 변화 수를
private float _nextCheckTime; // 변수를 선언할거에요 -> 다음 검사 시간을
#endregion
#region Public Properties
public bool IsInitialized => _isInitialized; // 값을 반환할거에요 -> 초기화 완료 여부 상태를
public bool IsPaused => _isPaused; // 값을 반환할거에요 -> 일시정지 여부 상태를
public int TotalObjectCount => _totalObjectCount; // 값을 반환할거에요 -> 총 관리 개수 수치를
public int VisibleObjectCount => _visibleObjectCount; // 값을 반환할거에요 -> 현재 표시 개수 수치를
public float CullingEfficiency => _totalObjectCount > 0 ? 1f - ((float)_visibleObjectCount / _totalObjectCount) : 0f; // 값을 계산해서 반환할거에요 -> 컬링 효율 비율을
#endregion
private void Awake() { ValidateConfiguration(); } // 함수를 실행할거에요 -> 시작 시 설정을 검증하는 Awake를
private void Start() { InitializeSystem(); } // 함수를 실행할거에요 -> 시스템을 초기화하는 Start를
private void Update() // 함수를 실행할거에요 -> 매 프레임 업데이트 로직인 Update를
{
if (!_isInitialized || _isPaused || _playerTransform == null) return; // 조건이 맞으면 중단할거에요 -> 동작 불가능한 상태라면
if (Time.time >= _nextCheckTime) // 조건이 맞으면 실행할거에요 -> 다음 검사 시간이 되었다면
{
UpdateCulling(); // 함수를 실행할거에요 -> 컬링 업데이트 기능을
_nextCheckTime = Time.time + GetCheckInterval(); // 값을 갱신할거에요 -> 다음 검사 예정 시간을
}
}
private void ValidateConfiguration() // 함수를 선언할거에요 -> 설정 누락 여부를 확인하는 ValidateConfiguration을
{
if (_config == null) // 조건이 맞으면 실행할거에요 -> 설정 에셋이 비어있다면
{
Debug.LogWarning("[PlayerRangeManager] Config가 없습니다! 기본값을 생성합니다."); // 경고를 출력할거에요 -> 자동 생성 메시지를
_config = RenderOptimizationConfig.CreateDefault(); // 값을 설정할거에요 -> 기본값으로 생성된 에셋을
}
}
private void InitializeSystem() // 함수를 선언할거에요 -> 전체 시스템을 초기화하는 InitializeSystem을
{
FindPlayer(); // 함수를 실행할거에요 -> 플레이어를 찾는 기능을
ScanEnvironmentObjects(); // 함수를 실행할거에요 -> 주변 오브젝트를 스캔하는 기능을
_isInitialized = true; // 상태를 바꿀거에요 -> 초기화 완료인 참(true)으로
if (ShouldLog()) Debug.Log($"[PlayerRangeManager] 초기화 완료. 대상 그룹: {_totalObjectCount}"); // 조건이 맞으면 로그를 출력할거에요 -> 결과 보고 메시지를
}
private void FindPlayer() // 함수를 선언할거에요 -> 플레이어를 탐색하는 FindPlayer를
{
GameObject player = GameObject.FindGameObjectWithTag(GetPlayerTag()); // 오브젝트를 찾을거에요 -> 지정된 태그를 가진 플레이어를
if (player != null) // 조건이 맞으면 실행할거에요 -> 플레이어를 찾았다면
{
_playerTransform = player.transform; // 값을 저장할거에요 -> 플레이어의 Transform 정보를
}
else // 조건이 틀리면 실행할거에요 -> 플레이어를 못 찾았다면
{
Debug.LogError($"[PlayerRangeManager] '{GetPlayerTag()}' 태그의 플레이어를 찾을 수 없습니다!"); // 에러를 출력할거에요 -> 태그 불일치 메시지를
}
}
private void ScanEnvironmentObjects() // 함수를 선언할거에요 -> 환경 오브젝트들을 스캔하는 ScanEnvironmentObjects를
{
_renderGroups.Clear(); // 리스트를 비울거에요 -> 기존에 있던 렌더 그룹들을
foreach (var parent in _environmentParents) // 반복할거에요 -> 등록된 모든 부모 폴더에 대해
{
if (parent == null) continue; // 조건이 맞으면 건너뛸거에요 -> 부모가 비어있다면
foreach (Transform child in parent) // 반복할거에요 -> 부모 폴더의 모든 자식 오브젝트에 대해
{
Renderer[] renderers = child.GetComponentsInChildren<Renderer>(true); // 배열을 가져올거에요 -> 자식 내부의 모든 렌더러들을
if (renderers.Length > 0) // 조건이 맞으면 실행할거에요 -> 렌더러가 존재한다면
{
_renderGroups.Add(new RenderGroup(renderers)); // 리스트에 추가할거에요 -> 새로운 렌더 그룹을 생성해서
}
}
}
_totalObjectCount = _renderGroups.Count; // 값을 저장할거에요 -> 총 관리 대상 개수를
}
private void UpdateCulling() // 함수를 선언할거에요 -> 컬링을 업데이트하는 UpdateCulling을
{
Vector3 playerPos = _playerTransform.position; // 값을 가져올거에요 -> 현재 플레이어의 위치를
float range = GetRenderRange(); // 값을 가져올거에요 -> 설정된 렌더링 거리 수치를
int changedCount = 0; // 변수를 초기화할거에요 -> 변경 개수를 0으로
int visibleCount = 0; // 변수를 초기화할거에요 -> 표시 중인 개수를 0으로
for (int i = 0; i < _renderGroups.Count; i++) // 반복할거에요 -> 모든 렌더 그룹에 대해
{
if (_renderGroups[i].UpdateVisibility(playerPos, range)) changedCount++; // 조건이 맞으면 증가시킬거에요 -> 상태가 바뀌었다면 변경 수를
if (_renderGroups[i].IsVisible) visibleCount++; // 조건이 맞으면 증가시킬거에요 -> 현재 보이고 있다면 표시 수를
}
_visibleObjectCount = visibleCount; // 값을 저장할거에요 -> 이번 검사 결과의 총 표시 개수를
_lastFrameChangedCount = changedCount; // 값을 저장할거에요 -> 변화가 발생한 그룹 수를
}
public void RefreshObjectList() { ScanEnvironmentObjects(); } // 함수를 실행할거에요 -> 목록을 새로 스캔하는 기능을
public void ShowAllObjects() { foreach (var g in _renderGroups) g.ForceShow(); _visibleObjectCount = _totalObjectCount; } // 함수를 실행할거에요 -> 모든 대상을 켜는 기능을
public void HideAllObjects() { foreach (var g in _renderGroups) g.ForceHide(); _visibleObjectCount = 0; } // 함수를 실행할거에요 -> 모든 대상을 끄는 기능을
public void SetPaused(bool paused) => _isPaused = paused; // 값을 바꿀거에요 -> 일시정지 여부를 전달받은 인자대로
private float GetRenderRange() => _config.RenderRange; // 값을 가져올거에요 -> 설정 파일의 렌더링 사거리 수치를
private float GetCheckInterval() => _config.CheckInterval; // 값을 가져올거에요 -> 설정 파일의 검사 간격 수치를
private string GetPlayerTag() => _config.PlayerTag; // 값을 가져올거에요 -> 설정 파일의 플레이어 태그를
private bool ShouldShowGizmos() => _config.ShowGizmos; // 값을 가져올거에요 -> 설정 파일의 기즈모 표시 여부를
private bool ShouldLog() => _config.EnableVerboseLogging; // 값을 가져올거에요 -> 설정 파일의 로그 활성화 여부를
private void OnDrawGizmos() // 함수를 실행할거에요 -> 기즈모를 그리는 OnDrawGizmos를
{
if (!ShouldShowGizmos() || _playerTransform == null) return; // 조건이 맞으면 중단할거에요 -> 표시 설정이 꺼졌거나 플레이어가 없다면
Gizmos.color = new Color(1f, 0f, 0f, 0.3f); // 색상을 설정할거에요 -> 빨간색 반투명으로
Gizmos.DrawWireSphere(_playerTransform.position, GetRenderRange()); // 그림을 그릴거에요 -> 플레이어 중심의 사거리 구체를
}
private void OnDrawGizmosSelected() // 함수를 실행할거에요 -> 선택 시 기즈모를 그리는 OnDrawGizmosSelected를
{
if (!ShouldShowGizmos() || !_isInitialized) return; // 조건이 맞으면 중단할거에요 -> 표시 설정이 꺼졌거나 초기화 전이라면
foreach (var group in _renderGroups) // 반복할거에요 -> 모든 렌더 그룹에 대해
{
Gizmos.color = group.IsVisible ? new Color(0f, 1f, 0f, 0.5f) : new Color(0.5f, 0.5f, 0.5f, 0.2f); // 색상을 결정할거에요 -> 상태에 따라 초록 혹은 회색으로
Gizmos.DrawWireSphere(group.ActualCenter, group.BoundingRadius); // 그림을 그릴거에요 -> 각 그룹의 경계면 구체를
}
}
}
}