WIN32API FrameWork/한단계씩 직접 구현

32. 이미지 등의 리소스 관리자

2022. 5. 24. 14:13

http://hyrule.tistory.com/111 

 

*** 공부 방법 ***

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;
}

정상적으로 출력되는 것을 확인할 수 있다.
GameFrameworkStepbyStep_32_TextureRender.zip
2.02MB

저작자표시 (새창열림)

'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
'WIN32API FrameWork/한단계씩 직접 구현' 카테고리의 다른 글
  • 34. 씬 단위의 리소스 관리
  • 33. 이미지의 애니메이션화를 위한 준비
  • 31. 이미지 로딩을 위한 준비 - 경로 관리자
  • 30. 스킬 구현 2 - 아우렐리온 솔 W
hyrule
hyrule
hyrule
C++ 프로그래밍 공부
hyrule
전체
오늘
어제
  • 분류 전체보기 (205)
    • C++기초 (50)
    • WIN32API FrameWork (109)
      • 한단계씩 직접 구현 (82)
      • 원본 (15)
      • 코드별 설명 개별저장(검색용) (12)
    • 자습 (21)
    • C++ TIPS (11)
    • 연습 노트 (3)
    • ETC (6)
    • DX2D StarCraft 모작 (1)

블로그 메뉴

  • 홈
  • 방명록

공지사항

인기 글

태그

  • notion
  • C++
  • 스타크래프트
  • hello
  • Windows 11
  • Tistory

최근 댓글

최근 글

hELLO · Designed By 정상우.
hyrule
32. 이미지 등의 리소스 관리자
상단으로

티스토리툴바

개인정보

  • 티스토리 홈
  • 포럼
  • 로그인

단축키

내 블로그

내 블로그 - 관리자 홈 전환
Q
Q
새 글 쓰기
W
W

블로그 게시글

글 수정 (권한 있는 경우)
E
E
댓글 영역으로 이동
C
C

모든 영역

이 페이지의 URL 복사
S
S
맨 위로 이동
T
T
티스토리 홈 이동
H
H
단축키 안내
Shift + /
⇧ + /

* 단축키는 한글/영문 대소문자로 이용 가능하며, 티스토리 기본 도메인에서만 동작합니다.