씬을 구현 하고 씬을 통해 화면을 변경하고 구성해보겠습니다.
씬
우선 부모 클래스로 사용할 씬을 생성해줍니다.
-Scene.cpp-
#include "stdafx.h"
#include "Scene.h"
CScene::CScene()
{
}
CScene::~CScene()
{
}
-Scene.h
#pragma once
class CScene abstract
{
public:
explicit CScene();
virtual ~CScene();
public:
virtual int Ready_Scene() =0;
virtual void Update_Scene() = 0;
virtual void Render_Scene(HDC hDC) =0;
virtual void Release_Scene() = 0;
};
게임 오브젝트와 같이 생성될 씬들의 부모로 사용할 클래스입니다 순수 가상함수들을 구성해놓습니다.
씬 매니저
게임 오브젝트와 같이 게임에 사용될 다양한씬들을 용도에따라 변경해줄수있는 매니저가 필요합니다 이를 씬매니저를 통해 구현하겠습니다.
-Scene_Manager.cpp-
#include "stdafx.h"
#include "Scene_Manager.h"
#include "Loading.h"
#include "Stage.h"
#include "MyMenu.h"
//스택틱 변수를 널포인터로 초기화해줍니다.
CScene_Manager* CScene_Manager::m_pInstance = nullptr;
CScene_Manager::CScene_Manager()
//이니셜라이저를 통해 멤버 변수들을 초기화해줍니다.
:m_eCurScene(SCENE_END)
, m_eNextScene(SCENE_END)
, m_pScene(nullptr)
{
}
CScene_Manager::~CScene_Manager()
{
Release_Scene_Manager();
}
void CScene_Manager::Scene_Change(ID eNextID)
{
//FSM 유한 상태 기계
m_eNextScene = eNextID;
// 이경우는 어떤 경우 ? 즉 씬이 변경되야 하는 상황.
// 현재씬과 다음씬(들어온값)이 다른경우에만 씬을 교체해주어야합니다
// 같은 값이 들어온다면 유지해주고 교체해주지않습니다.
if (m_eCurScene != m_eNextScene)
{
//우선 씬을 교체해주기위해 현재 씬을 삭제해줍니다.
Safe_Delete(m_pScene);
//스위치문을통해 들어온값에 맞는 씬을 추가해줍니다.
switch (m_eNextScene)
{
case CScene_Manager::SCENE_LOADING:
m_pScene = CLoading::Create();
break;
case CScene_Manager::SCENE_STAGE:
m_pScene = CStage::Create();
break;
case CScene_Manager::SCENE_MENU:
m_pScene = CMyMenu::Create();
break;
default:
break;
}
//다음 같은값이 들어오면 씬을 교체하지않을것이기때문에
//커렌트씬을 변경해줍니다.
m_eCurScene = m_eNextScene;
}
}
//업데이트와 렌더 릴리즈입니다.
//메인앱에서 게임오브젝트를 타지않고 이제 메인엡에서 씬을 탄후
//게임오브젝트를 탑니다.
void CScene_Manager::Update_Scene_Manager()
{
m_pScene->Update_Scene();
}
void CScene_Manager::Render_Scene_Manager(HDC hDC)
{
m_pScene->Render_Scene(hDC);
}
void CScene_Manager::Release_Scene_Manager()
{
Safe_Delete(m_pScene);
}
-Scene_Manager.h-
#pragma once
class CScene;
class CScene_Manager final
{
public:
//각 씬에 맞는 열거체를 만들어줍니다.
enum ID {SCENE_LOADING, SCENE_MENU,SCENE_STAGE, SCENE_END};
public:
//씬 매니저는 싱글톤을 이용해 구성합니다.
static CScene_Manager* Get_Instance()
{
if (nullptr == m_pInstance)
m_pInstance = new CScene_Manager;
return m_pInstance;
}
static void Destroy_Instance()
{
if (m_pInstance)
{
delete m_pInstance;
m_pInstance = nullptr;
}
}
private:
static CScene_Manager* m_pInstance;
private:
CScene_Manager();
~CScene_Manager();
public:
void Scene_Change(ID eNextID);
void Update_Scene_Manager();
void Render_Scene_Manager(HDC hDC);
void Release_Scene_Manager();
private:
// m_pScene이 실질적으로 가지고있는 씬이고 cur,next로 씬을 변경할때
// 사용합니다.
CScene* m_pScene;
ID m_eCurScene;
ID m_eNextScene;
};
싱글톤으로 구현을해 필요에따라 씬을 교체해주고 나타낼수있습니다.
로딩
우선 게임의 가장 처음 나오는 씬인 로딩씬을 만들어 보겠습니다.
-Loading.cpp-
#include "stdafx.h"
#include "Loading.h"
CLoading::CLoading()
{
}
CLoading::~CLoading()
{
Release_Scene();
}
int CLoading::Ready_Scene()
{
return 0;
}
void CLoading::Update_Scene()
{
if (CKey_Manager::Get_Instance()->Key_Up(KEY_RIGHT))
{
CScene_Manager::Get_Instance()->Scene_Change(CScene_Manager::SCENE_MENU);
}
//로딩은 초기화면 이기때문에 여기선 오른쪽키를 입력받으면 씬을 바꾸도록 했습니다.
}
void CLoading::Render_Scene(HDC hDC)
{
HDC hMemDC = CBitmap_Manager::Get_Instance()->FindImage(L"Logo");
//메인앱에서 미리 받아온 이미지를 가져와 화면에 그려줍니다.
if (nullptr == hMemDC)
{
return;
}
BitBlt(hDC, 0, 0, WINCX, WINCY, hMemDC, 0, 0, SRCCOPY);
}
void CLoading::Release_Scene()
{
}
CScene* CLoading::Create()
{
CScene* pInstance = new CLoading;
if (0 > pInstance->Ready_Scene())
Safe_Delete(pInstance);
return pInstance;
}
-Loading.h-
#pragma once
#include "Scene.h"
class CLoading final:
public CScene
{
private:
explicit CLoading();
public:
virtual ~CLoading();
public:
// CScene을(를) 통해 상속됨
virtual int Ready_Scene() override;
virtual void Update_Scene() override;
virtual void Render_Scene(HDC hDC) override;
virtual void Release_Scene() override;
public:
static CScene* Create();
};
로딩까지 구현했다면 MainCPP에서 기존의 게임구성요소들을 변경해주어야합니다.
이전에는 메인앱->게임오브젝트매니저->게임오브젝트순으로 흘러갔다면
이제는 메인앱->씬매니저->씬-게임오브젝트매니저->게임오브젝트 순으로 흘러가기때문입니다.
우선 기존 요소들은 씬의 스테이지 클래스를 만들어 옮겨준후
업데이트와 렌더부분도 게임오브젝트매니저가아닌 씬매니저를 통해 호출해줍니다 씬매니저의 씬이 해당하는 게임오브젝트매니저를 호출할것입니다.
-MainApp.cpp-
#include "stdafx.h"
#include "MainApp.h"
#include "Player.h"
#include "Monster.h"
#include "Collision_Manager.h"
#include "Mouse.h"
#include "GameObject_Manager.h"
#include "Line_Manager.h"
#include "Bitmap_Manager.h"
#include "Scene_Manager.h"
CMainApp::CMainApp()
:m_iFPS(0)
, m_szFPS(L"")
, m_dwFPSTime(0)
,m_hDC(nullptr)
{
}
/*
원래 구조
Client -> MainApp -> 각각의 오브젝트들 Update, Render 수행.
Client -> MainApp -> GameObject_Manager-> 각각의 오브젝트들 Update, Render 수행.
client -> Mainapp -> Scene_Manager -> Scene -> GameObject_Manager - > 각각의 오브젝트들 Update, Render 수행
*/
CMainApp::~CMainApp()
{
Release_MainApp();
}
int CMainApp::Ready_MainApp()
{
m_hDC = GetDC(g_hWnd);
m_dwFPSTime = GetTickCount();
CBitmap_Manager::Get_Instance()->Insert_Bitmap(L"../Image/BackBuffer.bmp", L"BackBuffer");
CBitmap_Manager::Get_Instance()->Insert_Bitmap(L"../Image/Back.bmp", L"DubleBuffer");
CBitmap_Manager::Get_Instance()->Insert_Bitmap(L"../Image/maja2.bmp", L"maja2");
CBitmap_Manager::Get_Instance()->Insert_Bitmap(L"../Image/Logo/Logo.bmp", L"Logo");
CBitmap_Manager::Get_Instance()->Insert_Bitmap(L"../Image/Menu/Menu.bmp", L"Menu");
CBitmap_Manager::Get_Instance()->Insert_Bitmap(L"../Image/Button/Start.bmp", L"Start");
CBitmap_Manager::Get_Instance()->Insert_Bitmap(L"../Image/Button/Edit.bmp", L"Edit");
CBitmap_Manager::Get_Instance()->Insert_Bitmap(L"../Image/Button/Exit.bmp", L"Exit");
CScene_Manager::Get_Instance()->Scene_Change(CScene_Manager::SCENE_LOADING);
return READY_OK;
}
void CMainApp::Update_MainApp()
{
CKey_Manager::Get_Instance()->Key_Update();
CScene_Manager::Get_Instance()->Update_Scene_Manager();
}
void CMainApp::Render_MainApp()
{
// Rectangle(m_hDC, 0, 0, WINCX, WINCY);
// Rectangle(m_hDC, 100, 100, WINCX - 100, WINCY - 100);
HDC hDubleBuffer = CBitmap_Manager::Get_Instance()->FindImage(L"DubleBuffer");
HDC hMemDC = CBitmap_Manager::Get_Instance()->FindImage(L"BackBuffer");
BitBlt(hDubleBuffer, 0, 0, WINCX, WINCY, hMemDC, 0, 0, SRCCOPY);
CScene_Manager::Get_Instance()->Render_Scene_Manager(hDubleBuffer);
++m_iFPS;
if (m_dwFPSTime + 1000 < GetTickCount())
{
swprintf_s(m_szFPS, L"FPS : %d", m_iFPS);
m_iFPS = 0;
m_dwFPSTime = GetTickCount();
}
TextOut(hDubleBuffer, 100, 100, m_szFPS, lstrlen(m_szFPS));
BitBlt(m_hDC, 0, 0, WINCX, WINCY, hDubleBuffer, 0, 0, SRCCOPY);
}
void CMainApp::Release_MainApp()
{
ReleaseDC(g_hWnd, m_hDC);
CKey_Manager::Destroy_Instance();
CBitmap_Manager::Destroy_Instance();
CGameObject_Manager::Destroy_Instance();
CLine_Manager::Destroy_Instance();
CScene_Manager::Destroy_Instance();
}
메뉴
다음은 게임 메뉴버튼을 띄울 메뉴버튼을 만들어보겠습니다.
우선 로딩에서 오른쪽키를 입력받으면 메뉴로 넘어가게 구성해두었기때문에 바로 메뉴 클래스를 만들어 보겠습니다.
버튼
메뉴에 올려놓을 버튼을 게임오브젝트를 상속받아서 구현하겠습니다.
-MyButton.h-
#pragma once
#include "GameObject.h"
class CMyButton final:
public CGameObject
{
private:
explicit CMyButton();
public:
virtual ~CMyButton();
public:
void Set_FrameKey(const TCHAR* pFrameKey) { m_pFrameKey = pFrameKey; }
public:
// CGameObject을(를) 통해 상속됨
virtual int Ready_GameObject() override;
virtual int Update_GameObject() override;
virtual void Late_Update_GameObject() override;
virtual void Render_GameObject(HDC hDC) override;
virtual void Release_GameObject() override;
public:
static CGameObject * Create();
private:
const TCHAR* m_pFrameKey; //프레임키를 통해 이미지를 불러옵니다
int m_iDrawID; //ID를통해 그림을 어디서부터 읽어올지 결정합니다
};
-MyButton.cpp-
#include "stdafx.h"
#include "MyButton.h"
CMyButton::CMyButton()
:m_iDrawID(0)
{
}
CMyButton::~CMyButton()
{
}
int CMyButton::Ready_GameObject()
{
m_tInfo.iCX = 150;
m_tInfo.iCY = 150;
//버튼 이미지의 맞춰 크기를 설정해줍니다.
return READY_OK;
}
int CMyButton::Update_GameObject()
{
CGameObject::Update_Rect_GameObject();
POINT pt{};
//커서의 위치를 가져온뒤 좌표를 화면에 맞게 해주고
//PtInRect를통해 만약 마우스와 충돌하면m_iDrawID = 1로
//이미지를 바꿔줍니다.
GetCursorPos(&pt);
ScreenToClient(g_hWnd, &pt);
if (PtInRect(&m_tRect, pt))
{
m_iDrawID = 1;
}
else m_iDrawID = 0;
return 0;
}
void CMyButton::Late_Update_GameObject()
{
}
void CMyButton::Render_GameObject(HDC hDC)
{
CGameObject::Update_Rect_GameObject();
HDC hMemDC = CBitmap_Manager::Get_Instance()->FindImage(m_pFrameKey);
if (nullptr == hMemDC)
return;
GdiTransparentBlt(hDC,
m_tRect.left,
m_tRect.top,
m_tInfo.iCX,
m_tInfo.iCY,
hMemDC,
m_tInfo.iCX * m_iDrawID, 0, //X축의 위치를 ID에따라 다르게 그려줍니다.
m_tInfo.iCX, m_tInfo.iCY,
RGB(255, 255, 255));
}
void CMyButton::Release_GameObject()
{
}
CGameObject * CMyButton::Create()
{
CGameObject* pInstance = new CMyButton;
if (0 > pInstance->Ready_GameObject())
Safe_Delete(pInstance);
return pInstance;
}
이런식으로 메인로고가 나오고 키를입력받으면 다음화면으로 넘어가게 구성해주었습니다