WIN32 Collider
카테고리: WINAPI
13일차
- 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
**6. CollisionMgr
1. CCollisionMgr.cpp
2. CCollisionMgr.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.
- 오늘은 Collider(충돌처리)에 대해 구현하였다.
- 그룹 기준(Player, Bullet, Monster등) 충돌 체크 매트릭스를 사용한다.
- 오늘은 아직 AABB알고리즘이나 OBB알고리즘으로 충돌을 처리하는 건 안했고, OnCollision(충돌 중), OnCollisionEnter(지금 충돌에 들어감), OnCollisionExit(지금 충돌에서 나옴)과 같은 설계에 중점으로 하였다.
- CCore.cpp
#include "pch.h"
#include "CCore.h"
#include "CTimeMgr.h"
#include "CkeyMgr.h"
#include "CSceneMgr.h"
#include "CCollisionMgr.h"
#include "CPathMgr.h"
void CCore::progress() {
//Manager Update
CTimeMgr::GetInstance()->update();
CkeyMgr::GetInstance()->update();
CSceneMgr::GetInstance()->update();
CCollisionMgr::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();
}
- 헤더에 새로 추가된 CCollisionMgr.h을 추가하고 Progress함수에 새로 update를 넣었다.
- CPlayer.cpp, CMonster.cpp
CPlayer::CPlayer()
: m_pTex(nullptr)
{
m_pTex = CResMgr::GetInstance()->LoadTexture(L"PlayerTex", L"texture\\Tenshi.bmp");
CreateCollider();
GetCollider()->SetOffsetPos(Vec2(0.f, 12.f));
GetCollider()->SetScale(Vec2(25.f, 25.f));
}
...
CMonster::CMonster()
: m_fSpeed(100.f)
, m_vCenterPos(Vec2(0.f, 0.f))
, m_fMaxMoveDistance(50.f)
, m_iDir(1)
{
CreateCollider();
GetCollider()->SetScale(Vec2(45.f, 45.f));
}
- 콜라이더를 넣고 스케일, 오프셋등을 조절해주었다.
- CScene_Manager.cpp
#include "pch.h"
#include "CScene_start.h"
#include "CObject.h"
#include "CCore.h"
#include "CTexture.h"
#include "CPlayer.h"
#include "CMonster.h"
#include "CCollisionMgr.h"
#include "CPathMgr.h"
CScene_Start::CScene_Start()
{
}
CScene_Start::~CScene_Start()
{
}
void CScene_Start::Enter()
{
//Object 추가.
//실제 생성된 객체는 플레이어, 주소를 받은 건 부모 클래스.
CObject* pObj = new CPlayer;
pObj->SetPos(Vec2(640.f, 384.f));
pObj->SetScale(Vec2(100.f, 100.f));
AddObject(pObj, GROUP_TYPE::PLAYER);
//몬스터 배치.
int iMonCount = 15;
float fMoveDist = 25.f;
float fObjScale = 50.f;
Vec2 vResolution = CCore::GetInstance()->GetResolution();
CMonster* pMonsterObj = nullptr;
float fTerm = (vResolution.x - ((fMoveDist + fObjScale / 2.f) * 2)) / (float)(iMonCount - 1);
for (int idx = 0; idx < iMonCount; idx++) {
//Monster Object 추가.
pMonsterObj = new CMonster;
pMonsterObj->SetPos(Vec2((fMoveDist + fObjScale / 2.f) + (float)idx * fTerm, 50.f));
pMonsterObj->SetCenterPos(pMonsterObj->GetPos());
pMonsterObj->SetMaxMoveDistance(fMoveDist);
pMonsterObj->SetScale(Vec2(fObjScale, fObjScale));
AddObject(pMonsterObj, GROUP_TYPE::MONSTER);
}
//충돌 지점.
//Player 그룹과 Monster그룹간의 충돌 체크
//update에서 로직, finalupdate에서 최종 움직임 완료
//이제 새로운 충돌이 발생할수도 있음.
CCollisionMgr::GetInstance()->CheckGroup(GROUP_TYPE::PLAYER, GROUP_TYPE::MONSTER);
}
void CScene_Start::Exit()
{
}
- 생성되는 오브젝트의 태크(그룹)을 바꿔주었고, CheckGroup함수로 저 태그들끼리 충돌하도록 바꾸었다.
- CScene.h
#pragma once
//전방선언하는 이유는 컴파일 속도에 영향을 주지 않기 위해.
class CObject;
class CScene
{
private:
vector<CObject*> m_arrObj[(UINT)GROUP_TYPE::END]; //벡터 안에 모든 오브젝트 집어 넣겠다. 이런 특성(요소)를 가진만큼 나눠주기.
//달리말하면 그룹 갯수만큼 나눠주기.
wstring m_strName; //Scene 이름
public:
void SetName(const wstring& _strName) { m_strName = _strName; }
const wstring& GetName() { return m_strName; }
void update();
void finalupdate();
void render(HDC _dc);
virtual void Enter() = 0; //해당 Scene에 진입 시 호출.
virtual void Exit() = 0; //해당 Scene에 탈출 시 호출.
public:
//클래스는 헤더에 구현하면 인라인 처리가 됨.
//따라서 함수 호출 비용이 사라짐.
void AddObject(CObject* _pObj, GROUP_TYPE _eType)
{
m_arrObj[(UINT)_eType].push_back(_pObj);
}
const vector<CObject*>& GetGroupObject(GROUP_TYPE _eType)
{
return m_arrObj[(UINT)_eType];
}
public:
CScene();
//소멸자의 가상함수 해줘야함. 씬 매니저가 모든 Scene을 부모 포인터로 관리함.
// CSceneMgr에서 씬을 소멸시킬때, 소멸자는 부모인 CScene만 호출됨.
virtual ~CScene();
};
- 그 그룹(태그)에 있는 오브젝트 벡터의 참조를 건네주는 GetGroupObject함수를 생성하였다.
- CScene.cpp
#include "pch.h"
#include "CScene.h"
#include "CObject.h"
CScene::CScene()
{
}
CScene::~CScene()
{
for (UINT typeIDX = 0; typeIDX < (UINT)GROUP_TYPE::END; typeIDX++) {
for (size_t objIDX = 0; objIDX < m_arrObj[typeIDX].size(); objIDX++) {
//m_arrObj[그룹][물체] 삭제.
delete m_arrObj[typeIDX][objIDX];
}
//씬이 사라지면, 그 씬의 벡터들도 다 사라짐.
//STL의 RAII가 알아서 삭제하기 때문.
}
}
void CScene::update()
{
for (UINT typeIDX = 0; typeIDX < (UINT)GROUP_TYPE::END; typeIDX++) {
for (size_t objIDX = 0; objIDX < m_arrObj[typeIDX].size(); objIDX++) {
m_arrObj[typeIDX][objIDX]->update();
}
}
}
//움직이고 했던 걸, 마지막으로 업데이트 함.
//충돌체가 플레이어 따라가게 함, 충돌 처리.
void CScene::finalupdate()
{
for (UINT typeIDX = 0; typeIDX < (UINT)GROUP_TYPE::END; typeIDX++) {
for (size_t objIDX = 0; objIDX < m_arrObj[typeIDX].size(); objIDX++) {
m_arrObj[typeIDX][objIDX]->finalupdate();
}
}
}
void CScene::render(HDC _dc)
{
for (UINT typeIDX = 0; typeIDX < (UINT)GROUP_TYPE::END; typeIDX++) {
for (size_t objIDX = 0; objIDX < m_arrObj[typeIDX].size(); objIDX++) {
m_arrObj[typeIDX][objIDX]->render(_dc);
}
}
}
- 크게 바뀐 건 없지만, finalupdate()를 보여주려고…
- CCollider.h
#pragma once
class CObject;
//오브젝트의 영역을 검사하는 역할을 가짐.
//충돌체가 그냥 오브젝트의 포지션에 있다면, 충돌체가 머리에만있을 경우, 몸통에 충돌체 따로 있을 경우...
//부위별로 따로 충돌체 두는 그런 거 못해서.
//1. 충돌은 그룹별. (그래서 Scene에서도 그룹별로 나눴음...)
//ex) Player <-> Monster이렇게.
class CCollider
{
private:
static UINT g_iNextID; //
CObject* m_pOwner; //collider를 소유하고 있는 오브젝트.
Vec2 m_vOffsetPos; //오브젝트로부터 상대적인 위치.
Vec2 m_vFinalPos; //매 프레임 finalupdate에서 계산됨.
Vec2 m_vScale; //충돌체의 크기.
UINT m_iID; //충돌체 고유한 ID값.
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; }
UINT GetID() { return m_iID; }
public:
void finalupdate();
void render(HDC _dc);
public:
//충돌 시점 함수.
void OnCollision(CCollider* _pOther);
void OnCollisionEnter(CCollider* _pOther);
void OnCollisionExit(CCollider* _pOther);
//복사 생성자 삭제.
CCollider& operator = (CCollider& _origin) = delete;
public:
CCollider();
CCollider(const CCollider& _origin);
~CCollider();
friend class CObject;
};
- 콜라이더만의 고유한 ID값과, 그걸 주는 GetID그리고 충돌함수들이 생기고, 복사 생성자, 대입연산자등에 대한 처리를 해줬다.
- CCollider.cpp
#include "pch.h"
#include "CCollider.h"
#include "CObject.h"
#include "CCore.h"
#include "SelectGDI.h"
UINT CCollider::g_iNextID = 0;
//기본 복사 생성자는 그냥 얕은 복사. m_iID는 복사되면, 그대로 같은 값을 가지게 됨.
CCollider::CCollider()
: m_pOwner(nullptr)
, m_iID(g_iNextID++)
{
}
CCollider::CCollider(const CCollider& _origin)
: m_pOwner(nullptr)
, m_vOffsetPos(_origin.m_vOffsetPos)
, m_vScale(_origin.m_vScale)
, m_iID(g_iNextID++)
{
}
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)
);
}
void CCollider::OnCollision(CCollider* _pOther)
{
}
void CCollider::OnCollisionEnter(CCollider* _pOther)
{
}
void CCollider::OnCollisionExit(CCollider* _pOther)
{
}
- 충돌 함수, 복사 생성자, 그리고 고유 ID를 위한 정적 변수등이 추가되었다.
- CCollisionMgr.h
#pragma once
class CCollider;
union COOLIDER_ID {
struct {
UINT Left_id;
UINT Right_id;
};
ULONGLONG ID;
};
class CCollisionMgr
{
SINGLE(CCollisionMgr)
private:
//무수한 갯수 * 무수한 갯수이고, 또한 빠르게 찾아올 수 있어야함.
// 자료구조는 따라서 HashTable을 해야함. (탐색 특화) ->
// 검색 조건은 두 충돌체들의 Key값이 있어야함. -> 충돌체 간의 고유한 ID값 필요.
unordered_map<ULONGLONG, bool> m_mapColInfo; //충돌체 간의 이전 프레임 충돌 정보.
UINT m_arrCheck[(UINT)GROUP_TYPE::END]; //그룹간의 충돌 체크 매트릭스
public:
void update();
void CheckGroup(GROUP_TYPE eLeft, GROUP_TYPE e_Right);
void Reset() {
memset(m_arrCheck, 0, sizeof(UINT) * (UINT)GROUP_TYPE::END);
}
private:
void CollisionGroupUpdate(GROUP_TYPE _eLeft, GROUP_TYPE _eRight);
bool IsCollision(CCollider* _pLeftCol, CCollider* _pRightCol);
};
- union의 특성(가장 큰 변수의 내부 공간을 공유한다)를 써서, Collider의 ID값(uint형)을 받아, ULONGLONG으로 LEFT, RIGHT로 나누어서 고유 ID를 생성한다.
- 또한 충돌체 간의 프레임 충돌 정보를 저장하기 위해(이전에 충돌했는지 등등…) unordered_map을 사용했다.(강의에선 그냥 map썼다.)
- 그 외에도 충돌 체크 매트릭스(태그별)등을 만들었고.
- CCollisionMgr.cpp
#include "pch.h"
#include "CCollisionMgr.h"
#include "CSceneMgr.h"
#include "CScene.h"
#include "CObject.h"
#include "CCollider.h"
//유니온은 제일 큰 사이즈를 기준으로, 메모리를 공유함.
CCollisionMgr::CCollisionMgr()
:m_arrCheck{}
{
}
CCollisionMgr::~CCollisionMgr() {
}
void CCollisionMgr::update()
{
//표를 확인하면서, 비트값이 ON되어 있다면 충돌하고,
//OFF라면 충돌하지 않음.
for (UINT iRow = 0; iRow < (UINT)GROUP_TYPE::END; iRow++) {
for (UINT iCol = iRow; iCol < (UINT)GROUP_TYPE::END; iCol++) {
if (m_arrCheck[iRow] & (1 << iCol)) {
//해당 그룹간의 충돌을 진행한다.
//따라서 현재 Scene에서 충돌체를 검사한다.
CollisionGroupUpdate((GROUP_TYPE)iRow, (GROUP_TYPE)iCol);
}
}
}
}
void CCollisionMgr::CollisionGroupUpdate(GROUP_TYPE _eLeft, GROUP_TYPE _eRight)
{
CScene* pCurScene = CSceneMgr::GetInstance()->GetCurScene();
//우리가 하려는 의도랑 조금 어긋나는 부분이 있음.
//반환타입이 참조이니, 우리가 레퍼런스를 가져온 것.
//vecLeft는 주소값의 복사본(지역변수)임. 따라서 &로 받아야함.
const vector<CObject*>& vecLeft = pCurScene->GetGroupObject(_eLeft);
const vector<CObject*>& vecRight = pCurScene->GetGroupObject(_eRight);
unordered_map<ULONGLONG, bool>::iterator colliderMapIter;
for (auto leftIDX = 0; leftIDX < vecLeft.size(); leftIDX++) {
//충돌체를 보유하지 않는 경우
if (nullptr == vecLeft[leftIDX]->GetCollider()) continue;
for (auto rightIDX = 0; rightIDX < vecRight.size(); rightIDX++) {
//충돌체를 보유하지 않는 경우. or 자기 자신과의 충돌인 경우.
if (nullptr == vecRight[rightIDX]->GetCollider() || vecLeft[leftIDX] == vecRight[rightIDX]) continue;
CCollider* pLeftCollider = vecLeft[leftIDX]->GetCollider();
CCollider* pRightCollider = vecRight[rightIDX]->GetCollider();
//충돌체들 간의 조합 ID
COOLIDER_ID ID;
ID.Left_id = pLeftCollider->GetID();
ID.Right_id = pRightCollider->GetID();
colliderMapIter = m_mapColInfo.find(ID.ID);
//충돌 정보가 미 등록 상태인 경우 등록(충돌하지 않았다. 로)
if (colliderMapIter == m_mapColInfo.end()) {
//두 조합의 검사를 최초로 했을 경우.
m_mapColInfo.insert(make_pair(ID.ID, false));
colliderMapIter = m_mapColInfo.find(ID.ID);
}
//현재 충돌한 경우.
if (IsCollision(pLeftCollider, pRightCollider)) {
if (colliderMapIter->second == false) {
//이번 프레임에 막 충돌한 경우.
pLeftCollider->OnCollisionEnter(vecRight[rightIDX]->GetCollider());
pRightCollider->OnCollisionEnter(vecLeft[leftIDX]->GetCollider());
colliderMapIter->second = true;
}
else if (colliderMapIter->second == true) {
//이전 프레임에도 충돌하고 있었을 경우.
pLeftCollider->OnCollision(vecRight[rightIDX]->GetCollider());
pRightCollider->OnCollision(vecLeft[leftIDX]->GetCollider());
}
}
else {
//충돌하지 않은 경우.
if (colliderMapIter->second == true) {
//막 지금 충돌에서 벗어난 경우.
pLeftCollider->OnCollisionExit(vecRight[rightIDX]->GetCollider());
pRightCollider->OnCollisionExit(vecLeft[leftIDX]->GetCollider());
colliderMapIter->second = false;
}
}
}
}
}
bool CCollisionMgr::IsCollision(CCollider* _pLeftCol, CCollider* _pRightCol)
{
return false;
}
void CCollisionMgr::CheckGroup(GROUP_TYPE _eLeft, GROUP_TYPE _eRight)
{
//그룹값 중에서 더 작은 타입을 행으로, 더 큰 값을 열(비트)로 사용.
//
UINT iRow = (UINT)_eLeft;
UINT iCol = (UINT)_eRight;
if (iCol < iRow) {
iRow = (UINT)_eRight;
iCol = (UINT)_eLeft;
}
if (m_arrCheck[iRow] & (1 << iCol)) {
m_arrCheck[iRow] &= ~(1 << iCol);
}
else {
m_arrCheck[iRow] |= (1 << iCol);
}
}
- 주석에 상세히 적혀있으니 위쪽은 설명하지 않고
- CheckGroup은 비트마스킹을 통한 값을 비트 연산자를 통해 체크 혹은 해제를 해주는 기능이다.
댓글 남기기