152 lines
5.4 KiB
C#
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);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|