이번에는 CCore 클래스의 progess 함수에 대해 살펴보겠다.
void CCore::progress() {
// ============
// Manager Update
// ============
CTimeMgr::GetInstance()->update();
CkeyMgr::GetInstance()->update();
CCamera::GetInstance()->update();
CSoundMgr::GetInstance()->update();
// ============
// Scene Update
// ============
CSceneMgr::GetInstance()->update();
// 충돌 체크.
CCollisionMgr::GetInstance()->update();
//UI 이벤트 체크
CUIMgr::GetInstance()->update();
ItemMgr::GetInstance()->update();
// ============
// Rendering
// ============
// Draw시작.
ID2D1HwndRenderTarget* pRenderTarget = Direct2DMgr::GetInstance()->GetRenderTarget();
//한 프레임당 그림 그리기 시작.
pRenderTarget->BeginDraw();
Clear();
//Direct2D 버전 렌더링
CSceneMgr::GetInstance()->render(pRenderTarget);
//한 프레임 렌더링 끝.
HRESULT hr = pRenderTarget->EndDraw();
if (FAILED(hr)) {
MessageBox(nullptr, L"렌더링 실패!", L"오류", MB_OK);
}
// Draw 끝.
//렌더링까지 전부 다 끝난 후에 EventMgr에서 이벤트 지연 처리.
// ============
// 이벤트 지연 처리.
// ============
CEventMgr::GetInstance()->update();
}
wWinMain() 함수에서 메세지를 처리하지 않는 대부분의 시간은 위의 progess() 함수가 수행된다.
progess() 함수가 한 번 호출되고 종료되는 상황이 1프레임의 작업이 된다.
이때 SingleTon으로 구현된 매니저들의 update()와 CSceneMgr에 등록된 현재 씬(장면)의 render() 함수를 호출해 현재 씬의 등록된 객체들의 update() 와 render() 를 호출해 객체들의 상태를 변화시키거나 화면에 그려주는 작업을 진행한다.
이후 마지막에 지금까지 EventMgr에 등록된 이벤트들을 처리해 준다.
<CScene.h> , <CScene.cpp>
CScene 클래스는 직접적으로 객체를 할당할 수 없는 추상 클래스로 구현하였다.
이유는 모든 씬(장면)들은 화면에 진입했을 때와 화면을 탈출할 때의 해야 할 일들이 전부 다를것이다. 그렇기 때문에 일부 함수들은 순수 가상 함수로 표현했기 때문에 CScene클래스는 상속을 받아 사용해야하는 클래스로 제작하였다.
CScene 클래스를 상속받는 클래스는 다음과 같다.
- Scene_Main : 메인 화면이 되는 씬(장면).
- Scene_Select_Character : 캐릭터를 선택하는 씬
- Scene_Select_Weapon : 무기를 선택하는 씬
- Scene_Start : 전투가 일어나는 씬
- Scene_Shop : 상점 씬
- Scene_Run_End : 게임 실패 or 성공 시 나오는 결산 화면
#pragma once
#include "global.h"
#include "CMonFactory.h"
//전방선언하는 이유는 컴파일 속도에 영향을 주지 않기 위해.
class CObject;
class CMonster;
class CScene
{
static bool isPause;
private:
vector<CObject*> m_arrObj[(UINT)GROUP_TYPE::END]; //벡터 안에 모든 오브젝트 집어 넣겠다. 이런 특성(요소)를 가진만큼 나눠주기.
//달리말하면 그룹 갯수만큼 나눠주기.
SCENE_TYPE m_eType;
wstring m_strName; //Scene 이름
CObject* m_pPlayer; //Player
public:
void SetName(const wstring& _strName) { m_strName = _strName; }
const wstring& GetName() { return m_strName; }
SCENE_TYPE GetSceneType() { return m_eType; }
void SetSceneType(SCENE_TYPE _eType) { m_eType = _eType; }
CObject* GetPlayer() { return m_pPlayer; }
virtual void start();
virtual void update();
virtual void finalupdate();
virtual void render(ID2D1HwndRenderTarget* _pRender);
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);
}
void RegisterPlayer(CObject* _pPlayer) { m_pPlayer = _pPlayer; }
const vector<CObject*>& GetGroupObject(GROUP_TYPE _eType)
{
return m_arrObj[(UINT)_eType];
}
vector<CObject*>& GetUIGroup() { return m_arrObj[(UINT)GROUP_TYPE::UI]; }
void DeleteGroup(GROUP_TYPE _eGroup);
void DeleteAll();
ID2D1Bitmap* CreateCompositeMapBitmap(const wstring &tag);
static void ChangePause(bool _bPause);
static bool GetPause() { return isPause; }
void AllDropItemRetrieve();
public:
CScene();
//소멸자의 가상함수 해줘야함. 씬 매니저가 모든 Scene을 부모 포인터로 관리함.
// CSceneMgr에서 씬을 소멸시킬때, 소멸자는 부모인 CScene만 호출됨.
virtual ~CScene();
friend class CMonster;
};
#include "pch.h"
#include "CScene.h"
#include "CObject.h"
#include "func.h"
#include "CTile.h"
#include "CTimeMgr.h"
#include "CResMgr.h"
#include "CPathMgr.h"
#include "CCamera.h"
#include "CCore.h"
#include "CTexture.h"
#include "Direct2DMgr.h"
#include "CMonster.h"
#include "CGround.h"
#include "CImage.h"
bool CScene::isPause = false;
CScene::CScene()
: m_iTileX(0)
, m_iTileY(0)
, m_pPlayer(nullptr)
{
}
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::AllDropItemRetrieve()
{
UINT typeIDX = (UINT)GROUP_TYPE::DROP_ITEM;
for (size_t objIDX = 0; objIDX < m_arrObj[typeIDX].size(); objIDX++) {
CMonster* dropItem = static_cast<CMonster*>(m_arrObj[typeIDX][objIDX]);
if (dropItem->Getinfo().m_eMonType == MON_TYPE::DROP_ITEM) {
dropItem->SetRecogRange(9999.f);
dropItem->SetSpeed(500.f);
}
}
}
void CScene::start()
{
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]->start();
}
}
}
void CScene::update()
{
for (UINT typeIDX = 0; typeIDX < (UINT)GROUP_TYPE::END; typeIDX++) {
for (size_t objIDX = 0; objIDX < m_arrObj[typeIDX].size(); objIDX++) {
if (!m_arrObj[typeIDX][objIDX]->IsDead()) {
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++) {
//Final Update는 돌려줌. 내부적으로 Component들의 마무리 단계 업데이트(충돌처리나, 참조관계등)
m_arrObj[typeIDX][objIDX]->finalupdate();
}
}
}
void CScene::render(ID2D1HwndRenderTarget* _pRender)
{
for (UINT typeIDX = 0; typeIDX < (UINT)GROUP_TYPE::END; typeIDX++) {
auto ObjVecIter = m_arrObj[typeIDX].begin();
for (; ObjVecIter != m_arrObj[typeIDX].end();) {
if (!(*ObjVecIter)->IsDead()) {
(*ObjVecIter)->render(_pRender);
ObjVecIter++;
}
else {
//Dead상태일 경우엔 렌더링에서 삭제하기.
ObjVecIter = m_arrObj[typeIDX].erase(ObjVecIter);
}
}
}
}
void CScene::DeleteGroup(GROUP_TYPE _eGroup)
{
Safe_Delete_Vec<CObject*>(m_arrObj[(UINT)_eGroup]);
}
void CScene::DeleteAll()
{
for (UINT GroupIdx = 0; GroupIdx < (UINT)GROUP_TYPE::END; GroupIdx++) {
if (GroupIdx == (UINT)GROUP_TYPE::PLAYER)
{
m_arrObj[(UINT)GROUP_TYPE::PLAYER].clear();
continue;
}
else if (GroupIdx == (UINT)GROUP_TYPE::WEAPON) {
m_arrObj[(UINT)GROUP_TYPE::WEAPON].clear();
}
DeleteGroup((GROUP_TYPE)GroupIdx);
}
}
ID2D1Bitmap* CScene::CreateCompositeMapBitmap(const wstring &tag)
{
Direct2DMgr* pD2DMgr = Direct2DMgr::GetInstance();
auto splitBitmaps = Direct2DMgr::GetInstance()->GetSplitBitmaps(tag);
const int gridCount = 36;
// 각 타일는 원래 MakeTile에서 다음과 같이 배치됨:
// 위치: (TILE_SIZE/4 + (TILE_SIZE/2 * tileX), TILE_SIZE/4 + (TILE_SIZE/2 * tileY))
// 크기: TILE_SIZE/2 × TILE_SIZE/2
// (TILE_SIZE = 64 → offset = 16, tileDrawSize = 32)
const float offset = 16.0f;
const float tileDrawSize = 32.0f;
// 전체 합성 비트맵의 크기 계산:
// 좌측 여백 offset에서 시작해 gridCount * tileDrawSize 만큼 확장.
const UINT compositeWidth = (UINT)(offset + tileDrawSize * gridCount);
const UINT compositeHeight = compositeWidth; // 정사각형
// 3. 오프스크린 렌더 타겟(비트맵 렌더 타겟) 생성
ID2D1Bitmap* pCompositeBitmap = nullptr;
ComPtr<ID2D1BitmapRenderTarget> pBitmapRT = nullptr;
HRESULT hr = Direct2DMgr::GetInstance()->GetRenderTarget()->CreateCompatibleRenderTarget(
D2D1::SizeF((FLOAT)compositeWidth, (FLOAT)compositeHeight),
&pBitmapRT
);
if (FAILED(hr))
return nullptr;
pBitmapRT->BeginDraw();
// 배경색 (예: 흰색)으로 클리어
pBitmapRT->Clear(D2D1::ColorF(D2D1::ColorF::Black));
// 4. 그리드 순회: 각 타일의 서브 비트맵 인덱스 결정 후 합성
for (int tileY = 0; tileY < gridCount; tileY++)
{
for (int tileX = 0; tileX < gridCount; tileX++)
{
int tileIdx = 0;
int rest = 0;
// 모서리 타일 처리
if (tileY == 0 && tileX == 0) tileIdx = 0;
else if (tileY == 0 && tileX == 35) tileIdx = 7;
else if (tileY == 35 && tileX == 0) tileIdx = 56;
else if (tileY == 35 && tileX == 35)tileIdx = 63;
// 상단 테두리
else if (tileY == 0 && (tileX >= 1 && tileX <= 34))
{
rest = tileX % 7;
if (rest == 0) rest++;
tileIdx = rest;
}
// 하단 테두리
else if (tileY == 35 && (tileX >= 1 && tileX <= 34))
{
rest = tileX % 7;
if (rest == 0) rest++;
tileIdx = rest + 56;
}
// 좌측 테두리
else if (tileX == 0 && (tileY >= 1 && tileY <= 34))
{
rest = tileY % 7;
if (rest == 0) rest++;
tileIdx = rest * 8;
}
// 우측 테두리
else if (tileX == 35 && (tileY >= 1 && tileY <= 34))
{
rest = tileY % 7;
if (rest == 0) rest++;
tileIdx = rest * 8 + 7;
}
// 내부 타일: 테두리 제외하고 랜덤하게 (테두리 인덱스 제외)
else
{
int randX = rand() % 7;
if (randX == 0) randX = 1;
int randY = rand() % 7;
if (randY == 0) randY = 1;
tileIdx = randY * 8 + randX;
}
// 5. 타일의 배치 위치 계산
// MakeTile에서의 위치: X = offset + tileX * tileDrawSize, Y = offset + tileY * tileDrawSize
float destLeft = offset + tileX * tileDrawSize;
float destTop = offset + tileY * tileDrawSize;
D2D1_RECT_F destRect = D2D1::RectF(destLeft, destTop, destLeft + tileDrawSize, destTop + tileDrawSize);
// 6. 서브 비트맵의 원본 영역: 여기서는 전체 서브 이미지 (크기 TILE_SIZE×TILE_SIZE)
D2D1_RECT_F srcRect = D2D1::RectF(0, 0, (FLOAT)TILE_SIZE, (FLOAT)TILE_SIZE);
// 7. 타일 텍스처에서 해당 서브 이미지를 DrawBitmap (타일 이미지가 64x64에서 32x32로 축소됨)
if (tileIdx >= 0 && tileIdx < (int)splitBitmaps.size())
{
pBitmapRT->DrawBitmap(
splitBitmaps[tileIdx], // 해당 서브 비트맵
destRect, // 합성될 위치 및 크기
1.0f, // 불투명도
D2D1_BITMAP_INTERPOLATION_MODE_LINEAR,
srcRect // 원본 영역
);
}
}
}
hr = pBitmapRT->EndDraw();
if (FAILED(hr))
return nullptr;
// 8. 오프스크린 렌더 타겟에서 최종 커다란 비트맵 추출
hr = pBitmapRT->GetBitmap(&pCompositeBitmap);
if (FAILED(hr))
return nullptr;
return pCompositeBitmap;
}
//static 함수.
void CScene::ChangePause(bool _bPause)
{
if (true == _bPause) {
CScene::isPause = _bPause;
CTimeMgr::GetInstance()->SetTimeScale(0.f);
}
else if (false == _bPause) {
CScene::isPause = _bPause;
CTimeMgr::GetInstance()->SetTimeScale(1.f);
}
}
CScene 클래스 코드 설명
CScene 클래스는 게임의 특정 씬(Scene)을 관리하는 역할을 한다. 씬은 플레이어, 몬스터, 아이템 등 다양한 오브젝트를 포함하며, 이 클래슨느 다음과 같은 작업을 수행한다.
- 씬 내 오브젝트 관리 ( 생성, 삭제, 업데이트 등 )
- 씬의 렌더링 처리
- 타일 맵 생성 및 합성
- 일시정지 ( Pause ) 기능
주요 메서드
- AllDropItemRetrieve()
- 웨이브 시간이 모두 흐른 뒤 플레이어에게 드랍된 아이템이 모두 빨려들어가게 하기 위한 함수
- GROUP_TYPE::DROP_ITEM인 오브젝트를 순회하며 처리
- DROP_ITEM의 인식범위와 속도를 설정
- start() , update() , finalupdate()
- start() : 씬 내의 모든 오브젝트의 start() 함수를 호출하여 초기화를 진행
- update() : 모든 오브젝트들의 상태를 업데이트 한다. 죽은 오브젝트는 업데이트하지 않음
- finalupdate() : 충돌 처리나 참조 관계를 정리
- render()
- 모든 오브젝트를 렌더링한다.
- 죽은 오브젝트는 벡터에서 제거하여 메모리를 정리한다.
- DeleteGroup(), DeleteAll()
- 특정 그룹의 오브젝트를 삭제하거나, 모든 오브젝트를 삭제한다.
- 단, PLAYER와 WEAPON 오브젝트들은 메모리를 해제하지 않고 살려둔다.
- CreateCompositeMapBitmap()
- 타일 맵을 합성하여 하나의 비트맵으로 생성하는 함수다.
- ChangePause()
- 게임 일시정지 상태를 변경
- 일시정지 시 시간 스케일을 0으로 설정하여 모든 동작이 멈추도록 한다.
다음 포스트에서는 오브젝트들에 대해 알아보겠다.

'WinApi > Brotato 모작' 카테고리의 다른 글
[Win32 API Brotato 모작] 6. Scene_Main (0) | 2025.03.13 |
---|---|
[Win32 API Brotato 모작] 5. Object (0) | 2025.03.12 |
[Win32 API Brotato 모작] 3. Main & Core & Manager (3) (0) | 2025.03.11 |
[Win32 API Brotato 모작] 2. Main & Core & Manager (2) (0) | 2025.03.11 |
[Win32 API Brotato 모작] 1. Main & Core & Manager (1) (0) | 2025.03.11 |