using UnityEngine; // 유니티 엔진의 기본 기능을 불러올거에요 -> UnityEngine을
namespace GameSystems.Optimization // 최적화 네임스페이스를 정의할거에요 -> GameSystems.Optimization으로
{
///
/// 렌더링 최적화를 위한 오브젝트 그룹 데이터
/// - 불변성(Immutability) 보장
/// - 캡슐화된 내부 데이터
///
public sealed class RenderGroup // 클래스를 선언할거에요 -> 상속 불가능한 최적화 그룹인 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)
///
/// 실제 메쉬의 중심점 (피벗이 아닌 bounds 기준)
///
public Vector3 ActualCenter => _actualCenter; // 프로퍼티를 선언할거에요 -> 실제 중심점을 반환하는 기능을
///
/// 오브젝트의 최대 반지름 (경계 구체)
///
public float BoundingRadius => _boundingRadius; // 프로퍼티를 선언할거에요 -> 경계 반지름을 반환하는 기능을
///
/// 현재 렌더링 상태
///
public bool IsVisible => _isCurrentlyVisible; // 프로퍼티를 선언할거에요 -> 표시 여부 상태를 반환하는 기능을
///
/// 관리 중인 Renderer 개수
///
public int RendererCount => _renderers?.Length ?? 0; // 프로퍼티를 선언할거에요 -> 렌더러 개수를 반환하는 기능을 (null이면 0)
#endregion
#region Constructor
///
/// RenderGroup 생성자
///
/// 대상 Renderer 배열 (null 체크 수행)
public RenderGroup(Renderer[] renderers) // 생성자를 실행할거에요 -> 렌더러 배열을 인자로 받아서
{
if (renderers == null || renderers.Length == 0) // 조건이 맞으면 실행할거에요 -> 렌더러가 비어있거나 없다면
{
Debug.LogError("[RenderGroup] Renderer 배열이 null 또는 비어있습니다."); // 에러를 출력할거에요 -> 로그 메시지를
_renderers = System.Array.Empty(); // 값을 할당할거에요 -> 빈 렌더러 배열을
_actualCenter = Vector3.zero; // 값을 할당할거에요 -> 제로 벡터를 중심점으로
_boundingRadius = 0f; // 값을 할당할거에요 -> 0을 반지름으로
return; // 중단할거에요 -> 초기화 로직을
}
// 방어적 복사 (외부에서 배열 수정 방지)
_renderers = new Renderer[renderers.Length]; // 배열을 생성할거에요 -> 같은 크기의 새로운 렌더러 배열을
System.Array.Copy(renderers, _renderers, renderers.Length); // 값을 복사할거에요 -> 원본 렌더러들을 내부 배열로
// 실제 중심점 계산
_actualCenter = CalculateGroupCenter(_renderers); // 값을 계산해서 넣을거에요 -> 그룹의 통합 중심점을
// 경계 반지름 계산
_boundingRadius = CalculateBoundingRadius(_renderers, _actualCenter); // 값을 계산해서 넣을거에요 -> 최대 경계 반지름을
_isCurrentlyVisible = true; // 초기값을 설정할거에요 -> 표시 상태를 참(true)으로
}
#endregion
#region Public Methods
///
/// 거리 기반으로 렌더링 상태 업데이트
///
/// 기준 위치 (플레이어 등)
/// 최대 렌더링 거리
/// 상태가 변경되었는지 여부
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; // 반환할거에요 -> 상태가 변경되었음을
}
///
/// 강제로 모든 Renderer 표시
///
public void ForceShow() // 함수를 선언할거에요 -> 강제 표시 기능인 ForceShow를
{
SetRenderersState(true); // 함수를 실행할거에요 -> 모든 렌더러를 켜는 기능을
_isCurrentlyVisible = true; // 상태를 설정할거에요 -> 표시 중인 것으로
}
///
/// 강제로 모든 Renderer 숨김
///
public void ForceHide() // 함수를 선언할거에요 -> 강제 숨김 기능인 ForceHide를
{
SetRenderersState(false); // 함수를 실행할거에요 -> 모든 렌더러를 끄는 기능을
_isCurrentlyVisible = false; // 상태를 설정할거에요 -> 숨김 중인 것으로
}
///
/// 그룹 유효성 검사 (Renderer가 파괴되었는지 확인)
///
public bool IsValid() // 함수를 선언할거에요 -> 유효성 검사 기능인 IsValid를
{
if (_renderers == null || _renderers.Length == 0) // 조건이 맞으면 반환할거에요 -> 배열이 비어있다면 거짓(false)을
return false; // 반환할거에요 -> 유효하지 않음을
// 하나라도 유효한 Renderer가 있으면 true
foreach (Renderer r in _renderers) // 반복할거에요 -> 배열 내 모든 렌더러에 대해
{
if (r != null) return true; // 조건이 맞으면 반환할거에요 -> 살아있는 객체가 있다면 참(true)을
}
return false; // 반환할거에요 -> 모두 파괴되었다면 거짓을
}
#endregion
#region Private Methods
///
/// 여러 Renderer의 통합 중심점 계산
///
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; // 반환할거에요 -> 계산된 중심점을 (없으면 제로 벡터)
}
///
/// 오브젝트의 최대 반지름 계산
///
private static float CalculateBoundingRadius(Renderer[] renderers, Vector3 center) // 함수를 선언할거에요 -> 반지름을 계산하는 정적 기능을
{
float maxRadius = 0f; // 변수를 초기화할거에요 -> 최대 반지름 수치를 0으로
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; // 반환할거에요 -> 최종 계산된 최대 반지름을
}
///
/// 모든 Renderer의 enabled 상태 일괄 변경
///
private void SetRenderersState(bool enabled) // 함수를 선언할거에요 -> 렌더러 상태를 일괄 변경하는 기능을
{
for (int i = 0; i < _renderers.Length; i++) // 반복할거에요 -> 배열의 인덱스만큼
{
Renderer r = _renderers[i]; // 객체를 가져올거에요 -> i번째 렌더러를
// null 체크 + 상태가 다를 때만 변경 (성능 최적화)
if (r != null && r.enabled != enabled) // 조건이 맞으면 실행할거에요 -> 렌더러가 존재하고 상태 변경이 필요하다면
{
r.enabled = enabled; // 설정을 바꿀거에요 -> 활성화 여부를 인자값대로
}
}
}
#endregion
#region Cleanup
///
/// 리소스 정리 (명시적 호출용)
///
public void Dispose() // 함수를 선언할거에요 -> 자원 정리 기능을
{
// Renderer 배열 초기화 (GC 도움)
System.Array.Clear(_renderers, 0, _renderers.Length); // 비울거에요 -> 렌더러 배열의 모든 요소를
}
#endregion
}
}