WinApi/TBI(더 바인딩 오브 아이작) 모작

[Win32API TBI 모작] 11. Scene & 전투 화면

Vfly 2025. 6. 2. 23:21

이번에는 전투화면을 담당하는 클래스 CScene_Fight에 대해 알아보자.

 

전체 소스코드 : https://github.com/vfly1189/TBI


화면 구성

입장 시

 

보스 방
사망시

 

 

🏗️ 클래스 구조 및 설계

주요 멤버 변수

// 렌더링 관리
Direct2DMgr* pD2DMgr;

// UI 패널들
CPanelUI* statPanel;           // 스탯 표시 패널
CPanelUI* hpPanel;             // 체력 UI 패널  
CPanelUI* activeItemPanel;     // 활성 아이템 패널
CPanelUI* pickUpItemPanel;     // 획득 아이템 패널
CPanelUI* miniMapPanel;        // 미니맵 패널

// 데이터 매핑 컨테이너
unordered_map<StatType, CSpriteUI*> m_mapStatTexts;
unordered_map<PickUpType, CSpriteUI*> m_mapPickUpText;
vector<CSpriteUI*> m_vecHearts;

// 상태 관리
PlayerStat m_stPrevPlayerStat;  // 이전 프레임 플레이어 스탯
PickUp m_stPrevPickups;         // 이전 프레임 픽업 아이템
bool m_bGameOver;               // 게임 오버 상태

 

 

🎮 핵심 시스템 분석

1. 씬 생명주기 관리

Enter() - 씬 초기화

void CScene_Fight::Enter()
{
    // 1. 해상도 및 기본 설정
    Vec2 vResolution = CCore::GetInstance()->GetResolution();
    Setup();
    
    // 2. 맵 생성 및 오브젝트 배치
    MapMgr::GetInstance()->MapGenerate();
    
    // 3. 플레이어 설정
    CPlayer* player = CPlayerMgr::GetInstance()->GetPlayer();
    player->SetPos(Vec2(0.f, 0.f));
    
    // 4. 충돌 시스템 설정
    CCollisionMgr::GetInstance()->CheckGroup(GROUP_TYPE::PLAYER, GROUP_TYPE::ITEM);
    
    // 5. UI 시스템 초기화
    CreateStatUI();
    CreateHpUI();
    CreateActiveItemUI();
    CreatePickUpUI();
    CreateMiniMap();
    CreateBossHpBar();
}

 

특징:

  • 컴포넌트 기반 설계: UI, 충돌, 사운드 등 각 시스템이 독립적으로 관리
  • 계층적 오브젝트 관리: 그룹별 오브젝트 분류 및 관리

 

2. 동적 UI 시스템

스탯 UI 생성 - 데이터 주도 설계

void CScene_Fight::CreateStatUI()
{
    // 아이콘 분할 데이터 구조화
    struct IconSplitData {
        LPCWSTR name;
        D2D1_POINT_2F start;
        D2D1_POINT_2F end;
    };
    
    IconSplitData iconData[] = {
        {L"stat_attack_icon",       {0, 0},   {16, 16}},
        {L"stat_move_speed_icon",   {16, 0},  {32, 16}},
        // ...
    };
    
    // UI 생성 파라미터 구조화
    struct StatUIParams {
        float yOffset;
        LPCWSTR iconName;
        float statValue;
        bool isFirstPanel = false;
    };
    
    // 반복문을 통한 UI 생성
    for (const auto& param : statParams) {
        CPanelUI* statItem = statPanel->AddChild<CPanelUI>(Vec2(0.f, param.yOffset));
        // UI 요소 생성 및 설정...
    }
}

설계 패턴:

  • 템플릿 메서드 패턴: 공통 UI 생성 로직의 재사용
  • 데이터 주도 설계: 구조체 배열을 통한 UI 파라미터 관리
  • 컨테이너 매핑: unordered_map을 통한 UI 요소 빠른 접근

 

실시간 UI 업데이트 시스템

void CScene_Fight::UpdateStatUI()
{
    // 업데이트 정보 배열화
    StatUpdateInfo stats[] = {
        {StatType::Attack, current.m_fAttackDmg, &m_stPrevPlayerStat.m_fAttackDmg, 1.0f},
        // ...
    };
    
    for (auto& stat : stats) {
        if (abs(stat.currentValue - *stat.prevValue) > FLT_EPSILON) {
            auto it = m_mapStatTexts.find(stat.type);
            if (it != m_mapStatTexts.end()) {
                wchar_t buffer[20];
                swprintf_s(buffer, L"%.2f", stat.currentValue * stat.multiplier);
                it->second->GetTextUI()->SetText(buffer);
            }
        }
    }
}

최적화 기법:

  • 더티 체킹: 값 변경 시에만 UI 업데이트
  • 부동소수점 비교: FLT_EPSILON 활용한 안전한 실수 비교
  • 효율적 검색: unordered_map을 통한 O(1) UI 요소 접근

 

3. 동적 비트맵 생성 - 미니맵 시스템

void CScene_Fight::CreateMiniMapOriginal()
{
    // 1. 동적 렌더 타겟 생성
    ID2D1BitmapRenderTarget* pGridRenderTarget = nullptr;
    HRESULT hr = pD2DMgr->GetRenderTarget()->CreateCompatibleRenderTarget(
        D2D1::SizeF(cols * 18.f + 54.f * 2.f, rows * 16.f + 48.f * 2.f),
        &pGridRenderTarget
    );
    
    // 2. 그리드 기반 이미지 합성
    for (UINT y = 0; y < rows; ++y) {
        for (UINT x = 0; x < cols; ++x) {
            if (gridMap[y][x] == 0) continue;
            
            // 셀 타입별 다른 이미지 렌더링
            if (cellMaps[y][x]->IsChecked()) {
                pGridRenderTarget->DrawBitmap(pCellImage2, destRect, ...);
            } else {
                pGridRenderTarget->DrawBitmap(pCellImage, destRect, ...);
            }
        }
    }
    
    // 3. 생성된 비트맵 저장
    pD2DMgr->StoreCreateMap(pGridBitmap, L"minimap_grid");
}

 

기술적 하이라이트:

  • 동적 텍스처 생성: 런타임에 맵 상태에 따른 미니맵 텍스처 생성
  • Direct2D 활용: 하드웨어 가속 기반의 효율적인 비트맵 조작
  • 메모리 관리: 자동 해제 및 재사용을 통한 메모리 최적화

 

4. 최적화된 렌더링 시스템

뷰포트 컬링 구현

void CScene_Fight::update()
{
    Vec2 vLookAt = CCamera::GetInstance()->GetLookAt();
    Vec2 vLookAtLT = vLookAt - Vec2(480.f, 270.f);
    Vec2 vLookAtRB = vLookAt + Vec2(480.f, 270.f);
    
    for (UINT typeIDX = 0; typeIDX < (UINT)GROUP_TYPE::END; typeIDX++) {
        for (size_t objIDX = 0; objIDX < m_arrObj[typeIDX].size(); objIDX++) {
            // 정적 오브젝트에 대한 뷰포트 컬링
            if (typeIDX == (UINT)GROUP_TYPE::TILE || 
                typeIDX == (UINT)GROUP_TYPE::WALL) {
                
                Vec2 vPos = m_arrObj[typeIDX][objIDX]->GetFinalPos();
                if ((vPos.x > vLookAtLT.x && vPos.x < vLookAtRB.x) &&
                    (vPos.y > vLookAtLT.y && vPos.y < vLookAtRB.y)) {
                    m_arrObj[typeIDX][objIDX]->update();
                }
            }
        }
    }
}

성능 최적화:

  • 뷰포트 컬링: 화면에 보이는 오브젝트만 업데이트
  • 오브젝트 타입별 분기: 정적/동적 오브젝트 구분 처리

 

 

🛠️ 사용된 디자인 패턴

1. 싱글톤 패턴

  • Direct2DMgr, CPlayerMgr, MapMgr 등 전역 관리자 클래스들

2. 컴포넌트 패턴

  • UI 요소들을 독립적인 컴포넌트로 구성

3. 옵저버 패턴

  • 스탯 변경 시 UI 자동 업데이트

4. 팩토리 패턴

  • UI 생성 함수들에서 타입별 객체 생성

5. 상태 패턴

  • 게임 오버, 보스전 등 상태별 동작 분기