WIN32 Collider

Date:     Updated:

카테고리:

태그:

12일차

- Default
    - Client.h
    - framework.h
    - Resouce.h
    - targetver.h
- Engine
    1. Header
        **1. define.h**
        **2. struct.h**
    2. Core
        **1. CCore.cpp**
        **2. CCore.h**
    3. Manager
        1. KeyMgr
            1. CkeyMgr.cpp
            2. CkeyMgr.h
        2. TimeMgr
            1. CTimeMgr.cpp
            2. CTimeMgr.h
        3. SceneMgr
            1. CSceneMgr.cpp
            2. CSceneMgr.h
    		4. PathMgr
						1. CPathMgr.cpp
						2. CPathMgr.h
				**5. ResMgr
						1. CResMgr.cpp
						2. CResMgr.h**
    **4. Object
		    1. Monser
			    1. CMonster.cpp**
			    2. CMonster.h
****			  2. Missle
				  **1. Missile.cpp**
				  2. Missile.h
				**3. Player
					1. CPlayer.cpp**
					2. Cplayer.h
****        **1. CObject.cpp
        2. CObject.h**
    5. Scene
        1. Scene_Start
            1. CScene_Start.cpp
            2. CScene_Start.h
        2. Scene_Tool
        3. CScene.cpp
        4. CScene.h
    6. Resource
		    1. Sound
		    2. Texture
			    1. CTexture.cpp
				  2. CTexture.h
		    3. CRes.cpp
		    4. CRes.h
		**7. Component
				1. CCollider.cpp
				2. CCollider.h
		8. Module
				1. SelectGDI.cpp
				2. SelectGDI.h**
- 리소스 파일
- main.cpp
- pch.h.
  • 오늘은 엄청나게 건드린 게 많다!
  • 텍스쳐 매니저, 콜라이더, GDI(펜, 브러쉬) 편하게. 를 건드렸다고 보면 될 거 같다. 그러면 코드를 보자!
  1. define.h
#pragma once

//매크로는 코드를 치환하는 것. 
//따라서 함수를 호출한 적이 없어서 스위칭이 없어서 더 빠름. 
//주의점은 매크로는 함수가 아니라, 그대로 코드를 치환하는거라서

//완전히 다른 느낌이 될 수 있음...

//이렇게 하면 내가 해제를 신경쓰지 않아도 된다는 점
//반대로 이렇게 하면의 단점. 
//끝까지 끌고 나가야 하는 방식.

//SingleTon 매크로. 

#define SINGLE(type)public: \
					static type* GetInstance()\
					{\
						static type mgr;\
						return &mgr;\
					}\
					private:\
						type();\
						~type();

#define fDT CTimeMgr::GetInstance()->GetfDT()
#define DT	CTimeMgr::GetInstance()->GetDT()

#define KEY_CHECK(key, state) CkeyMgr::GetInstance()->GetKeyState(key) == state

#define KEY_HOLD(key) KEY_CHECK(key, KEY_STATE::HOLD)
#define KEY_TAP(key) KEY_CHECK(key, KEY_STATE::TAP)
#define KEY_NONE(key) KEY_CHECK(key, KEY_STATE::NONE)
#define KEY_AWAY(key) KEY_CHECK(key, KEY_STATE::AWAY)

#define PI 3.1415926535f

enum class GROUP_TYPE {
	DEFAULT,
	PLAYER,
	MISSILE,
	MONSTER,

	END = 32,
};

enum class SCENE_TYPE {
	TOOL,
	START,
	STAGE_01,
	STAGE_02,

	END,
};

enum class BRUSH_TYPE {
	HOLLOW,
	END,
};

enum class PEN_TYPE {
	RED,
	GREEN, 
	BLUE,
	END,
};
  • 펜 설정을 위한 브러시 타입, 펜 타입을 추가해줬다.
  1. struct.h
#pragma once

struct Vec2 {
	float x;
	float y;

public:
	float Length() {
		return sqrt(x * x + y * y);
	}

	Vec2& Normalize() {
		float fLen = Length();
		assert(fLen != 0.f);

		x /= fLen;
		y /= fLen;

		return *this;
	}

	Vec2& operator = (POINT _pt) {
		x = (float)_pt.x;
		y = (float)_pt.y;
		;
	}

	Vec2 operator + (Vec2 _vOther) {
		return Vec2(x + _vOther.x, y + _vOther.y);
	}

	Vec2 operator - (Vec2 _vOther) {
		return Vec2(x - _vOther.x, y - _vOther.y);
	}

	Vec2 operator * (Vec2 _vOther) {
		return Vec2(x * _vOther.x, y * _vOther.y);
	}

	Vec2 operator / (Vec2 _vOther) {
		assert(!(0.f == _vOther.x || 0.f == _vOther.y));
		return Vec2(x / _vOther.x, y / _vOther.y);
	}

public:
	Vec2()
		: x(0.f)
		, y(0.f)
	{}

	Vec2(float _x, float _y)
		: x(_x)
		, y(_y)
	{}

	Vec2(int _x, int _y)
		: x((float)_x)
		, y((float)_y)
	{}

	Vec2(const POINT& _pt)
		: x((float)_pt.x)
		, y((float)_pt.y)
	{}
};
  • 사칙연산 operator를 추가해줬다.
  1. CCore.h
#pragma once

#include "define.h"

class CCore {
	SINGLE(CCore);

private:
	HWND	m_hWnd;				//메인 윈도우 핸들. 
	POINT	m_ptResolution;		//메인 윈도우 해상도

	HDC		m_hDC;				//메인 윈도우에 Draw 할 DC

	HBITMAP m_hBit;				//사본용 . 
	HDC		m_memDC;

	//자주 사용하는 GDI Object 펜
	HBRUSH	m_arrBrush[(UINT)BRUSH_TYPE::END];
	HPEN	m_arrPen[(UINT)PEN_TYPE::END];

private:
	void CreateBrushPen();

public:
	int init(HWND _hWnd, POINT _ptResolution);
	void progress();

public:
	HWND GetMainHwnd() { return m_hWnd; }
	HDC GetMainDC() { return m_hDC; }
	POINT GetResolution() { return m_ptResolution; }

	HBRUSH GetBrush(BRUSH_TYPE _eType) { return m_arrBrush[(UINT)_eType]; }
	HPEN GetPen(PEN_TYPE _eType) { return m_arrPen[(UINT)_eType]; }

};
  • 펜을 쉽게 관리하기 위한, 배열과 Get함수를 만들어줬다.
  1. CCore.cpp
#include "pch.h"
#include "CCore.h"

#include "CTimeMgr.h"
#include "CkeyMgr.h"
#include "CSceneMgr.h"
#include "CPathMgr.h"

//CCore* CCore::g_pInst = nullptr;

CCore::CCore() 
	: m_hWnd(0), m_ptResolution{}, m_hDC(0)
	, m_hBit(0), m_memDC(0)
	, m_arrBrush{}, m_arrPen{}
{
}

CCore::~CCore() {
	//m_hWnd에 엮여있던 m_hDC를 해제해준다. 
	ReleaseDC(m_hWnd, m_hDC);

	//Create로 만든 것은 DeleteDC로 해야한다. 
	DeleteDC(m_memDC);
	DeleteObject(m_hBit);
	
	for (auto penIDX = 0; penIDX < (UINT)PEN_TYPE::END; penIDX++) {
		DeleteObject(m_arrPen[penIDX]);
	}
}

int CCore::init(HWND _hWnd, POINT _ptResolution) {
	m_hWnd = _hWnd;
	m_ptResolution = _ptResolution;

	//해상도에 맞게 윈도우 크기 조정. 

	RECT rt = { 0,0,m_ptResolution.x, m_ptResolution.y };
	//테두리 포함해서, 크기 보정해주는 것. 
	//rt 포인터에다가 다시 값을 변환해서 넣어줌. 
	AdjustWindowRect(&rt, WS_OVERLAPPEDWINDOW, true);

	//타이틀이나 그런 걸 제외한, 순수하게 물체가 그려질 수 있는 영역.
	//첫 번째로 들어가는 건, 어떤 윈도우인지. 
	//SetWindow는 타이틀창이나 테두리까지 포함한 크기임. 
	SetWindowPos(m_hWnd, nullptr, 100, 100, rt.right - rt.left, rt.bottom - rt.top, 0);
	
	//어디다가 그려요? m_hWnd에다가 그려요. 
	m_hDC = GetDC(m_hWnd);

	//이중 버퍼링 용도의 비트맵과 DC를 만든다. 
	//그림을 그릴 수 있는 건 윈도우에 달려있는, 해상도만큼의 픽셀(묶어서 비트맵이라고 부름)

	//결국 똑같은 해상도로 비트맵을 가지면 되는 거 아닌가? 해서 똑같은 해상도의 비트맵을 만듬. 
	//Compatible은 호환성. (호환성을 가지고 있는 비트맵)
	//비트맵 아이디 값을 받았음. 
	m_hBit = CreateCompatibleBitmap(m_hDC, m_ptResolution.x, m_ptResolution.y);

	//새로 만든 목적지의 DC가 필요함. (어떤 구조인지에 대한 Device Context가 필요함)
	m_memDC = CreateCompatibleDC(m_hDC);

	//이번엔 그림을 그릴 타겟, 목적지를 교체해주는 것. 위에서 만든 비트맵을 전달해줌. 
	//되돌아 리턴되는 건 원래 비트맵이지 않을까? 기본적으로 들어가있는 1픽셀짜리 Default 비트맵. 
	HBITMAP holdBit = (HBITMAP)SelectObject(m_memDC, m_hBit);
	DeleteObject(holdBit);

	//자주 사용 할 펜 및 브러쉬 생성. 
	CreateBrushPen();

	//Manager initialize
	CPathMgr::GetInstance()->init();
	CTimeMgr::GetInstance()->init();
	CkeyMgr::GetInstance()->init();
	CSceneMgr::GetInstance()->init();

	return S_OK;
}

void CCore::progress() {

	//Manager Update
	CTimeMgr::GetInstance()->update();
	CkeyMgr::GetInstance()->update();

	CSceneMgr::GetInstance()->update();

	// ============
	//	Rendering
	// ============

	//초기화(흰 사각형으로 덧 씌움)
	Rectangle(m_memDC, -1, -1, m_ptResolution.x + 1, m_ptResolution.y + 1);

	//어디다가 그려야하는지 알려주기. 
	CSceneMgr::GetInstance()->render(m_memDC);

	BitBlt(m_hDC, 0, 0, m_ptResolution.x, m_ptResolution.y, m_memDC, 0, 0, SRCCOPY);
	
	//CTimeMgr::GetInstance()->render();
}

void CCore::CreateBrushPen()
{
	// Hollow Brush
	m_arrBrush[(UINT)BRUSH_TYPE::HOLLOW] = (HBRUSH)GetStockObject(HOLLOW_BRUSH);

	//R,G,B Pen
	m_arrPen[(UINT)PEN_TYPE::RED] = (HPEN)CreatePen(PS_SOLID, 1, RGB(255, 0, 0));
	m_arrPen[(UINT)PEN_TYPE::GREEN] = (HPEN)CreatePen(PS_SOLID, 1, RGB(0, 255, 0));
	m_arrPen[(UINT)PEN_TYPE::BLUE] = (HPEN)CreatePen(PS_SOLID, 1, RGB(0, 0, 255));

}
  • 처음 설정할 때, 펜을 만들어준다. 정도? 당연히 생성자, 소멸자 세팅도 해준다.
  1. CResMgr.h
#pragma once

class CTexture;
class CResMgr
{
	SINGLE(CResMgr);
private:
	map<wstring, CTexture*> m_mapTex;

public:
	CTexture* LoadTexture(const wstring& _strKey, const wstring& _strRelativePath);
	CTexture* FindTexture(const wstring& _strKey);
};

  • 텍스쳐를 관리하는(텍스쳐는 1번만 로딩하여, 오브젝트에 그걸 주는 방식이기에) 매니저이다.
  • Map방식으로 굴러가는데, 내 생각엔 unordered_map이 더 좋을 거 같기도… 그렇게 로딩, 검색이 빈번한 게 아니라서 괜찮나?
  1. CResMgr.cpp
#include "pch.h"
#include "CResMgr.h"

#include "CPathMgr.h"
#include "CTexture.h"

CResMgr::CResMgr() {

}

CResMgr::~CResMgr() {
	for (auto mapIter : m_mapTex) {
		delete mapIter.second;
	}
}

CTexture* CResMgr::LoadTexture(const wstring& _strKey, const wstring& _strRelativePath)
{
	//이미 텍스쳐가 map에 있는지 없는지.
	CTexture* pTex = FindTexture(_strKey);

	//이미 있다면 그냥 리턴해주기. 
	if (pTex != nullptr) return pTex;

	wstring strFilePath = CPathMgr::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;
}

CTexture* CResMgr::FindTexture(const wstring& _strKey)
{
	auto TextureIter = m_mapTex.find(_strKey);

	if (TextureIter == m_mapTex.end()) return nullptr;
	else return TextureIter->second;
}

  • Load 호출받으면 Find로 맵에 기존에 있는지 확인! 없으면 주소값으로 로딩하고, 있다면 그냥 Value값 준다!!
  1. CCollider.h
#pragma once

class CObject;
//오브젝트의 영역을 검사하는 역할을 가짐. 

//충돌체가 그냥 오브젝트의 포지션에 있다면, 충돌체가 머리에만있을 경우, 몸통에 충돌체 따로 있을 경우...
//부위별로 따로 충돌체 두는 그런 거 못해서.
class CCollider
{
private:
	CObject*	m_pOwner;		//collider를 소유하고 있는 오브젝트. 

	Vec2		m_vOffsetPos;	//오브젝트로부터 상대적인 위치. 
	Vec2		m_vFinalPos;	//매 프레임 finalupdate에서 계산됨. 
	Vec2		m_vScale;		//충돌체의 크기. 

public:

	void SetOffsetPos(Vec2 _vPos) { m_vOffsetPos = _vPos; }
	void SetScale(Vec2 _vScale) { m_vScale = _vScale; }

	Vec2 GetOffsetPos() { return m_vOffsetPos; }
	Vec2 GetScale() { return m_vScale; }

public:

	void finalupdate();

	void render(HDC _dc);

public:
	CCollider();
	~CCollider();

	friend class CObject;
};

  • 콜라이더(충돌처리)를 위한 컴포넌트이다.
  • 오브젝트에 부착하는 형태로 설계가 이루어지며, finalupdate를 통해, 최종 처리를 한다
  1. CCollider.cpp
#include "pch.h"
#include "CCollider.h"

#include "CObject.h"
#include "CCore.h"

#include "SelectGDI.h"

CCollider::CCollider()
	: m_pOwner(nullptr)
{
}

CCollider::~CCollider()
{
}

void CCollider::finalupdate()
{
	//부모 오브젝트의 위치를 따라간다.
	Vec2 vObjectPos = m_pOwner->GetPos();
	m_vFinalPos = vObjectPos + m_vOffsetPos;
}

void CCollider::render(HDC _dc)
{

	//변수 이름 없이 만들면, 이름 없는 임시 객체 만들어짐.
	//여기서 바로 만들자마자 소멸시켜버림.
	/*
	1. 임시 객체는 사용하지 않으면 즉시 소멸한다.
	2. 참조 변수나 일반 변수에 저장하면 생명주기를 연장할 수 있다.
	3. 임시 객체를 참조로 반환하면, 소멸될 가능성이 있으므로 조심해야 한다.
	*/
	SelectGDI greenPen(_dc, PEN_TYPE::GREEN);
	SelectGDI hollowBrush(_dc, BRUSH_TYPE::HOLLOW);

	Rectangle(_dc, (int)(m_vFinalPos.x - m_vScale.x / 2.f),
		(int)(m_vFinalPos.y - m_vScale.y / 2.f),
		(int)(m_vFinalPos.x + m_vScale.x / 2.f),
		(int)(m_vFinalPos.y + m_vScale.y / 2.f)
	);

}

  • finalupdate로 오브젝트의 위치를 따라가고.
  • render에서 콜라이더의 범위를 그려준다고 보면 된다.
  1. SelectGDI.h
#pragma once
class SelectGDI
{

private:
	HDC			m_dc;
	HPEN		m_hDefaultPen;
	HBRUSH		m_hDefaultBrush;

public:
	SelectGDI(HDC _dc, PEN_TYPE _ePenType);
	SelectGDI(HDC _dc, BRUSH_TYPE _ePenType);
	~SelectGDI();
};
  1. SelectGDI.cpp
#include "pch.h"
#include "SelectGDI.h"

#include "CCore.h"

SelectGDI::SelectGDI(HDC _dc, PEN_TYPE _ePenType)
	: m_dc(_dc)
	, m_hDefaultBrush(nullptr)
	, m_hDefaultPen(nullptr)
{
	//충돌체는 테두리 라인만 그려져야 함. 
//hollowBrush -> 공백 브러쉬. 테두리만. 
	HPEN hPen = CCore::GetInstance()->GetPen(_ePenType);
	m_hDefaultPen = (HPEN)SelectObject(_dc, hPen);
}

SelectGDI::SelectGDI(HDC _dc, BRUSH_TYPE _eBrushType)
	: m_dc(_dc)
	, m_hDefaultBrush(nullptr)
	, m_hDefaultPen(nullptr)
{
	HBRUSH hHollowBrush = CCore::GetInstance()->GetBrush(_eBrushType);
	m_hDefaultBrush = (HBRUSH)SelectObject(_dc, hHollowBrush);
}

SelectGDI::~SelectGDI()
{ 
	SelectObject(m_dc, m_hDefaultPen);
	SelectObject(m_dc, m_hDefaultBrush);

	DeleteObject(m_hDefaultPen);
	DeleteObject(m_hDefaultBrush);
}

  • 편하게 GDI(브러쉬, 펜)를 사용하기 위한 클래스이다. 소멸자에서 알아서 해제 해준다.
  1. CMissile.cpp, CMonster.cpp, CPlayer.cpp
#include "pch.h"
#include "CMissile.h"

#include "CTimeMgr.h"

CMissile::CMissile()
	:m_vDir(Vec2(1.f,0.f))
	,m_fTheta(PI / 4.f)
{
	//m_vDir.Normalize();
	CreateCollider();
}

CMissile::~CMissile()
{

}

void CMissile::update()
{
	Vec2 vPos = GetPos();

	vPos.x += 600.f * fDT * m_vDir.x;
	vPos.y += 600.f * fDT * m_vDir.y;

	SetPos(vPos);
}

void CMissile::render(HDC _dc)
{
	Vec2 vPos = GetPos();
	Vec2 vScale = GetScale();

	//부모 클래스 만들기. 
	//CObject::render(_dc);
	Ellipse(_dc, (int)(vPos.x - vScale.x / 2.f), (int)(vPos.y - vScale.y / 2.f),
		(int)(vPos.x + vScale.x / 2.f), (int)(vPos.y + vScale.y / 2.f));
}
  • 예시로 미사일의 코드이다. 생성자에 콜라이더를 생성해준다. 이러기 위해서 CreateCollider()함수가 있어야 하는데, 그건 CObject에 있다.
  1. CObject.h
#pragma once

//오브젝트라는 건, 가장 부모격인 녀석. 오브젝트마다 성향이 있음 
//어떤 건 UI, 어떤 건 캐릭터... 

//오브젝트를 일괄적으로 관리할 녀석이 필요함. -> Scene

//이 오브젝트는 부모 클래스. -> 상속받은 오브젝트도 Scene의 업데이트 시점에
//부모쪽에 구현되어있는 update뿐만 아니라, 각 오브젝트(자식)들 마다의 update를 하고 싶음

//충돌할 오브젝트, 충돌하지 않을 오브젝트(UI, 배경등등...) <- 이런 식으로 계속 트리처럼 나누는 건 예외가 있을수도 있음. 
// 따라서 부품기반의 구조가 필요함(컴포넌트 구조)
//구조의 

class CCollider;

class CObject
{

private:
	Vec2			m_vPos;
	Vec2			m_vScale;

	CCollider*		m_pCollider;

public:
	void SetPos(Vec2 _vPos) { m_vPos = _vPos; }
	void SetScale(Vec2 _vScale) { m_vScale = _vScale; }

	Vec2 GetPos() { return m_vPos; }
	Vec2 GetScale() { return m_vScale; }

	CCollider* GetCollider() { return m_pCollider; }

	void CreateCollider();

public:
	virtual void update() = 0;

	//부모에서 함수의 구현이 끝나게, 자식에서 오버라이딩 못하게 막기. 
	virtual void finalupdate() final;
	virtual void render(HDC _dc);

	void component_render(HDC _dc);

public:
	CObject();
	virtual ~CObject();
};
  • CreateCollider로 콜라이더를 생성하고, 그 콜라이더의 주소를 주는 GetCollider함수가 있다.
  • 또한 update가 다 끝난 후, 후 처리를 위한 finalupdate()코드가 final로 추가되었다.
  1. CObject.cpp
#include "pch.h"
#include "CObject.h"

#include "CCollider.h"

CObject::CObject()
	: m_vPos{}
	, m_vScale{} 
	, m_pCollider(nullptr)
{

}

CObject::~CObject() {
	if (m_pCollider != nullptr) delete m_pCollider;
}

void CObject::finalupdate()
{
	if (m_pCollider) m_pCollider->finalupdate();
}

void CObject::render(HDC _dc)
{

	Rectangle(_dc, (int)(m_vPos.x - m_vScale.x / 2.f), (int)(m_vPos.y - m_vScale.y / 2.f),
		(int)(m_vPos.x + m_vScale.x / 2.f), (int)(m_vPos.y + m_vScale.y / 2.f));

}

void CObject::component_render(HDC _dc)
{
	if (m_pCollider != nullptr)	m_pCollider->render(_dc);
}

void CObject::CreateCollider()
{
	m_pCollider = new CCollider;

	m_pCollider->m_pOwner = this;
} 
  • 부착되는 컴포넌트를 위한 component_render함수, 그리고 콜라이더 만드는 CreateCollider함수가 들어갔다
  • finalupdate()함수는 콜라이더의 최종 위치를 결정한다.

WINAPI 카테고리 내 다른 글 보러가기

댓글 남기기