WIN32 Collider, EventManager
카테고리: WINAPI
14일차
- Default
- Client.h
- framework.h
- Resouce.h
- targetver.h
- Engine
1. Header
**1. define.h**
2. struct.h
**3. func.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
7. EventMgr
1. CEventMgr.cpp
2. CEventMgr.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.
**- func.cpp**
- 오늘은 충돌처리, 그리고 이벤트 처리에 대해 공부했다.
- 조만간 소스코드 전체를 Github에 올릴 예정이니, 만약 내 블로그 포스팅을 보시는 분들은 그걸 보고 공부하셔도 될 것이다.
- 충돌처리는 AABB알고리즘으로 구현하였으며, AABB알고리즘은 따로 블로그에 포스팅할 예정이다.
- 그리고 이벤트 매니저가 생겼다. 이는 모든 사건은 프레임 단위로 동일하게 적용 되어야 한다. 것에 기반되어 있다.
- 그렇지 않으면 한 프레임 안에서 삭제하고, 생성하고가 생겨서 그 오브젝트를 참조하고 있는 오브젝트의 처리에 에러가 생긴다.
- define.h
enum class EVENT_TYPE {
CREATE_OBJECT,
DELETE_OBJECT,
SCENE_CHANGE,
END,
};
- 맨 끝에 이 부분이 추가 되었다.
0-1. func.h
#pragma once
class CObject;
void CreateObject(CObject* _pObj, GROUP_TYPE _eGroup);
- 당연히 pch.h에 func.h 파일을 인클루드 해준다.
0-2. func.cpp
#include "pch.h"
#include "func.h"
#include "CObject.h"
#include "CEventMgr.h"
void CreateObject(CObject* _pObj, GROUP_TYPE _eGroup)
{
tEvent createEvn = {};
createEvn.eEven = EVENT_TYPE::CREATE_OBJECT;
createEvn.lParam = (DWORD_PTR)_pObj;
createEvn.wParam = (DWORD_PTR)_eGroup;
CEventMgr::GetInstance()->AddEvent(createEvn);
}
- CEventMgr.h
#pragma once
struct tEvent {
EVENT_TYPE eEven;
DWORD_PTR lParam;
DWORD_PTR wParam;
};
//충돌은 만들었지만, 실제 충돌을 활용하려고 하면. 때리고 삭제시키고가 필요한데.
//EX) Scene변경 -> 렌더링을 이미 다 끝마쳤는데?
//어떤 이벤트가 발생하면, 해당 프레임의 모두가 적용되어야 한다.
//같은 사건인데도 다르게 받아들여질수가 있음 <- 어떤 사건, 이벤트들을 프레임 단위로 동기화 해줘야 하지 않을까??
//바로 죽이지 않음 -> 지연처리를 통해서 다음 프레임에 모두 적용시킬 것.
class CEventMgr
{
SINGLE(CEventMgr)
private:
vector<tEvent> m_vecEvent;
vector<CObject*> m_vecDeadScheduled; //삭제 예정인 오브젝트들.
public:
void update();
private:
void Excute(const tEvent& _eve);
public:
void AddEvent(const tEvent& _eve) {
m_vecEvent.push_back(_eve);
}
//삭제를 하면, 다음 프레임에서 삭제 적용.
//삭제된 오브젝트를 원래 알고있던 오브젝트는 걔가 삭제 됐는지 어떤지 알 방법이 없음.
//그래서 저 녀석을 삭제할 거라고 트리거를 on시킴. 그 오브젝트는 Dead상태가 됨.
//다음 프레임에서는 Dead상태로 존재해서, 참조하는 오브젝트들이 얘 곧 죽겠구나 알 수 있음.
//또 다음 프레임에 삭제 예정인 애들을 삭제시킴.
};
- CEventMgr.cpp
#include "pch.h"
#include "CEventMgr.h"
#include "CObject.h"
#include "CSceneMgr.h"
#include "CScene.h"
CEventMgr::CEventMgr()
: m_vecEvent{}
{
}
CEventMgr::~CEventMgr() {
}
void CEventMgr::update()
{
//==================================================
//이전 프레임에서 등록해준 Dead Object들을 삭제한다.
//==================================================
for (auto deadObjectPtr : m_vecDeadScheduled) {
delete deadObjectPtr;
}
m_vecDeadScheduled.clear();
// ===============
// Event 처리.
// ===============
for (const auto& event_node : m_vecEvent) {
Excute(event_node);
}
m_vecEvent.clear();
}
void CEventMgr::Excute(const tEvent& _eve)
{
switch (_eve.eEven) {
case EVENT_TYPE::CREATE_OBJECT:
{
// lParam : Object Address
// wParam : Group Type
CObject* pNewObj = (CObject*)_eve.lParam;
GROUP_TYPE eType = (GROUP_TYPE)_eve.wParam;
CSceneMgr::GetInstance()->GetCurScene()->AddObject(pNewObj, eType);
}
break;
case EVENT_TYPE::DELETE_OBJECT:
{
// lParam : Object Address
// wParam : NULL
// Object를 Dead 상태로 변경하고 -> 삭제 예정 오브젝트들을 모아둔다.
CObject* pDeleteObj = (CObject*)_eve.lParam;
pDeleteObj->SetDead();
m_vecDeadScheduled.push_back(pDeleteObj);
}
break;
case EVENT_TYPE::SCENE_CHANGE:
break;
}
}
- 이벤트를 받아서, EventMgr에서 생성, 삭제, 씬 생성들을 한다.
- 또한 삭제는 1프레임의 유예를 둔 후에(다른 오브젝트들이 알 수 있게) 진짜로 삭제한다.
- 일괄적으로 vector에 쌓인 이벤트들을 처리한다.
- CCore.cpp
void CCore::progress() {
// ============
// Manager Update
// ============
CTimeMgr::GetInstance()->update();
CkeyMgr::GetInstance()->update();
// ============
// Scene 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();
//렌더링까지 전부 다 끝난 후에 EventMgr에서 이벤트 지연 처리.
// ============
// 이벤트 지연 처리.
// ============
CEventMgr::GetInstance()->update();
}
- 이벤트 매니저는 코어의 렌더링까지 전부 다 끝난 후에 update를 처리한다(따라서 안전한 시기에 한다)
- CObject.h
#pragma once
//오브젝트라는 건, 가장 부모격인 녀석. 오브젝트마다 성향이 있음
//어떤 건 UI, 어떤 건 캐릭터...
//오브젝트를 일괄적으로 관리할 녀석이 필요함. -> Scene
//이 오브젝트는 부모 클래스. -> 상속받은 오브젝트도 Scene의 업데이트 시점에
//부모쪽에 구현되어있는 update뿐만 아니라, 각 오브젝트(자식)들 마다의 update를 하고 싶음
//충돌할 오브젝트, 충돌하지 않을 오브젝트(UI, 배경등등...) <- 이런 식으로 계속 트리처럼 나누는 건 예외가 있을수도 있음.
// 따라서 부품기반의 구조가 필요함(컴포넌트 구조)
//구조의
class CCollider;
class CObject
{
private:
wstring m_ObjName;
Vec2 m_vPos;
Vec2 m_vScale;
CCollider* m_pCollider;
bool m_bAlive; //자기 자신이 활성화 or 비활성화. (삭제 전용)
bool m_bEnable; //일시적인 활성화 or 비활성화.
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; }
void SetName(const wstring& _strName) { m_ObjName = _strName; }
const wstring& GetName() { return m_ObjName; }
CCollider* GetCollider() { return m_pCollider; }
void CreateCollider();
virtual void OnCollision(CCollider* _pOther) {};
virtual void OnCollisionEnter(CCollider* _pOther) {};
virtual void OnCollisionExit(CCollider* _pOther) {};
bool IsDead() {
return !m_bAlive;
}
private:
void SetDead() { m_bAlive = false; }
public:
virtual void update() = 0;
//부모에서 함수의 구현이 끝나게, 자식에서 오버라이딩 못하게 막기.
virtual void finalupdate() final;
virtual void render(HDC _dc);
void component_render(HDC _dc);
public:
CObject();
virtual ~CObject();
friend class CEventMgr;
};
- 다음 프레임에서 죽음을 알려주기 위한 m_bAlive변수를 만든다.
- 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)
{
Vec2 vLeftPos = _pLeftCol->GetFinalPos();
Vec2 vLeftScale = _pLeftCol->GetScale();
Vec2 vRightPos = _pRightCol->GetFinalPos();
Vec2 vRightScale = _pRightCol->GetScale();
if ((abs(vRightPos.x - vLeftPos.x) < (vLeftScale.x + vRightScale.x) / 2.f) &&
(abs(vRightPos.y - vLeftPos.y) < (vLeftScale.y + vRightScale.y) / 2.f)) {
return true;
}
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);
}
}
- AABB 알고리즘을 사용하여 충돌 검사를 한다.
- 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값.
UINT m_iCol;
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; }
Vec2 GetFinalPos() { return m_vFinalPos; }
CObject* GetObj() { return m_pOwner; }
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;
};
댓글 남기기