*** 공부 방법 ***
1. 코딩을 해야 하는 부분은 첫 부분에 변수나 함수, 메소드에 대한 선언이 코드블럭으로 표시되어 있다. //ex) MakeFunction(); 2. 코드블럭 하단에는 해당 선언에 대한 구현 로직이 작성되어 있다. 처
hyrule.tistory.com
[사전 지식]
- WIN32API에서는 일반적으로 이미지를 '정적 라이브러리'인 msimg32.lib를 통해 처리한다.
-CF) 라이브러리: 라이브러리는 일종의 다른사람이 만들어 놓은 '코드 덩어리' 이다.
-- .lib: 정적 라이브러리 -> 전처리기를 통해 컴파일 이전 단계에서 라이브러리가 구성되어있는 코드를 통으로 링크를 걸어놓고 시작:
- .lib파일을 링크하는 방법은 2가지가 있다.
-1. 비주얼 스튜디오 설정을 통해 링크 걸기: 프로젝트 속성 창에서 링커 - 입력 - 추가 종속성
-2. 코드를 통해 링크 걸기
#pragma comment(lib, "msimg32.lib")
<이미지 로딩>
- 이미지 로딩도 마찬가지로 파일을 로딩하는 과정(fopen)이라고 보면 된다.
-- 이미지 파일의 형식에 맞게 로딩이 가능한 라이브러리를 통해 파일을 여는 것이다.
* 변수 목록
HDC m_hMemDC
- 이미지를 저장하는 곳: HDC
-- 우리가 여태까지 사용했던 HDC와 같지만, 화면에 표시되지 않고 메모리상에서만 존재하고 있는 DC이다.
-- 메모리에 도장을 하나 생성해 놓고, 거기에 이미지를 파 놓는다고 생각하면 된다. -> 필요할 때마다 가져다 찍는 것임.
-- 사용 방법이 정해져 있으므로, 그것만 외워놓으면 된다.
HBITMAP h_Bmp
- 이미지 데이터가 저장될 구조체
- 도장 파는 도구라고 보면 된다. 해당 구조체에 저장된 이미지를 메모리 DC에 새겨주면,
메모리 DC를 사용해 도화지에 찍어낸다고 보면 된다.
BITMAP m_BmpInfo;
typedef struct tagBITMAP
{
LONG bmType;
LONG bmWidth;
LONG bmHeight;
LONG bmWidthBytes;
WORD bmPlanes;
WORD bmBitsPixel;
LPVOID bmBits;
} BITMAP, *PBITMAP, NEAR *NPBITMAP, FAR *LPBITMAP;
- 이미지에 대한 정보를 저장하는 구조체.
- bmWidth와 bmHeight 두 개 외에는 거의사용하지 않는다.
* 사용 과정
h_MemDC = CreateCompatibleDC(m_hDC);
- 화면 DC를 인자로 넣으면, 거기에 사용할 수 있는 DC를 반환해준다.
m_hBmp = (HBITMAP)LoadImage(m_hInstance, Path, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
1번 인자: 윈도우의 인스턴스 핸들
2번 인자: 경로
3번 인자: 형식(비트맵)
4,5번 인자: 이미지의 사이즈(0, 0 입력하면 알아서 로드함)
6번 인자: 파일로부터 로드한다고 설정
m_hPrevBmp = (HBITMAP)SelectObject(m_hMemDC, m_hBmp);
- 읽어온 비트맵을 메모리 DC에 지정하고,
메모리 DC가 원래 들고있던 도구를 m_hPrevBmp에 저장한다.(나중에 메모리에서 제거할 때 원상복구 해놔야됨)
GetObject(m_hBmp, sizeof(BITMAP), &m_BmpInfo);
- 마지막으로 비트맵의 정보를 m_BmpInfo 구조체 변수에 저장해놓는다.
SelectObject(m_hMemDC, m_hPrevBmp);
DeleteObject(m_hBmp);
DeleteDC(m_hMemDC);
- 마지막으로 셋팅해둔 DC를 제거할 때는,
1. SelectObject() 함수로MemDC에 들어있던 정보를 원상복구 해놓고,
2. DeleteObject() 함수로 m_hBmp를 제거하고,
3. DeleteDC() 함수를 통해 마지막으로 m_hMemDC를 제거한다.
- 저장된 이미지를 출력할 떄는, BitBlt() 함수를 사용한다.
Vector2 RenderLT;
RenderLT = m_Pos - m_Pivot * m_Size;
BitBlt(hDC, (int)RenderLT.x, (int)RenderLT.y,
(int)m_Size.x, (int)m_Size.y, m_Texture->GetDC(),
0, 0, SRCCOPY);
-1번 인자: 출력 DC
-2번, 3번 인자: 좌측 상단의 좌표 x, y
-4번, 5번 인자: 사이즈
-6번 인자: 메모리 DC(출력할 텍스처 DC)
-7, 8번 인자: 출력할 이미지의 시작 좌표(0,0을 넣으면 모두 출력)
-9번 인자: 출력 방식(SRCCOPY: 원본 이미지 출력)
class CResourceManager
- Include/Resource 하위 폴더에 생성한다.
- 새 필터를 만들어 모아놓는다.
- 이미지 관리를 위한 관리자 클래스 - 싱글턴 패턴으로 생성하고, CGameManager에서 객체 생성 및 초기화를 해준다.
- 다른 매니저 클래스들과 마찬가지로 초기화 메소드와 업데이트 메소드를 가지고 있지만, DeltaTime을 인자로 받지는 않는다.
class CTextureManager
- Include/Resource/Texture 폴더를 만들고 이 안에 생성한다.
- CResourceManager만이 자유롭게 해당 클래스를 관리할 수 있다. 그 외 외부에서는 접근 불가능하게 틀어막아 준다.
-> 생성, 초기화 및 삭제는 모두 CResourceManager에서 담당한다.
class CTexture
- 실제 텍스처 데이터를 들고 있을 클래스.
- 텍스처 같은 경우 공유해서 사용할 일이 매우 많으므로, Reference Count를 상속받아 사용하는 편이 좋다.
- CTextureManager에서만 자유롭게 관리할 수 있고, 그 외 외부에서는 접근 불가능하게 만든다.
- 사전 지식에서 나왔던 HDC, HBITMAP, BITMAP 변수가 모두 여기 들어간다.
- 나중에 이미지 파일을 불러올 수 있게 들고있는 HDC를 반환해주는 GetDC() 메소드도 만들어준다.
//Texture.h
#pragma once
#include "../../Ref.h"
class CTexture :
public CRef
{
friend class CTextureManager;
private:
CTexture();
~CTexture();
private:
HDC m_hMemDC;
HBITMAP m_hBmp;
HBITMAP m_hPrevBmp;
BITMAP m_BmpInfo;
public:
HDC GetDC() const
{
return m_hMemDC;
}
};
CTexture::LoadTexture()
- CPathManager를 통해 실제 이미지파일의 경로를 찾아 로드하는 메소드
- 절차의 성공/실패 여부를 bool 변수로 반환
- 인자로 파일명과 경로명을 지정하고, 경로명은 기본값으로 TEXTURE_PATH를 준다.
<로직>
- CPathManager을 통해 들어온 경로명의 이름으로 경로를 받아온다.
- 경로가 존재하면 해당 경로에 파일명을 붙여 완전한 경로를 만든다.
- 이후 경로를 활용하여 이미지를 로딩한다.
- 로딩에 성공하면 true를 반환한다.
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);
// 화면DC를 넣고 메모리 DC를 얻는다.
m_hMemDC = CreateCompatibleDC(CGameManager::GetInst()->GetWindowDC());
// 비트맵을 로딩한다.
m_hBmp = (HBITMAP)LoadImage(CGameManager::GetInst()->GetWindowInstance(),
FullPath, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
if (!m_hBmp)
return false;
// 읽어온 비트맵을 메모리 DC에 지정한다.
// 기존에 DC가 가지고 있던 도구를 반환한다.
m_hPrevBmp = (HBITMAP)SelectObject(m_hMemDC, m_hBmp);
GetObject(m_hBmp, sizeof(BITMAP), &m_BmpInfo);
return true;
}
- 이제 CTexture에 대한 설계는 완료됐다.
CTextureManager::FindTexture()
- CTextureManager에서는, CTexture을 생성하고, 목록에 보관함으로써 텍스처 리소스를 관리한다.
- 우선 가장 먼저, CTexture의 목록을 보관할 자료구조를 만들어준다.(참고 - CTexture은 현재 Reference Count를 사용 중이다)
-- 텍스처를 찾을 수 있는 이름을 가지고 있어야 하며, 탐색이 빠른 자료구조가 필요하다.
private:
std::unordered_map<std::string, CSharedPtr<class CTexture>> m_mapTexture;
CTextureManager::FindTexture()
- 위 자료구조를 탐색할 수 있는 메소드를 만들어보자.
-- 인자로 이름을 받아서 해당 CTexture를 찾으면 CTexture 주소를 반환하고, 찾지 못하면 nullptr을 반홚나다.
//TextureManager.cpp
CTexture* CTextureManager::FindTexture(const std::string& Name)
{
auto iter = m_mapTexture.find(Name);
if (iter == m_mapTexture.end())
return nullptr;
return iter->second;
}
CTextureManager::LoadTexture()
- CTexture은, CTextureManager에서 목록을 관리하므로 CTextureManager에서도 필요 시 CTexture을 생성하고 이미지를 로드할 수 있도록 메소드를 짜 주어야 한다.
- 인자는 CTexture에서 받는 인자들에 '텍스처 이름(문자열)'만 추가한다.
- 새 CTexture을 동적 할당하고, 인자를 전달하여 이미지를 로딩시킨다.
- 만든 CTexture은 '텍스처 이름' 인자를 활용하여 자료구조에 삽입해준다.
- 성공/실패 여부를 bool 타입으로 반환한다.
//TextureManager.cpp
bool CTextureManager::LoadTexture(const std::string& Name,
const TCHAR* FileName, const std::string& PathName)
{
// 같은 이름으로 저장된게 있다면 잘못된것이다.
CTexture* Texture = FindTexture(Name);
if (Texture)
return false;
Texture = new CTexture;
if (!Texture->LoadTexture(FileName, PathName))
{
SAFE_RELEASE(Texture);
return false;
}
m_mapTexture.insert(std::make_pair(Name, Texture));
return true;
}
class CResourceManager
CResourceManager::LoadTexture();
CResourceManager::FindTexture();
- 리소스를 총괄 관리하는 CResourceManager에서도 텍스처 생성을 할 수 있게 만들어준다.
- 이미 기능은 모두 자식 클래스들에 구현되어 있으므로, 해당 메소드에서는 인자를 그대로 전달해주는 역할만 수행한다.
//ResourceManager.cpp
bool CResourceManager::LoadTexture(const std::string& Name, const TCHAR* FileName, const std::string& PathName)
{
return m_TextureManager->LoadTexture(Name, FileName, PathName);
}
CTexture* CResourceManager::FindTexture(const std::string& Name)
{
return m_TextureManager->FindTexture(Name);
}
class CGameObject
- 이제 대략적인 구현은 완료되었다.
- 테스트로 이미지가 정상적으로 로드되는지만 확인해 보자.
- CResourceManager에서 테스트용 bmp 이미지를 하나 로딩해보자. 인터넷에서 대충 아무거나 갖다 쓰면 된다.
- 해당 파일을 Bin/Texture 폴더 안에 넣어놓고, 파일 명으로 로드한다.
//ResourceManager.cpp
bool CResourceManager::Init()
{
LoadTexture("Monster", TEXT("teemo1.bmp"));
return true;
}
* 이 코드는 테스트 차원에서 임시로 넣어놓은 코드이다. 정상출력되는 것이 확인되면 제거
class CGameObject
- 실제로 텍스처를 사용할 CGameObject에는 사용할 CTexture 주소를 가지고 있어야 한다.
protected:
CSharedPtr<class CTexture> m_Texture;
CGameObejct::SetTexture()
- 그리고 자신이 사용할 텍스처를 찾아주는 메소드도 필요하다.
- CResourceManager에 접근해서 찾은 후에, m_Texture 변수에 들고 있는다.
//CGameObject.cpp
void CGameObject::SetTexture(const std::string& Name)
{
m_Texture = CResourceManager::GetInst()->FindTexture(Name);
}
CGameObject::Render()
- 이제, 들고온 텍스처를 출력해주어야 한다.
- CGameObject를 상속받는 모든 클래스에서 전부 각각 출력할 필요 없이,
- 자식 오브젝트의 Render() 메소드에서 부모 메소드를 타고가면서 Render()를 호출하여 최종적으로 CGameObject::Render() 메소드로 들어오도록 해준다.
--ex)CMonster::Render() 내부-> CCharacter::Render() 내부 -> CGameObject::Render()
- 출력은 BitBlt를 이용하여 해준다.
void CGameObject::Render(HDC hDC, float DeltaTime)
{
if (m_Texture)
{
Vector2 RenderLT;
RenderLT = m_Pos - m_Pivot * m_Size;
BitBlt(hDC, (int)RenderLT.x, (int)RenderLT.y,
(int)m_Size.x, (int)m_Size.y, m_Texture->GetDC(),
0, 0, SRCCOPY);
}
}
- 이제 CMonster 객체의 초기화 단계에서 SetTexture 메소드를 통해 텍스처를 등록해 준 뒤 실행해주면,
bool CMonster::Init(CGameObject* Obj)
{
CCharacter::Init(Obj);
SetTexture("Monster");
//...기존코드...//
return true;
}
'WIN32API FrameWork > 한단계씩 직접 구현' 카테고리의 다른 글
34. 씬 단위의 리소스 관리 (0) | 2022.05.25 |
---|---|
33. 이미지의 애니메이션화를 위한 준비 (0) | 2022.05.25 |
31. 이미지 로딩을 위한 준비 - 경로 관리자 (0) | 2022.05.24 |
30. 스킬 구현 2 - 아우렐리온 솔 W (0) | 2022.05.23 |
29. CreateObject 메소드 수정 (0) | 2022.05.23 |