WIN32API FrameWork/한단계씩 직접 구현

33. 이미지의 애니메이션화를 위한 준비

hyrule 2022. 5. 25. 14:20

http://hyrule.tistory.com/111 

 

*** 공부 방법 ***

1. 코딩을 해야 하는 부분은 첫 부분에 변수나 함수, 메소드에 대한 선언이 코드블럭으로 표시되어 있다. //ex) MakeFunction(); 2. 코드블럭 하단에는 해당 선언에 대한 구현 로직이 작성되어 있다. 처

hyrule.tistory.com

 


- 보통 2D 애니메이션을 구현하는 방법으로는 두 가지가 있다.

-1. 스프라이트 아틀라스 

-> 이런 식으로 연속적인 이미지를 한 장의 파일에 다 때려박은 뒤, 좌표를 이용하여 한장씩 로딩하는 방식이다.

 

-2. 프레임

-> 이런 식으로 한장 한장 로딩해서 재생하는 방식이다.

 

- 이 두가지 방식 모두 가능하게 구현을 해보자.


- 가장 먼저, 들어온 이미지 파일이 둘 중 어떤 타입인지를 구분할 수 있도록 해주어야 한다.

- Flags.h 헤더 파일을 만들고, 거기에 로드할 이미지 파일이 어떤 타입인지를 구분할 수 있는 열거체를 선언 및 정의한다.

- 이 헤더파일을 GameInfo.h 헤더 파일에 포함시켜준다.

더보기
//flags.h

#pragma once

enum class ETexture_Type
{
	Sprite,
	Frame
};

- 그리고 CTexutre도 여러 장의 텍스처 파일을 저장할 수 있게 전체적인 수정을 해주어야 한다.

-- 우선, 방금 선언한 열거체를 변수로 들고있게 해준다.(m_TextureType) 기본값은 Sprite

-- 그리고, 기존의 이미지 정보를 들고있던 변수들을 구조체로 묶는다.(struct ImageInfo)

--> 변수 초기화 및 소멸도 구조체 안에서 진행한다.

더보기
struct ImageInfo
{
    HDC     hMemDC;
    HBITMAP hBmp;
    HBITMAP hPrevBmp;
    BITMAP  BmpInfo;

    ImageInfo() :
        hMemDC(0),
        hBmp(0),
        hPrevBmp(0),
        BmpInfo{}
    {
    }

    ~ImageInfo()
    {
        // 도구를 원래대로 돌려준다.
        SelectObject(hMemDC, hPrevBmp);
        DeleteObject(hBmp);
        DeleteDC(hMemDC);
    }
};

-- 이렇게 해주고 CTexture 안에서는 해당 구조체를 배열에 들고있게 해주면 이미지 파일을 동시에 여러개 들고있을 수 있게 된다.

더보기
private:
    ETexture_Type   m_Type;
    std::vector<ImageInfo*> m_vecImageInfo;

- 클래스 내부의 모든 메소드도 여러가지 수정 및 추가가 필요하다.

 

-- 우선 모든 메소드는 위의 변경에 맞게 수정해주어야 한다.

 

-- GetDC() 메소드는, 이제 인자로 배열 번호를 집어넣어서 해당 배열 번호에 해당하는 hMemDC를 반환받는다.

더보기
public:
    HDC GetDC(int Index = 0) const
    {
        return m_vecImageInfo[Index]->hMemDC;
    }

-- LoadTexture()은, LoadTexture() 메소드와 LoadTextureFullPath() 메소드로 분활한다.

--- LoadTexture()에서 파일 이름과 경로를 인자로 받으면, Full Path를 만들어 LoadTextureFullPath에 전달한다.

--- LoadTextureFullPath()에서는 해당 경로를 가지고 그대로 로드하면 된다.

---- 그냥 LoadTexture() 메소드를 반으로 자르면 됨

더보기
bool CTexture::LoadTexture(const TCHAR* FileName, 
	const std::string& PathName)
{
	const PathInfo* Path = CPathManager::GetInst()->FindPath(PathName);

	TCHAR	FullPath[MAX_PATH] = {};

	if (Path)
		lstrcpy(FullPath, Path->Path);

	lstrcat(FullPath, FileName);

	return LoadTextureFullPath(FullPath);
}

bool CTexture::LoadTextureFullPath(const TCHAR* FullPath)
{
	// 화면DC를 넣고 메모리 DC를 얻는다.
	HDC	hDC = CreateCompatibleDC(CGameManager::GetInst()->GetWindowDC());

	// 비트맵을 로딩한다.
	HBITMAP	hBmp = (HBITMAP)LoadImage(CGameManager::GetInst()->GetWindowInstance(),
		FullPath, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);

	if (!hBmp)
		return false;

	// 읽어온 비트맵을 메모리 DC에 지정한다.
	// 기존에 DC가 가지고 있던 도구를 반환한다.
	HBITMAP hPrevBmp = (HBITMAP)SelectObject(hDC, hBmp);

	BITMAP	BmpInfo;

	GetObject(hBmp, sizeof(BITMAP), &BmpInfo);

	ImageInfo* Info = new ImageInfo;

	Info->hMemDC = hDC;
	Info->hBmp = hBmp;
	Info->hPrevBmp = hPrevBmp;
	Info->BmpInfo = BmpInfo;

	m_vecImageInfo.push_back(Info);

	return true;
}

-- 또한, 아까 설명했던 Frame 형식의 애니메이션 이미지 여러 장을 동시에 로드할 수 있는 LoadTexture() 기능도 구현해야 한다.

--- 이 LoadTexture() 메소드를 호출한 경우, 이미지 여러장을 애니메이션화 시키겠다는 의미이므로 ETextureType을 기본값인 Sprite에서 Frame으로 변경해주어야 한다. 

--- 이 경우, LoadTexture()은 파일 이름에 대한 인자로 위처럼 TCHAR이 아닌 '문자열이 담긴 벡터의 레퍼런스'를 받는다.

---> 해당 벡터에는 애니메이션에 쓰일 파일들의 이름이 순차적으로 넣어서 전달한다.

---- 이 때, 문자 집합이 유니코드인지 멀티바이트인지 #ifdef로 확인하고, 해당 문자 집합에 따라 '문자열이 담긴 벡터의 레퍼런스'를 다르게 받도록 메소드를 나눠놓는 작업도 필요하다.

----- UNICODE -> wstring, else -> string

 

--- 받은 파일명의 문자열 벡터를 순회돌면서 경로를 집어넣는다.

CF) std::(w)string은 문자열의 대입과 덧셈이 가능하므로 활용하자

((w)stirng 변수).c_str -> 기존의 char 형식으로 문자열을 반환해주는 함수

더보기
#ifdef UNICODE

bool CTexture::LoadTexture(const std::vector<std::wstring>& vecFileName,
	const std::string& PathName)
{
	m_Type = ETexture_Type::Frame;

	const PathInfo* Path = CPathManager::GetInst()->FindPath(PathName);

	std::vector<std::wstring>	vecFullPath;

	size_t	Size = vecFileName.size();

	// 미리 개수만큼 push 해놓는 효과이다.
	vecFullPath.resize(Size);

	// 미리 배열 공간을 확보하여 꽉 찼을때 공간 재할당이 일어나는것을 방지해주는
	// 역할을 할때 사용한다.
	//vecFullPath.reserve(Size);

	for (size_t i = 0; i < Size; ++i)
	{
		if (Path)
			vecFullPath[i] = Path->Path;

		vecFullPath[i] += vecFileName[i];
	}

	return LoadTextureFullPath(vecFullPath);
}

bool CTexture::LoadTextureFullPath(const std::vector<std::wstring>& vecFullPath)
{
	m_Type = ETexture_Type::Frame;

	size_t	Size = vecFullPath.size();

	for (size_t i = 0; i < Size; ++i)
	{
		// 화면DC를 넣고 메모리 DC를 얻는다.
		HDC	hDC = CreateCompatibleDC(CGameManager::GetInst()->GetWindowDC());

		// 비트맵을 로딩한다.
		// string 이나 wstring 클래스의 c_str() 함수는 문자열 포인터를 얻어온다.
		HBITMAP	hBmp = (HBITMAP)LoadImage(CGameManager::GetInst()->GetWindowInstance(),
			vecFullPath[i].c_str(), IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);

		if (!hBmp)
			return false;

		// 읽어온 비트맵을 메모리 DC에 지정한다.
		// 기존에 DC가 가지고 있던 도구를 반환한다.
		HBITMAP hPrevBmp = (HBITMAP)SelectObject(hDC, hBmp);

		BITMAP	BmpInfo;

		GetObject(hBmp, sizeof(BITMAP), &BmpInfo);

		ImageInfo* Info = new ImageInfo;

		Info->hMemDC = hDC;
		Info->hBmp = hBmp;
		Info->hPrevBmp = hPrevBmp;
		Info->BmpInfo = BmpInfo;

		m_vecImageInfo.push_back(Info);
	}

	return true;
}

#else

bool CTexture::LoadTexture(const std::vector<std::string>& vecFileName,
	const std::string& PathName)
{
	m_Type = ETexture_Type::Frame;

	const PathInfo* Path = CPathManager::GetInst()->FindPath(PathName);

	std::vector<std::string>	vecFullPath;

	size_t	Size = vecFileName.size();

	// 미리 개수만큼 push 해놓는 효과이다.
	vecFullPath.resize(Size);

	// 미리 배열 공간을 확보하여 꽉 찼을때 공간 재할당이 일어나는것을 방지해주는
	// 역할을 할때 사용한다.
	//vecFullPath.reserve(Size);

	for (size_t i = 0; i < Size; ++i)
	{
		if (Path)
			vecFullPath[i] = Path->Path;

		vecFullPath[i] += vecFileName[i];
	}

	return LoadTextureFullPath(vecFullPath);
}

bool CTexture::LoadTextureFullPath(const std::vector<std::string>& vecFullPath)
{
	m_Type = ETexture_Type::Frame;

	size_t	Size = vecFullPath.size();

	for (size_t i = 0; i < Size; ++i)
	{
		// 화면DC를 넣고 메모리 DC를 얻는다.
		HDC	hDC = CreateCompatibleDC(CGameManager::GetInst()->GetWindowDC());

		// 비트맵을 로딩한다.
		// string 이나 wstring 클래스의 c_str() 함수는 문자열 포인터를 얻어온다.
		HBITMAP	hBmp = (HBITMAP)LoadImage(CGameManager::GetInst()->GetWindowInstance(),
			vecFullPath[i].c_str(), IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);

		if (!hBmp)
			return false;

		// 읽어온 비트맵을 메모리 DC에 지정한다.
		// 기존에 DC가 가지고 있던 도구를 반환한다.
		HBITMAP hPrevBmp = (HBITMAP)SelectObject(hDC, hBmp);

		BITMAP	BmpInfo;

		GetObject(hBmp, sizeof(BITMAP), &BmpInfo);

		ImageInfo* Info = new ImageInfo;

		Info->hMemDC = hDC;
		Info->hBmp = hBmp;
		Info->hPrevBmp = hPrevBmp;
		Info->BmpInfo = BmpInfo;

		m_vecImageInfo.push_back(Info);
	}

	return true;
}

#endif // UNICODE

- 이 메소드는 CResourceManager에서 타고 들어가는 구조이므로, 

CTextureManager과 CResourceManager에서도 동일한 작업을 반복해주어야 한다.

 

- 현재 작업이 완료되지 않았기 때문에 컴파일은 되지 않음!

GameFrameworkStepbyStep_33_TextureUpdate.zip
2.02MB