몬스터가 여러 동작을 수행할 때 애니메이션과, 동작에 대한 상태 변경에 대해서 간단하게 작성한 글입니다.
1. 애니메이션 자료구조
✅ AnimInfo
- 애니메이션 스프라이트의 상태 정보 저장용 구조체입니다.
typedef struct AnimInfo
{
int iFrameStartInit = 0; // 스프라이트의 시작 프레임
int iFrameEndInit = 0; // 스프라이트의 끝 프레임
int iFrameEnd = 0; // 스프라이트 이미지의 끝
int iCurrentFrame = 0; // 현재 스프라이트의 프레임 (열 정보)
int iMotion = 0; // 해당 스프라이트의 몇번째 줄을 사용할 것인지? (행 정보)
float fChangeTime = 0.f; // 현재 애니메이션이 어느정도 시간이 지났을 때 전환되는지.
bool bIsReverse = false; // 현재 스프라이트를 뒤집어서 실행할 것인지.
}ANIMINFO;
✅ AnimProcessInfo
- 애니메이션이 시간에 따라 동작하게 만들기 위한 정보.
typedef struct AnimProcessInfo
{
float fCurTime = 0.f; // 현재 애니메이션의 진행 시간.
int iAnimRepeatEndCnt = 0; // 애니메이션을 몇번 반복할 것인지? -1이면 무한 반복,
int iAnimRepeatCurCnt = 0; // 현재 몇번째 반복 중인지.
}ANIMPROCESS;
2. 애니메이션 처리 함수
int Class자료형::MoveAnimation(float fDeltaTime)
{
// 0. 애니메이션 진행 중임을 반환, 1. 애니메이션이 종료되었음을 반환.
if (!m_AnimInfo.bIsReverse)
{
// 스프라이트 역재생이 아닌 경우
// 1. AnimProcess의 CurTime에 fDeltaTime을 게임의 프레임마다 더해줍니다.
// 2. AnimProcess의 CurTime이 Animation Frame의 전환 시간보다 커질 경우.
// 애니메이션 프레임을 증가시켜줍니다.
// - 이때 Animation Frame이 마지막값에 도달했는지 체크하고, 마지막인 경우 아래 처리를 수행합니다.
// - 애니메이션이 무한 반복인 경우 => 프레임을 시작 값으로 다시 초기화 해줍니다. return 0;
// - 애니메이션이 반복이 끝난 경우 return 1;
// - 반복횟수가 안채워졌을 경우 => 프레임을 시작 값으로 다시 초기화 해줍니다. return 0;
// - 마지막이 아닌 경우에는 다음 프레임으로 전환합니다.
}
else
{
// 스프라이트 역재생인 경우.
}
return 0;
}
- 게임의 프레임마다 호출되어 애니메이션을 업데이트합니다.
- 주요 동작:
- 누적 시간(fCurTime)에 fDeltaTime을 더함.
- fCurTime이 fChangeTime을 넘으면 다음 프레임으로 이동.
- 종료 조건에 따라 반복 또는 종료 처리.
3. 상태 기반 몬스터 AI 설계
✅ 상태 클래스 (State 클래스 계층)
- 몬스터의 다양한 행동 패턴을 상태(State)로 나눠서 관리.
🔹 CSwordManState (SwordMan 상태에 대한 부모 클래스)
#pragma once
#include "CState.h"
#include "CSwordMan.h
class CSwordManState :
public CState
{
public:
enum STATE : uint8_t
{
STATE_SPAWN = 0,
STATE_IDLE,
STATE_WALK,
STATE_ATTACK,
STATE_HIT,
STATE_DEATH,
STATE_END
};
const STATE& GetState() { return m_eState; }
protected:
CSwordMan* m_pSwordMan;
STATE m_eState = STATE_END;
public:
CSwordManState(CSwordMan* pSwordMan) : m_pSwordMan(pSwordMan) {};
virtual ~CSwordManState() {};
};
✅ 상태 매니저 (CSwordManStateMgr)
- 현재 상태를 관리하고 전환하며, 상태 업데이트를 위임합니다.
#pragma once
#include "CSwordManState.h"
class CSwordManStateMgr
{
public:
CSwordManStateMgr(CSwordMan* pSwordMan) : m_pSwordMan(pSwordMan), m_pCurState(nullptr)
{
}
~CSwordManStateMgr()
{
SafeDelete(m_pCurState);
}
const CSwordManState::STATE& GetCurState() const { return m_pCurState->GetState(); }
void ChangeState(CSwordState* pNewState);
void Update();
void LateUpdate();
private:
CSwordMan* m_pSwordMan = nullptr;
CSwordManState* m_pCurState = nullptr;
};
- ChangeState: 현재 상태 제거 후 새로운 상태로 전환하고 Enter() 호출.
- Update / LateUpdate: 매 프레임마다 현재 상태의 함수 실행.
4. 예시: IDLE 상태 구현
✅ CSwordManStateIdle 클래스
- 몬스터가 대기 중일 때의 상태를 정의합니다.
🔹 Enter() 함수
void CSwordManStateIdle::Enter()
{
// IDLE상태에 진입하면 해야할 일
// 1. 애니메이션 초기화
// 2. 애니메이션 프로스세스 초기화
// 3. 이미지 DC 변경.
// 4. 현재 이미지 크기조정
// Animation 초기화.
m_AnimInfo.iCurrentFrame = 0;
m_AnimInfo.iFrameEnd = 0;
m_AnimInfo.iFrameStartInit = 0;
m_AnimInfo.iFrameEndInit = 0;
m_AnimInfo.iMotion = 0;
m_AnimInfo.fChangeTime = 0.f;
// 프로세스 초기화
m_AnimProcess.fCurTime = 0.f;
m_AnimProcess.iAnimRepeatCurCnt = 0;
m_AnimProcess.iAnimRepeatEndCnt = 0;
if (m_pSwordMan->GetDirection() == CSwordMan::DIRECTION::DIR_RIGHT) // 왼쪽 오른쪽 방향 확인
{
m_pFrameKey = m_pSwordMan->GetFrameKey(CSwordMan::ANIM_STATE::STATE_RIGHT);
}
else
{
m_pFrameKey = m_pSwordMan->GetFrameKey(CSwordMan::ANIM_STATE::STATE_LEFT);
}
HDC hMemDC = CGameMgr::GetBmpMgr()->Find_Img(m_pFrameKey);
if (hMemDC != nullptr)
{
m_pSwordMan->SetNewImageDC(hMemDC);
}
m_fPatternTime = 0.2f;
m_pSwordMan->Set_ImageSize({ SWORDMAN_X, SWORDMAN_Y });
m_pSwordMan->SetNewAnimInfo(m_AnimInfo);
m_pSwordMan->SetNewAnimProcess(m_AnimProcess);
m_pSwordMan->SetCanHit(true);
m_pSwordMan->Set_Speed(0.f);
}
🔹 Update() 함수
void CSwordManStateIdle::Update()
{
if (m_fPatternTime > 0.f)
{
m_fPatternTime -= DELTA_TIME;
}
else
{
// 시간이 지나면 WALK 상태로 전환
m_pSwordMan->GetSwordStateMgr()->ChangeState(new CSwordStateWalk(m_pSwordMan));
}
}
5. 전체 흐름 정리
[ 게임 프레임마다 실행되는 흐름 ]
1. CSwordMan::LateUpdate()
→ SwordStateMgr->Update()
→ 현재 State의 Update()
→ 애니메이션 또는 행동 처리