233 lines
6.9 KiB
C#
233 lines
6.9 KiB
C#
using UnityEngine;
|
|
|
|
namespace GameSystems.Optimization
|
|
{
|
|
/// <summary>
|
|
/// 렌더링 최적화를 위한 오브젝트 그룹 데이터
|
|
/// - 불변성(Immutability) 보장
|
|
/// - 캡슐화된 내부 데이터
|
|
/// </summary>
|
|
public sealed class RenderGroup
|
|
{
|
|
#region Private Fields
|
|
|
|
private readonly Vector3 _actualCenter;
|
|
private readonly Renderer[] _renderers;
|
|
private readonly float _boundingRadius;
|
|
private bool _isCurrentlyVisible;
|
|
|
|
#endregion
|
|
|
|
#region Public Properties (Read-Only)
|
|
|
|
/// <summary>
|
|
/// 실제 메쉬의 중심점 (피벗이 아닌 bounds 기준)
|
|
/// </summary>
|
|
public Vector3 ActualCenter => _actualCenter;
|
|
|
|
/// <summary>
|
|
/// 오브젝트의 최대 반지름 (경계 구체)
|
|
/// </summary>
|
|
public float BoundingRadius => _boundingRadius;
|
|
|
|
/// <summary>
|
|
/// 현재 렌더링 상태
|
|
/// </summary>
|
|
public bool IsVisible => _isCurrentlyVisible;
|
|
|
|
/// <summary>
|
|
/// 관리 중인 Renderer 개수
|
|
/// </summary>
|
|
public int RendererCount => _renderers?.Length ?? 0;
|
|
|
|
#endregion
|
|
|
|
#region Constructor
|
|
|
|
/// <summary>
|
|
/// RenderGroup 생성자
|
|
/// </summary>
|
|
/// <param name="renderers">대상 Renderer 배열 (null 체크 수행)</param>
|
|
public RenderGroup(Renderer[] renderers)
|
|
{
|
|
if (renderers == null || renderers.Length == 0)
|
|
{
|
|
Debug.LogError("[RenderGroup] Renderer 배열이 null 또는 비어있습니다.");
|
|
_renderers = System.Array.Empty<Renderer>();
|
|
_actualCenter = Vector3.zero;
|
|
_boundingRadius = 0f;
|
|
return;
|
|
}
|
|
|
|
// 방어적 복사 (외부에서 배열 수정 방지)
|
|
_renderers = new Renderer[renderers.Length];
|
|
System.Array.Copy(renderers, _renderers, renderers.Length);
|
|
|
|
// 실제 중심점 계산
|
|
_actualCenter = CalculateGroupCenter(_renderers);
|
|
|
|
// 경계 반지름 계산
|
|
_boundingRadius = CalculateBoundingRadius(_renderers, _actualCenter);
|
|
|
|
_isCurrentlyVisible = true; // 초기 상태는 표시
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Public Methods
|
|
|
|
/// <summary>
|
|
/// 거리 기반으로 렌더링 상태 업데이트
|
|
/// </summary>
|
|
/// <param name="referencePosition">기준 위치 (플레이어 등)</param>
|
|
/// <param name="maxDistance">최대 렌더링 거리</param>
|
|
/// <returns>상태가 변경되었는지 여부</returns>
|
|
public bool UpdateVisibility(Vector3 referencePosition, float maxDistance)
|
|
{
|
|
// 거리 계산 (오브젝트 크기 고려)
|
|
float distance = Vector3.Distance(referencePosition, _actualCenter);
|
|
bool shouldBeVisible = (distance - _boundingRadius) <= maxDistance;
|
|
|
|
// 상태 변경이 필요한 경우에만 처리
|
|
if (shouldBeVisible == _isCurrentlyVisible)
|
|
{
|
|
return false; // 변경 없음
|
|
}
|
|
|
|
// Renderer 상태 일괄 변경
|
|
SetRenderersState(shouldBeVisible);
|
|
_isCurrentlyVisible = shouldBeVisible;
|
|
|
|
return true; // 변경됨
|
|
}
|
|
|
|
/// <summary>
|
|
/// 강제로 모든 Renderer 표시
|
|
/// </summary>
|
|
public void ForceShow()
|
|
{
|
|
SetRenderersState(true);
|
|
_isCurrentlyVisible = true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 강제로 모든 Renderer 숨김
|
|
/// </summary>
|
|
public void ForceHide()
|
|
{
|
|
SetRenderersState(false);
|
|
_isCurrentlyVisible = false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 그룹 유효성 검사 (Renderer가 파괴되었는지 확인)
|
|
/// </summary>
|
|
public bool IsValid()
|
|
{
|
|
if (_renderers == null || _renderers.Length == 0)
|
|
return false;
|
|
|
|
// 하나라도 유효한 Renderer가 있으면 true
|
|
foreach (Renderer r in _renderers)
|
|
{
|
|
if (r != null) return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Private Methods
|
|
|
|
/// <summary>
|
|
/// 여러 Renderer의 통합 중심점 계산
|
|
/// </summary>
|
|
private static Vector3 CalculateGroupCenter(Renderer[] renderers)
|
|
{
|
|
if (renderers.Length == 1 && renderers[0] != null)
|
|
{
|
|
return renderers[0].bounds.center;
|
|
}
|
|
|
|
// 모든 bounds를 포함하는 통합 경계 계산
|
|
Bounds? combinedBounds = null;
|
|
|
|
foreach (Renderer r in renderers)
|
|
{
|
|
if (r == null) continue;
|
|
|
|
if (!combinedBounds.HasValue)
|
|
{
|
|
combinedBounds = r.bounds;
|
|
}
|
|
else
|
|
{
|
|
Bounds temp = combinedBounds.Value;
|
|
temp.Encapsulate(r.bounds);
|
|
combinedBounds = temp;
|
|
}
|
|
}
|
|
|
|
return combinedBounds?.center ?? Vector3.zero;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 오브젝트의 최대 반지름 계산
|
|
/// </summary>
|
|
private static float CalculateBoundingRadius(Renderer[] renderers, Vector3 center)
|
|
{
|
|
float maxRadius = 0f;
|
|
|
|
foreach (Renderer r in renderers)
|
|
{
|
|
if (r == null) continue;
|
|
|
|
// 각 Renderer 중심에서의 거리 + extent 크기
|
|
float distanceFromCenter = Vector3.Distance(r.bounds.center, center);
|
|
float extent = r.bounds.extents.magnitude;
|
|
float totalRadius = distanceFromCenter + extent;
|
|
|
|
if (totalRadius > maxRadius)
|
|
{
|
|
maxRadius = totalRadius;
|
|
}
|
|
}
|
|
|
|
return maxRadius;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 모든 Renderer의 enabled 상태 일괄 변경
|
|
/// </summary>
|
|
private void SetRenderersState(bool enabled)
|
|
{
|
|
for (int i = 0; i < _renderers.Length; i++)
|
|
{
|
|
Renderer r = _renderers[i];
|
|
|
|
// null 체크 + 상태가 다를 때만 변경 (성능 최적화)
|
|
if (r != null && r.enabled != enabled)
|
|
{
|
|
r.enabled = enabled;
|
|
}
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Cleanup
|
|
|
|
/// <summary>
|
|
/// 리소스 정리 (명시적 호출용)
|
|
/// </summary>
|
|
public void Dispose()
|
|
{
|
|
// Renderer 배열 초기화 (GC 도움)
|
|
System.Array.Clear(_renderers, 0, _renderers.Length);
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
}
|