WIN32 Collider
카테고리: WINAPI
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(펜, 브러쉬) 편하게. 를 건드렸다고 보면 될 거 같다. 그러면 코드를 보자!
- 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,
};
- 펜 설정을 위한 브러시 타입, 펜 타입을 추가해줬다.
- 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를 추가해줬다.
- 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함수를 만들어줬다.
- 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));
}
- 처음 설정할 때, 펜을 만들어준다. 정도? 당연히 생성자, 소멸자 세팅도 해준다.
- 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이 더 좋을 거 같기도… 그렇게 로딩, 검색이 빈번한 게 아니라서 괜찮나?
- 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값 준다!!
- 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를 통해, 최종 처리를 한다
- 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에서 콜라이더의 범위를 그려준다고 보면 된다.
- 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();
};
- 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(브러쉬, 펜)를 사용하기 위한 클래스이다. 소멸자에서 알아서 해제 해준다.
- 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에 있다.
- 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로 추가되었다.
- 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()함수는 콜라이더의 최종 위치를 결정한다.
댓글 남기기