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

152 lines
5.4 KiB
C#

using UnityEngine;
using System.Collections.Generic;
namespace GameSystems.Optimization
{
[DisallowMultipleComponent]
[AddComponentMenu("Game Systems/Optimization/Player Range Manager")]
public sealed class PlayerRangeManager : MonoBehaviour
{
#region Serialized Fields
[Header("=== 필수 설정 ===")]
[SerializeField, Tooltip("최적화 설정 에셋")]
private RenderOptimizationConfig _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(); }
private void Start() { InitializeSystem(); }
private void Update()
{
if (!_isInitialized || _isPaused) return;
if (Time.time >= _nextCheckTime)
{
UpdateCulling();
_nextCheckTime = Time.time + GetCheckInterval();
}
}
private void ValidateConfiguration()
{
if (_config == null) _config = RenderOptimizationConfig.CreateDefault();
}
public void InitializeSystem()
{
if (!FindPlayer()) return;
if (!ScanEnvironmentObjects()) return;
_isInitialized = true;
_nextCheckTime = Time.time;
}
private bool FindPlayer()
{
GameObject playerObj = GameObject.FindGameObjectWithTag(GetPlayerTag());
_playerTransform = (playerObj != null) ? playerObj.transform : transform;
return true;
}
// ⭐ [핵심 수정] 리스트 내의 모든 폴더를 뒤져서 '낱개' 단위로 등록합니다.
private bool ScanEnvironmentObjects()
{
if (_environmentParents == null || _environmentParents.Count == 0) return false;
_renderGroups.Clear();
foreach (Transform parent in _environmentParents)
{
if (parent == null) continue;
// ⭐ 폴더 구조가 몇 층이든 상관없이 모든 Renderer를 낱낱이 찾아냅니다.
Renderer[] allRenderers = parent.GetComponentsInChildren<Renderer>(_config.IncludeInactiveObjects);
foreach (Renderer r in allRenderers)
{
if (r == null) continue;
// 각 렌더러를 독립적인 그룹으로 생성 (개별 최적화의 핵심)
_renderGroups.Add(new RenderGroup(new Renderer[] { r }));
}
}
_totalObjectCount = _renderGroups.Count;
return _totalObjectCount > 0;
}
private void UpdateCulling()
{
if (_playerTransform == null) return;
Vector3 playerPosition = _playerTransform.position;
float maxDistance = GetRenderRange();
int changedCount = 0;
int visibleCount = 0;
for (int i = 0; i < _renderGroups.Count; i++)
{
if (_renderGroups[i].UpdateVisibility(playerPosition, maxDistance)) 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 void OnDrawGizmos()
{
if (!ShouldShowGizmos() || _playerTransform == null) return;
Gizmos.color = new Color(1f, 0f, 0f, 0.3f);
Gizmos.DrawWireSphere(_playerTransform.position, GetRenderRange());
}
private void 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.DrawSphere(group.ActualCenter, 0.3f);
}
}
}
}