반응형
  • 티스토리 홈
  • 프로필사진
    묭묭.cpp
  • 방명록
  • 공지사항
  • 태그
  • 블로그 관리
  • 글 작성
묭묭.cpp
  • 프로필사진
    묭묭.cpp
    • 분류 전체보기 (103)
      • 데이터베이스 (2)
      • 포트폴리오 (25)
      • 윈도우 인터널즈 (20)
      • 네트워크 (4)
      • IOCP 게임서버 (11)
      • C (2)
      • 디스어셈블리 디버깅 (1)
      • WindowsAPI (11)
      • 학원 강의 정리 모음 (20)
      • 운영체제 (5)
  • 방문자 수
    • 전체:
    • 오늘:
    • 어제:
  • 최근 댓글
      등록된 댓글이 없습니다.
    • 최근 공지
        등록된 공지가 없습니다.
      # Home
      # 공지사항
      #
      # 태그
      # 검색결과
      # 방명록
      • 윈도우즈 API 게임 제작 준비 07 - 리소스 관리
        2023년 10월 28일
        • 묭묭.cpp
        • 작성자
        • 2023.10.28.:14

        지난 포스팅에서 약 1달 정도 시간이 흐른 것 같다.

        추석 연휴도 있었고 감기도 심하게 걸렸었어서 공부를 하긴 했지만 기록하는데는 시간을 쓰지 못하였다.

        얼마전 넥토리얼이란 넥슨 인턴십 채용에도 도전해봤지만 서류 탈락이었다.

        다시 힘내서 블로그 포스팅과 개발 공부를 열심히 진행해보려고 한다.

        1. 리소스

        리소스는 이미지나 사운드 등 게임에서 활용될 모든 자원을 뜻한다.

        이런 자원들의 공통적인 요소를 담는 부모 클래스를 만들고 시작하도록 하겠다.

        먼저 어떤 것들이 공통적인 요소일까를 생각해보면 리소스를 구분하기 위한 Key 값과 해당 리소스의 위치인 상대경로 정도가 될 수 있다.

        이 정보를 토대로 Resource 클래스를 구현해보자.

        class CResource
        {
        public:
        	CResource();
        	~CResource();
        
        public:
        	void 		SetKey(const wstring& strKey_) { m_strKey = strKey_; }
        	void 		SetRelativePath(const wstring& strPath_) { m_strRelativePath = strPath_; }
        
        	const wstring& 	GetKey() { return m_strKey; }
        	const wstring& 	GetRelativePath() { return m_strRelativePath; }
        
        
        private:
        	wstring m_strKey;
        	wstring m_strRelativePath;
        };

        앞으로 사용될 모든 리소스는 이 클래스를 상속받아 구현할 것이다.

         

        그럼 이어서 바로 이미지 리소스인 텍스쳐 클래스를 구현해볼 것이다.

         

        2. Texture

        게임에서 이미지 데이터는 Texture라고 표현된다. Texture의 사전적인 의미는 질감, 감촉인데 왜 해당 단어가 사용될까?

        3D modeling을 생각해보면 된다. 

        구글에서 3D Modeling을 검색하면 색이 입혀지지 않은 3D 모델을 볼 수 있다.

        색이 입혀지지 않은 모델에 색을 입혀줘야 하는데 이때 겉표면에 해당하는 이미지가 바로 Texture이다.

        3D 모델을 넘어 게임 프로그래머들 사이에서 2D와 3D에 관계없이 게임에 사용되는 이미지를 Texture라고 표현하는 것 같다.

         

        그럼 바로 Texture 클래스를 구현하도록 하겠다.

        class CTexture :
            public CResource
        {
        public:
            CTexture();
            ~CTexture();
        
            void Load(const wstring& strFilePath_);
        
            UINT GetWidth() { return m_bitInfo.bmWidth; }
            UINT GetHeight() { return m_bitInfo.bmHeight; }
        
            HDC GetDC() { return m_dc; }
        
        private:
            HDC         m_dc;
            HBITMAP     m_hBit;
            BITMAP      m_bitInfo;
        };

        Texture 클래스에서 중요한 것은 DC 핸들과 BITMAP 핸들이다.

        게임 화면에 그릴 Texture를 BITMAP 형식으로 로드하고 DC를 연결해준다.

        그리고 이것을 게임 화면(비트맵)에 그려주면 된다.

         

        BITMAP 구조체는 해당 BITMAP의 정보를 가지고 있고 가로, 세로 길이를 구하는데 사용된다.

        그럼 Load 함수를 구현해보자.

        void CTexture::Load(const wstring& strFilePath_)
        {
        	// 윈도우에서 제공 - hinst, path, option, size
        	// 파일로부터 로딩한 데이터를 비트맵으로 생성
        	m_hBit = (HBITMAP)LoadImage(nullptr, strFilePath_.c_str(), IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION | LR_LOADFROMFILE);
        
        	// 로딩 실패
        	assert(m_hBit);
        
        	// 비트맵과 연결할 DC
        	m_dc = CreateCompatibleDC(CCore::GetInstance()->GetMainDC());
        
        	// 비트맵과 DC 연결
        	HBITMAP hPrecBit = (HBITMAP)SelectObject(m_dc, m_hBit);
        	
            	// BITMAP 구조체에 정보를 채움
        	GetObject(m_hBit, sizeof(BITMAP), &m_bitInfo);
        }

         

        LoadImage 함수는 파일을 로드하여 비트맵을 생성하는 함수이다.

        msdn에 따르면 첫 번째 인자는 hinst 핸들인데 이미 정의된 이미지를 사용하는 경우 NULL로 지정하면 된다고 한다.

        두 번째 인자는 파일 경로이다.

        세 번째 인자는 로드할 이미지의 type이다. 윈도우에서는 커서, 아이콘, 비트맵을 지원하고 비트맵을 사용하기 위해 IMAGE_BITMAP 을 넣어준다.

        4, 5번의 인자는 size의 x, y 정보이다. 0으로 설정하면 로드한 이미지에 맞는 크기를 지정해준다고 한다.

        마지막 인자는 옵션 정보이다.

         

        해당 함수를 성공적으로 수행하고나면 비트맵 핸들이 반환된다.

        그리고 MainDC를 받아와서 memory DC를 생성해주고 이것을 연결해주면 화면에 texture 리소스를 띄울 준비는 끝이다.

         

        이어서 리소스를 로드하기 위해 경로를 지정해주어야 하는데 이것을 관리하는 방법에 대해 알아보도록 하겠다.

        3. PathManager

        컴퓨터에서는 상대경로와 절대경로라는 경로 개념을 사용한다.

        상대경로는 프로그램을 실행한 위치부터 경로를 찾는 개념이고 절대경로는 C:\ 부터 시작하는 Full 경로이다.

        그런데 실행파일을 옮겨서 실행하게 되면 절대경로를 사용하는 경우 프로그램에 문제가 생길 수 있다.

        이러한 문제를 해결하기 위해서 절대경로를 상대경로로 변환하여 사용하는 것이 필요하다.

         

        PathManager의 정의는 다음과 같다.

        class CPathManager
        {
        	SINGLE(CPathManager);
        
        public:
        	void init();
        	wchar_t* GetContentPath() { return m_szContentPath; }
        
        private:
        	wchar_t m_szContentPath[255];
        };

         

         

        생성자에서 프로그램이 실행되는 위치의 경로를 받아와서 상대경로를 구해주면 된다.

        여기서 Path 문자열의 크기가 255인 이유는 윈도우에서 파일 경로의 최대 크기가 255이기 때문이다.

        void CPathManager::init()
        {
        	GetCurrentDirectory(255, m_szContentPath);
        
        	int iLen = wcslen(m_szContentPath);
        	for (int i = iLen - 1; i >= 0; --i)
        	{
        		if ('\\' == m_szContentPath[i])
        		{
        			m_szContentPath[i] = '\0';
        			break;
        		}
        	}
        
        	wcscat_s(m_szContentPath, 255, L"\\bin\\content\\");
        
        	SetWindowText(CCore::GetInstance()->GetMainHandle(), m_szContentPath);
        }

        GetCurrentDirectory 함수가 실행되면 현재 파일의 경로가 2번째 인자의 문자열에 채워진다.

        이때 파일의 경로는 기본적으로 Visual Studio에서 F5를 눌러 실행하면 프로젝트 디렉토리가 되고

        직접 실행파일을 실행하게 되면 해당 실행 파일이 위치한 경로가 된다.

        이것을 통일시키면 모든 환경에서 동일한 결과를 기대할 수 있다.

         

        우선 출력 파일 위치를 관리하기 위해 설정을 변경하였다.

        프로젝트 속성 -> 일반 -> 출력 디렉터리를 변경하면 된다.

        디버그 모드에서는 $(SolutionDir)Ouput\bin_debug\

        릴리즈 모드에서는 $(SolutionDir)Ouput\bin\ 으로 설정해주었고

        실습을 진행하며 모든 리소스의 관리는 bin\content 디렉터리에서 진행할 것이다.

         

        이어서 F5를 눌러 실행했을 때 감지되는 디렉터리 설정도 변경해주었다.

        프로젝트 설정 -> 디버깅 -> 작업 디렉터리의 경로를 $(SolutionDir)Output\bin\ 으로 변경해주면 된다.

         

        여기까지 진행하면 GetCurrentDirectory 함수로 얻어온 경로는 어떤 환경에서든 Output\bin\ 경로가 된다.

         

        이어서 Load 함수를 설명하면

        반복문을 통하여 뒤에서부터 \ 문자를 만나면 \0을 대입하여 Output 디렉터리로 경로를 맞춰준다.

        그 이후 \bin\content\ 의 경로를 붙여주어 리소스가 있는 위치를 잡아주게 된다.

         

        그럼 이제 Player의 Texture를 전투기로 바꿔보도록 하겠다.

         

        CPlayer::CPlayer() : m_pTex(nullptr)
        {
        	// Texture 로딩
        	m_pTex = new CTexture;
        	wstring strFilepath = CPathManager::GetInstance()->GetContentPath();
        
        	// 이미지 파일은 24비트 bmp 파일이어야 함
        	strFilepath += L"texture\\Player.bmp";
        	m_pTex->Load(strFilepath);
        }

        Player의 생성자에서 Texture를 로드한다.

         

        void CPlayer::render(HDC dc_)
        {
        	int iWidth = (int)m_pTex->GetWidth();
        	int iHeight = (int)m_pTex->GetHeight();
        
        	Vec2 vPos = GetPos();
        
        	// 좌 상단
        	(int)(vPos.x - (float)(iWidth / 2));
        	(int)(vPos.y - (float)(iHeight / 2));
        
        	/*BitBlt(dc_
        		, (int)(vPos.x - (float)(iWidth / 2))
        		, (int)(vPos.y - (float)(iHeight / 2))
        		, iWidth, iHeight
        		, m_pTex->GetDC()
        		, 0, 0
        		, SRCCOPY);*/
        
        	// 설정한 색상을 투명화 - 주로 마젠타, 255, 0, 255
        	TransparentBlt(dc_
        		, (int)(vPos.x - (float)(iWidth / 2))
        		, (int)(vPos.y - (float)(iHeight / 2))
        		, iWidth, iHeight
        		, m_pTex->GetDC()
        		, 0, 0, iWidth, iHeight
        		, RGB(255, 0, 255));
        }

        render 함수에서 BitBlt 혹은 TransparentBlt을 통해 화면에 그려주면 된다.

        그런데 BitBlt을 사용하면 이미지 겉 테두리가 보라색으로 나올수도 있다.

        윈도우에서 가장 사용하지 않는 색상을 보라색으로 설정하여 비트맵 이미지 중 테두리가 보라색인 경우가 많다고 한다.

        이 때 TransparentBlt 함수를 이용하면 지정된 색상을 투명처리할 수 있는데 마지막 인자를 255, 0, 255로 설정해주면 정상적으로 전투기가 출력되게 된다.

         

        여기서 문제점은 Player에 리소스를 추가하여 사용할 때처럼 Object 등 리소스가 필요할 때마다 코드를 삽입하여 사용하는 것은 리소스의 추적, 관리 측면에서 매우 비효율적이다.

        앞으로 모든 리소스의 관리는 ResourceManager 클래스에서 진행하기 위해 ResourceManager 클래스를 구현하여 사용해야 한다.

         

        4. ResourceManager

        class CResourceManager
        {
        	SINGLE(CResourceManager);
        
        public:
        	CTexture* LoadTexture(const wstring& strKey_, const wstring& strRelativePath_);
        	CTexture* FindTexture(const wstring& strKey_);
        private:
        	map<wstring, CTexture*> m_mapTex;
        };

        ResourceManager의 정의이다.

        모든 Resource를 map에 저장하여 관리하고 Key값을 사용하여 리소스를 찾아 사용할 수 있다.

        LoadTexture 함수에서 Texture를 로드할 수 있고 FindTexture에서는 이미 있는 Resource를 찾아 사용할 수 있다.

         

        CTexture* CResourceManager::LoadTexture(const wstring& strKey_, const wstring& strRelativePath_)
        {
        	CTexture* pTex = FindTexture(strKey_);
        	if (pTex != nullptr)
        	{
        		return pTex;
        	}
        
        	wstring strFilePath = CPathManager::GetInstance()->GetContentPath();
        	strFilePath += strRelativePath_;
        
        	pTex = new CTexture;
        	pTex->Load(strFilePath);
        	pTex->SetKey(strKey_);
        	pTex->SetRelativePath(strRelativePath_);
        
        	m_mapTex.insert(make_pair(strKey_, pTex));
        
        	return pTex;
        }

        LoadTexture 함수의 구현부이다. key값과 경로를 받아 리소스를 로드하는 역할을 한다.

        Player 함수에 있던 부분을 옮겨오고 Key값과 경로를 설정하는 부분을 추가하였다.

        이미 Resource가 Load 되어있다면 이미 있는 것을 찾아서 반환한다.

         

        CTexture* CResourceManager::FindTexture(const wstring& strKey_)
        {
        	auto it = m_mapTex.find(strKey_);
        
        	if (it == m_mapTex.end())
        	{
        		return nullptr;
        	}
        
        	return it->second;
        }

        FindTexture 함수의 구현부이다.

        key값을 기준으로 값을 찾고 it->sencond 값인 texture 클래스의 포인터 값을 반환한다.

         

        추가로 Texture 클래스를 직접적으로 생성할 수 없도록 만들어주었다.

        생성자를 private로 막아주고 friend class로 ResourceManager를 설정해주었다.

         

        Player 클래스의 생성자에서 다음과 같이 불러와 사용하면 된다.

        CPlayer::CPlayer() : m_pTex(nullptr)
        {
        	// Texture 로딩
        	m_pTex = CResourceManager::GetInstance()->LoadTexture(L"PlayerTex", L"texture\\Player.bmp");
        }

         

        여기까지 Resource를 구현하고 사용하는 방법을 학습하였다.

        다음 시간에는 충돌처리를 공부할 예정이다.

        포스팅을 마치도록 하겠다.

        반응형

        'WindowsAPI' 카테고리의 다른 글

        윈도우즈 API 게임 제작 준비 08 - 충돌처리  (0) 2023.11.06
        윈도우즈 API 게임 제작 준비 06 - 게임 수학 (삼각함수)  (0) 2023.10.01
        윈도우즈 API 게임 제작 준비 05 - Object Class  (0) 2023.09.26
        윈도우즈 API 게임 제작 준비 04 - KeyManager, SceneManager  (1) 2023.09.24
        윈도우즈 API 게임 제작 준비 03 - Core 클래스, Timer 클래스  (0) 2023.09.23
        다음글
        다음 글이 없습니다.
        이전글
        이전 글이 없습니다.
        댓글
      조회된 결과가 없습니다.
      스킨 업데이트 안내
      현재 이용하고 계신 스킨의 버전보다 더 높은 최신 버전이 감지 되었습니다. 최신버전 스킨 파일을 다운로드 받을 수 있는 페이지로 이동하시겠습니까?
      ("아니오" 를 선택할 시 30일 동안 최신 버전이 감지되어도 모달 창이 표시되지 않습니다.)
      목차
      표시할 목차가 없습니다.
        • 안녕하세요
        • 감사해요
        • 잘있어요

        티스토리툴바