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

[Win32 API TBI 모작] 9. 몬스터 & State 패턴

Vfly 2025. 5. 30. 23:12

이번에는 몬스터와 관련된 모든 클래스들에 대해 알아보자.

 

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


아키텍처 구조

1. CMonFactory (Factory Pattern)

역할: 다양한 타입의 몬스터 객체를 생성하는 팩토리 클래스

 

주요 특징:

  • 정적 팩토리 메서드: CreateMonster() 메서드로 통합된 몬스터 생성 인터페이스 제공
  • 타입별 전용 생성 함수: 각 몬스터 타입별로 특화된 생성 로직 분리
  • 헬퍼 메서드: 공통 초기화 작업을 담당하는 유틸리티 함수들
 
// 메인 팩토리 메서드
static CMonster* CreateMonster(MON_TYPE _eType, Vec2 _vPos, Vec2 _vGridPos, int option = 0);

// 타입별 전용 생성 함수
static CMonster* CreateFlyMonster(Vec2 _vPos, Vec2 _vGridPos, int option);
static CMonster* CreateHorfMonster(Vec2 _vPos, Vec2 _vGridPos);
static CMonster* CreateBabyPlumMonster(Vec2 _vPos, Vec2 _vGridPos);

 

2. AI 시스템 (State Pattern)

역할: 몬스터의 행동을 상태별로 관리하는 AI 시스템

구성 요소:

  • AI 클래스: 상태 관리자 역할
  • CState 추상 클래스: 모든 상태의 베이스 클래스
  • 구체적 상태 클래스들: 각 몬스터별 특화된 행동 상태들
class AI {
    map<MON_STATE, CState*> m_mapstate;  // 상태 저장소
    CState* m_pCurState;                 // 현재 상태
    CMonster* m_pOwner;                  // 소유자 몬스터
};

 

 

구현된 몬스터 타입

1. Fly 몬스터

특성:

  • 빠른 이동 속성 (속도: 40.f)
  • 높은 인식 범위 (99999.f)
  • 옵션에 따른 다른 애니메이션 (일반/공격형)

상태: Idle → Trace → Dead

2. Horf 몬스터

특성:

  • 고정형 몬스터 (속도: 0.f)
  • 원거리 공격 (공격 범위: 800.f)
  • 중간 인식 범위 (200.f)

상태: Idle → Attack → Dead

3. Baby Plum 보스

특성:

  • 고체력 보스 몬스터 (HP: 1000)
  • 다양한 공격 패턴 (회전, 바운스, 급강하)
  • 복잡한 애니메이션 시스템

상태: Idle → Trace → Attack → Dead

 

핵심 설계 패턴

1. Factory Pattern 적용

CMonster* CMonFactory::CreateMonster(MON_TYPE _eType, Vec2 _vPos, Vec2 _vGridPos, int option)
{
    switch (_eType) {
        case MON_TYPE::FLY:
            return CreateFlyMonster(_vPos, _vGridPos, option);
        case MON_TYPE::HORF:
            return CreateHorfMonster(_vPos, _vGridPos);
        case MON_TYPE::BABY_PLUM:
            return CreateBabyPlumMonster(_vPos, _vGridPos);
    }
}

장점:

  • 객체 생성 로직의 캡슐화
  • 새로운 몬스터 타입 추가 시 확장성
  • 클라이언트 코드와 구체적 클래스의 분리

 

2. State Pattern 적용

class CState {
    virtual void update() = 0;
    virtual void Enter() = 0;
    virtual void Exit() = 0;
};

장점:

  • 몬스터 행동의 모듈화
  • 상태 전환 로직의 명확성
  • 새로운 행동 패턴 추가 용이성

 

 

몬스터별 상태 분석

Baby Plum (보스 몬스터)

상태 다이어그램

IDLE → TRACE → ATTACK → IDLE
  ↓              ↓
DEAD ←────────← DEAD

 

주요 상태들

 

1. CBabyPlumTraceState (추적 상태)

void CBabyPlumTraceState::update() {
    // HP 체크 및 게임오버 확인
    if (GetMonster()->Getinfo().m_iCurHP <= 0) {
        ChangeAIState(GetAI(), MON_STATE::DEAD);
        return;
    }
    
    // 공격 타이밍 체크 (3초마다)
    m_fAccTime += fDT;
    if (m_fAccTime > m_fAttackDuration) {
        ChangeAIState(GetAI(), MON_STATE::ATTACK);
        return;
    }
    
    // 플레이어 추적 로직
    Vec2 vMonDir = vPlayerPos - vMonsterPos;
    vMonDir.Normalize();
    vMonsterPos += vMonDir * pMonster->Getinfo().m_fSpeed * fDT;
}

 

2. CBabyPlumAttackState (공격 상태)

 

3가지 랜덤 공격 패턴을 구현:

enum class BABYPLUM_ATTACK_TYPE {
    TAKE_DOWN,    // 8방향 + 14방향 눈물 발사
    SPIN,         // 회전하며 12방향 눈물 발사
    BACK_BOUNCE   // 벽 튕기며 연속 눈물 발사
};

 

TAKE_DOWN 패턴 분석:

void CBabyPlumAttackState::CreateTakeDownTear() {
    // 8방향 고속 눈물
    for (int i = 0; i < 8; i++) {
        float angle = i * (PI / 4);
        Vec2 vDir = { cos(angle), sin(angle) };
        // 400 속도로 발사
    }
    
    // 14방향 저속 눈물
    for (int i = 0; i < 14; i++) {
        float angle = i * (2 * PI / 14);
        Vec2 vDir = { cos(angle), sin(angle) };
        // 200 속도로 발사
    }
}

 

BACK_BOUNCE 패턴의 정교한 구현:

void CBabyPlumAttackState::update() {
    // 방향 변화 감지 및 애니메이션 변경
    if (m_vPrevDir != vCurDir) {
        // 8가지 방향 전환에 따른 애니메이션 매핑
        if (m_vPrevDir == Vec2(1, -1) && vCurDir == Vec2(1, 1)) 
            GetMonster()->GetAnimator()->Play(L"babyplum_attack_backbounce_back_left", true, 1);
        // ... 8가지 케이스 처리
    }
    
    // 동적 속도 증가 (100 → 1200 over 7초)
    float speed = 100.f + (1200.f - 100.f) * (m_fAccTime / 7.f);
    if (speed > 1200.f) speed = 1200.f;
}

 

 

Horf 몬스터 (원거리 공격형)

CHorfAttackState 특징:

  • 정적 포탑형 몬스터 (속도 0)
  • 0.8초 쿨타임으로 정확한 타이밍 제어
  • 플레이어 위치 예측 공격
 
void CHorfAttackState::CreateTear() {
    // 플레이어 위치 기반 방향 계산
    Vec2 tearDir = (CPlayerMgr::GetInstance()->GetPlayer()->GetPos() - m_vMonsterPos).Normalize();
    
    // 몬스터 전용 눈물 생성
    CTear* attackTear = new CTear(m_vMonsterPos, GetMonster()->Getinfo().m_fAttRange, true);
    attackTear->SetDir(tearDir);
}

 

Fly 몬스터 (기본형)

 

단순하지만 효과적인 3단계 구조:

  • CFlyIdleState: 대기
  • CFlyTraceState: 추적
  • CFlyDeadState: 사망

 

구현의 핵심 기법

1. 상태 전환 메커니즘

void AI::ChangeState(MON_STATE _eNextState) {
    CState* pNextState = GetState(_eNextState);
    
    assert(m_pCurState != pNextState);  // 같은 상태 전환 방지
    m_pCurState->Exit();                // 현재 상태 종료 처리
    m_pCurState = pNextState;           // 상태 변경
    m_pCurState->Enter();               // 새 상태 진입 처리
}

 

2. Factory Pattern과의 결합

CMonster* CMonFactory::CreateBabyPlumMonster(Vec2 _vPos, Vec2 _vGridPos) {
    // AI 시스템 구성
    AI* pAI = new AI;
    pAI->AddState(new CBabyPlumIdleState);
    pAI->AddState(new CBabyPlumTraceState);
    pAI->AddState(new CBabyPlumAttackState);
    pAI->AddState(new CBabyPlumDeadState);
    pAI->SetCurState(MON_STATE::IDLE);
    
    pMon->SetAI(pAI);
}

 

3. 애니메이션과 상태의 동기화

void CBabyPlumTraceState::Enter() {
    GetMonster()->GetAnimator()->PauseAllAnimations();
    GetMonster()->GetAnimator()->Play(L"babyplum_idle", true, 1);
    m_fAccTime = 0.f;  // 타이머 초기화
}

 

 

 

코드 품질 분석

장점:

  1. 명확한 책임 분리: 각 클래스가 단일 책임을 가짐
  2. 확장성: 새로운 몬스터나 상태 추가가 용이
  3. 재사용성: 공통 기능의 함수화
  4. 가독성: 의미 있는 네이밍과 구조화

개선 가능 점:

  1. 예외 처리: assert 외의 런타임 예외 처리 강화
  2. 메모리 최적화: 스마트 포인터 도입 고려

 

어필 포인트

  1. 디자인 패턴 활용: Factory와 State 패턴의 실무적 적용
  2. 객체지향 설계: 캡슐화, 상속, 다형성의 적절한 활용
  3. 게임 개발 경험: 실제 게임 로직과 애니메이션 시스템 구현
  4. 코드 구조화: 유지보수성과 확장성을 고려한 설계
  5. C++ 숙련도: 현대적 C++ 기법과 메모리 관리 역량