24/03/27
36화. Win32API Animation (1)
게임 제작을 위해 추가해야할 마지막 필수요소인 애니메이션을 구현해보자
CAnimator 클래스 :
Animation 도 Collider와 마찬가지로 Component 로 만들거다. (CAnimator 클래스)
Component 로 만드는 이유는 해당 기능을 가질 수동 있고 안 가질수도 있기 때문이다.
CAnimator 객체를 소유하는 Object 객체가 사용할 애니메이션들을 관리하는 매니저 역할을 한다.
복사 생성자 와 깊은 복사 :
Collider와 마찬가지로 Animator도 포인터(주소) 타입 멤버 변수이기에 복사생성자를 통해 Object 객체가 생성될 때, "깊은 복사"를 통해 복사되어야 한다.
Animator 클래스 작성완료되면 마무리 작업한다. 우선 구조만 잡아둠
메모리 해제 :
리소스 매니저(ResMgr) 클래스가 관리하고 있던 Texture 객체들을 자신이 삭제될때 메모리 해제를 해주듯이 Animator 클래스도 관리하는 Animation 객체들을 자신이 삭제될때 함께 메모리 해제 해주어야한다.
map 자료구조의 메모리 해제 작업이 자주 발생하기에 Vector 와 마찬가지로 템플릿 전역함수를 생성해서 해당 작업을 대체해준다.
참고로 template 안에서 inner class 를 사용하려면 "typename" 키워드를 붙여줘야한다. (VS2015부터 적용된 규칙)
전역함수 friend 선언 :

CTexture 클래스의 생성을 CResMgr 에서만 할 수 있도록 하기위해 생성자와 소멸자를 private 접근 제한자로 막아두었더니 Safe_Delete_Map() 전역 템플릿 함수에서 접근을 할 수가 없다.
이럴땐 전역함수를 friend 선언해주면 되는데 코드는 아래와 같다.
// 템플릿 전역함수 Safe_Delete_Map() 의 원형
template<typename T1, typename T2>
void Safe_Delete_Map(map<T1, T2>& _map)
{
typename map<T1, T2>::iterator iter = _map.begin();
for (; iter != _map.end(); ++iter)
{
if (nullptr != iter->second)
delete iter->second;
}
_map.clear();
}
// friend 선언
template<typename T1, typename T2>
friend void Safe_Delete_Map(map<T1, T2>& _map);

37화. Win32API Animation (2)
CreateComponent() 함수 :
Object 클래스에 CreateAnimator() 함수 추가
Collider 와 마찬가지로 Object 객체에서 필요시 Component 소유하기 위해 사용
component_render() :
Component 객체들의 render() 함수 호출해주는 코드 잊지 말고 추가
CAnimator::update() / render() :
Animator 클래스에 현재 재생중인 애니메이션의 주소값을 저장하는 m_pCurAnim 변수 추가
Animator 클래스는 매 프레임마다 update() 를 받는다.
Animator::update() 에서는 m_pCurAnim 에 저장되어 있는 현재 재생중인 애니메이션에 대한 정보를 업데이트해준다.
즉, 매 프레임마다 m_pCurAnim의 Animation::update() 함수를 호출해준다.
Animator::render() 에서는 m_pCurAnim 의 Animation::render() 함수를 호출해준다.
Object - Animator - Animation 으로 이어지는 애니메이션 구조 설계 끝
CAnimator::CreateAnimation() :
애니메이션을 생성하는 함수
void CreateAnimation(const wstring& _strName, CTexture* _pTex, Vec2 _vLT, Vec2 _vSliceSize, Vec2 _vStep, float _fDuration, UINT _iFrameCount);
// const wstring& _strName : 애니메이션의 이름
// CTexture* _pTex : 생성 시키려는 애니메이션 이미지가 들어 있는 Texture
// Vec2 _vLT : Texture에서 애니메이션으로 만드려는 이미지가 있는 Left-Top 위치
// Vec2 _vSliceSize : 애니메이션을 이루는 이미지들중 한장의 크기 (width * height)
// Vec2 _vStep : 애니메이션의 다음장면에 해당하는 이미지의 LT위치에 도달하기위한 이동거리
// float _fDuration : 각 프레임 재생 시간
// UINT _iFrameCount : 애니메이션을 이루는 프레임의 개수
애니메이션이 만들어지는 과정 :
Animator::CreateAnimation() 함수가 호출되면...
우선, 해당 이름(_strName)으로 이미 존재하는 애니메이션이 있는지 확인해준다.
이후 애니메이션을 생성하여 이름과 해당 애니메이션을 재생시켜줄 Animator 객체를 등록해준다.
Animation::Create() 함수에서는 애니메이션을 이루는 프레임들에 대한 정보를 Animation::tAnimFrm 구조체에 등록 및 저장해준다.
마무리 작업으로 생성된 애니메이션 객체를 Animator가 관리하는 애니메이션으로 등록해준다.
프레임 정보 구조체 :
애니메이션을 구성하는 각 프레임에 대한 정보
Animation 클래스는 프레임정보를 vector 컨테이너에 담아서 관리한다.
24/03/28
38화. Win32API Animation (3)
지난 강의 내용 :
이전 강의에서 애니메이션을 이루는 10장의 프레임에 대한 정보를 채워 Animation::m_vecFrm 컨테이너에 저장했다.
한 프레임 출력 :
해당 프레임들 중 특정 한 프레임의 이미지를 출력하는 작업을 해보자
Animator 클래스가 Component 형태로 Object 클래스에 필요시 포함될 수 있기에, 더이상 Player 객체는 사용할 이미지를 출력해주기위해서 render(HDC) 함수에서 BitBlt(), TransparentBlt() 와 같은함수를 사용하지 않아도 된다.
또한 멤버로 Texture 객체를 가지고 있지 않아도 된다.
해당 작업들은 Animator, Animation 클래스들이 대신 해줄거다.
Player - Animator - Animation 으로 이어지는 렌더링 과정을 통해서 Animation의 첫번째 프레임 장면이 출력된다.
애니메이션의 현재 프레임의 값을 0으로 초기화 해두었기에 현재 에니메이션의 첫번째 프렘이이 화면에 출력되는 것을 확인할 수 있다.
현재 프레임을 다른 값으로 수정하면 초기화된 값에 해당하는 프레임이 출력된다.
애니메이션 기능 구현 :
애니메이션이 구현되기 위해선 Animation::m_vecFrm 컨테이너에 저장되어 있는 프레임들이 순차적으로 출력되어야한다.
그러기 위해선 Animation의 update() 에서 시간에 따라 현재 프레임의 값을 변경해줘야한다.
Animation 클래스에 멤버변수로 m_fAccTime 을 추가해준다.
현재 프레임의 재생 누적시간을 저장하는 역할을 담당한다.
update() 함수에서 Delta Time 의 값을 계속 m_fAccTime 변수에 누적시켜준다.
누적된 시간이 Animation을 생성할때 지정해준 프레임당 재생시간을 초과한다면
현재프레임을 다음 프레임으로 진행시켜주고 누적시간을 초기화해준다.
Animation::update() 함수는 Animator::update() 함수에 의해 호출되고 Animator::update() 는 소유자인 CObject에 의해 호출되어야한다.
현재 Player 객체의 애니메이션을 출력하려고 하는거니 Player()::update() 에 Animator의 update() 함수 호출 코드를 추가한다.
문제점 :
사용한 Texture가 일정한 간격으로 배치가 되어 있어야 사용할 수 있는 애니메이션 기법이다.
실제 Texture 는 일정하게 배치되어 있지 않은 경우가 대부분이다.
때문에 두가지의 방법이 있다.
- 원하는 이미지들을 구해서 직접 노가다로 이미지를 편집한다.
- 프로그래밍으로 균일하게 배치된 이미지가 아니더라도 사용할 수 있게 지원해준다.
편집된 이미지를 사용했다는 전제로 코드 보완하기 :
한번만 재생하고 싶은 경우...
현재 구현된 코드는 무한정 반복 재생되고 있다.
하지만, 만약 반복재생이 되길 원친 않은 경우라면...?
Animation 클래스에 애니메이션이 모든 프레임을 한번 재생완료 했음을 알리는 플래그 값(m_bFinish) 을 추가해준다.
Animation::update(), render() 의 코드를 수정한다.
update() 함수는 기존에는 마지막 프레임이 넘어가는 경우 현재 프레임을 다시 0으로 되돌렸지만,
현재 프레임을 "-1" 재생완료 여부를 true 로 바꿔주는 코드로 변경한다.
// 마지막 프레임을 넘어가는 경우
if (m_vecFrm.size() <= m_iCurFrm)
{
// 기존 코드
//m_iCurFrm = 0;
// 바뀐 코드
m_iCurFrm = -1;
m_bFinish = true;
}
또한, update(), render() 함수 둘 모두 처음에 조건문을 추가하여 만약 m_bFinsh 가 true 이면 함수가 바로 종료되도록 한다.
if (m_bFinish)
return;
코드 캡쳐 :


이렇게 되면 애니메이션이 한번 재생완료되면 화면에서 사라지게 된다.
현재 재생중인 애니메이션을 설정하는 CAnimator::Play() 함수에 애니메이션 반복여부를 설정해주는 인자를 추가한다.
Animator 에서 Animation 의 update()를 호출하여 애니메이션의 데이터를 갱신 한 후,
재생완료 여부를 확인하고 만약 현재 프레임이 반복재생하도록 설정되어 있었다면 아래의 조건문을 만족하고
if (m_bRepeat && m_pCurAnim->IsFinish())
// m_bRepeat : 현재 애니메이션 반복재생 여부
// m_pCurAnim->IsFinish() : 현재 애니메이션 재생 완료 여부
현재 애니메이션을 SetFrame() 함수에 인자로 지정한 프레임으로 되돌려준다.
SetFrame(int) 함수는 현재 애니메이션의 재생완료여부(m_bFinish) 를 false, 현재 프레임(m_iCurFrm)을 인자로 들어온 값으로 그리고 현재 프레임 누적 재생 시간을 0.f 로 재설정 해준다.
디테일 작업 :
기존 코드는 애니메이션을 재생할때 프레임이 변경되면 프레임 누적 재생시간(m_fAccTime)을 "0"으로 초기화 해줬다.
하지만, Delta Time 을 누적받은 시간이 정확히 프레임 재생시간(fDuration)과 맞아 떯어질 일은 없다.
때문에 조건문도 "==" 가 아닌 ">"로 해둔거다.
때문에, 다음 애니메이션 프레임을 재생할때 사용될 프레임 재생시간은 "이전 프레임의 누적된 재생 시간 - 지정된 프레임 재생 시간" 으로 계산되어야 정확하다.
39화. Win32API Animation (4)
Offset :
애니메이션도 Collider와 마찬가지로 Object 위치 기준으로 Offset 값으로 위치 조정이 가능하게 기능 구현해보자
애니메이션의 각 프레임에 대한 정보를 나타내는 구조체 tAnimFrm 에 Offset 변수를 추가해준다.
애니메이션 렌더시 Object의 기준위치 값에 Offset 을 더해주는 연산을 추가한다.
CAnimation::GetFrame(int) :
특정 애니메이션의 특정 프레임 정보를 가져올 수 있는 함수
tAnimFrm& GetFrame(int _iIdx) { return m_vecFrm[_iIdx]; }
해당 함수를 호출한 곳에서 값을 수정할 수 있도록 하기위해 참조 값을 반환해준다.
실습 :
GetFrame() 함수로 애니메이션을 이루는 각 프레임에 대한 데이터에 접근한 후 offset 값을 수정해준다.
렌더링 결과를 보면 Collider의 중심이 아니라 위에 애니메이션이 출력되는걸 확인 할 수 있다.
Next Note
Win32API_StudyNote_9
coder-qussong.tistory.com
'Win32API' 카테고리의 다른 글
Win32API_StudyNote_10 (0) | 2024.03.29 |
---|---|
Win32API_StudyNote_9 (0) | 2024.03.28 |
Win32API_StudyNote_7 (0) | 2024.03.25 |
Win32API_StudyNote_6 (0) | 2024.03.24 |
Win32API_StudyNote_5 (0) | 2024.03.22 |