오늘은 룬 시스템에서 마우스 호버 시 불필요한 함수 호출이 너무 많아서 성능에 영향을 주는 문제를 해결했음
처음엔 배치 검증 로직만 업데이트했는데, 함수 호출 체인을 분석해보니 텍스처 로드, 중복 검증 등 비효율적인 부분들이 많이 발견되어 전면적으로 리팩토링했음
배치 검증 로직 업데이트 후 함수 호출 순서를 추적해보니 비효율적인 부분들이 보였음
기존 함수 호출 체인 (마우스 호버 한 번당)
NativeOnMouseMove → UpdateGridPreview → FindOptimalPlacementPosition
├─ GetRuneShape (1회)
├─ PreviewRunePlacement (N회) ← 후보 검증용
│ ├─ GetFragmentedRuneTexture (N회) ← 텍스처 로드 중복!
│ └─ IsValidCell (N×M회)
└─ PreviewRunePlacement (1회) ← 최종 프리뷰용
├─ GetFragmentedRuneTexture (1회)
└─ IsValidCell (M회)
예를 들어, L자 룬으로 테스트할 때 후보 3개 검증 + 최종 프리뷰 = 텍스처 로드 4회 발생
코드를 자세히 분석해보니 여러 비효율적인 패턴들이 발견됨
문제 1: CanPlaceRuneAt - 불필요한 래퍼 함수
bool UGS_ArcaneBoardManager::CanPlaceRuneAt(uint8 RuneID, const FIntPoint& Pos)
{
TArray<FIntPoint> AffectedCells; // 사용하지도 않음!
return PreviewRunePlacement(RuneID, Pos, AffectedCells);
}
문제 2: 텍스처 로드 중복
// FindOptimalPlacementPosition에서
GetRuneShape(RuneID, RuneShape) // TArray<FIntPoint>
// PreviewRunePlacement에서
GetFragmentedRuneTexture(RuneID, RuneShape) // TMap<FIntPoint, UTexture2D*>
단순 배치 검증에는 위치 정보만 필요한데 무거운 텍스처까지 로드하고 있었음
문제 3: PlaceRune에서 비효율적인 순서
// 1. 무거운 텍스처 로드 먼저
TMap<FIntPoint, UTexture2D*> RuneShape;
if (!GetFragmentedRuneTexture(RuneID, RuneShape)) return false;
// 2. 배치 검증 나중에 (실패하면 위의 로드가 낭비)
if (!CanPlaceRuneAt(RuneID, Pos)) return false;
단계별로 최적화 방안을 적용했음
1단계: 경량 검증 함수로 수정
// 텍스처 로드 없이 배치 가능 여부만 확인
bool CanPlaceRuneAt(uint8 RuneID, const FIntPoint& Position)
{
TArray<FIntPoint> RuneShape;
if (!GetRuneShape(RuneID, RuneShape)) return false;
for (const FIntPoint& Offset : RuneShape)
{
FIntPoint CellPos = Position + Offset;
if (!IsValidCell(CellPos)) return false;
}
return true;
}
2단계: FindOptimalPlacementPosition 최적화
// BEFORE: 후보 검증에 PreviewRunePlacement 사용 (텍스처 로드 포함)
if (PreviewRunePlacement(RuneID, CandidatePosition, AffectedCells))
// AFTER: 경량 함수로 대체
if (CanPlaceRuneAt(RuneID, CandidatePosition))
3단계: 불필요한 래퍼 함수 제거
CanPlaceRuneAt 함수 수정PlaceRune 함수의 호출 순서도 개선했음
최적화된 PlaceRune
bool UGS_ArcaneBoardManager::PlaceRune(uint8 RuneID, const FIntPoint& Pos)
{
// 1. 먼저 가벼운 배치 검증 (실패 시 빠른 리턴)
if (!CanPlaceRuneAtPositionLightweight(RuneID, Pos))
{
return false;
}
// 2. 배치 가능한 경우에만 무거운 텍스처 로드
TMap<FIntPoint, UTexture2D*> RuneShape;
if (!GetFragmentedRuneTexture(RuneID, RuneShape))
{
return false;
}
// ... 나머지 배치 로직
}
최적화 전후 비교
Before (기존)
TArray<FIntPoint> 메모리 할당 다수After (최적화)
룬 시스템에서 함수의 책임을 명확히 분리하는 것이 중요하다는 것을 깨달았음. 배치 검증과 시각적 프리뷰는 서로 다른 목적이므로 별도 함수로 처리해야 성능과 유지보수성을 모두 확보할 수 있음.
설계 개선사항