[25.06.11] :: 가디언 앤 시커 프로젝트 12

chooha·2025년 6월 11일

가디언앤시커

목록 보기
12/25

📝 개발일지 - 배치 검증 시스템 함수 호출 최적화

👨‍💻 오늘의 개발 작업

오늘은 룬 시스템에서 마우스 호버 시 불필요한 함수 호출이 너무 많아서 성능에 영향을 주는 문제를 해결했음
처음엔 배치 검증 로직만 업데이트했는데, 함수 호출 체인을 분석해보니 텍스처 로드, 중복 검증 등 비효율적인 부분들이 많이 발견되어 전면적으로 리팩토링했음


💡 오늘의 5분 기록

1. 문제 확인 - 마우스 호버 시 과도한 함수 호출

배치 검증 로직 업데이트 후 함수 호출 순서를 추적해보니 비효율적인 부분들이 보였음

기존 함수 호출 체인 (마우스 호버 한 번당)

NativeOnMouseMove → UpdateGridPreview → FindOptimalPlacementPosition
├─ GetRuneShape (1회)
├─ PreviewRunePlacement (N회) ← 후보 검증용
│   ├─ GetFragmentedRuneTexture (N회) ← 텍스처 로드 중복!
│   └─ IsValidCell (N×M회)
└─ PreviewRunePlacement (1회) ← 최종 프리뷰용
    ├─ GetFragmentedRuneTexture (1회)
    └─ IsValidCell (M회)

예를 들어, L자 룬으로 테스트할 때 후보 3개 검증 + 최종 프리뷰 = 텍스처 로드 4회 발생

2. 원인 파악 - 불필요한 래퍼 함수와 중복 작업

코드를 자세히 분석해보니 여러 비효율적인 패턴들이 발견됨

문제 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;

3. 해결 과정 - 함수 분리와 호출 순서 최적화

단계별로 최적화 방안을 적용했음

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 함수 수정
  • 호출 부분들을 용도에 맞는 함수로 교체

4. 문제 해결 - 검증 순서 최적화

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;
    }
    
    // ... 나머지 배치 로직
}

5. 성과와 깨달은 점

최적화 전후 비교

Before (기존)

  • 마우스 호버당 텍스처 로드: 4회 (3회 후보 검증 + 1회 최종)
  • PlaceRune 텍스처 로드: 최대 2회 (중복 + 실패 시 낭비)
  • 불필요한 TArray<FIntPoint> 메모리 할당 다수

After (최적화)

  • 마우스 호버당 텍스처 로드: 1회 (최종 프리뷰만)
  • PlaceRune 텍스처 로드: 1회 (성공 확정 후에만)
  • 경량 검증으로 조기 실패 처리

룬 시스템에서 함수의 책임을 명확히 분리하는 것이 중요하다는 것을 깨달았음. 배치 검증과 시각적 프리뷰는 서로 다른 목적이므로 별도 함수로 처리해야 성능과 유지보수성을 모두 확보할 수 있음.

설계 개선사항

  • 경량 검증과 무거운 작업 분리
  • 조기 실패 처리로 불필요한 연산 방지
  • 불필요한 래퍼 함수 제거로 호출 체인 단순화

0개의 댓글