이번에는 이 프로그램에서 그려질 모든 객체의 부모 클래스이자 추상 클래스인 CObject 클래스에 대해 설명하겠다.
CObject를 상속받는 자식 클래스들은 다음과 같다.
- CDamagedUI : 몬스터에게 데미지를 입힐 때 얼만큼의 피해를 입혔는지 숫자로 표시하기 위한 클래스
- Ground : 전투 맵 상에서 맵 밖으로 빠져나가지 못하게 하기 위해 벽을 구현할 클래스
- Missile : 총기류 무기들의 탄환을 구현하기 위한 클래스
- Monster : 몬스터
- Player : 유저 플레이어
- UI : 화면상에 Text를 표현하거나, 뒷 배경과 같은 Panel, 버튼을 구현하기 위한 클래스
- Weapon : 플레이어가 소지할 무기
CObject 멤버 변수
private:
GROUP_TYPE m_ObjType;
wstring m_ObjName;
Vec2 m_vPos;
Vec2 m_vPrevPos;
Vec2 m_vScale;
Vec2 m_vRenderScale;
//Component
CCollider* m_pCollider;
CAnimator* m_pAnimator;
CRigidbody* m_pRigidBody;
CGravity* m_pGravity;
vector<CImage*> m_pImages;
CTextUI* m_pTextUI;
bool m_bAlive; //자기 자신이 활성화 or 비활성화. (삭제 전용)
bool m_bEnable; //일시적인 활성화 or 비활성화.
bool m_bFlipX;
//Render Wave용 Float
float m_fWaveDuration;
float m_fWaveElapsed;
- 기본 속성
- GROUP_TYPE m_ObjType : 오브젝트의 그룹 타입 ( 플레이어, 몬스터, UI 등등 분류 )
- wstring m_ObjName : 오브젝트 이름
- Vec2 m_vPos : 현재 위치
- Vec2 m_PrevPos : 이전 프레임에서의 위치 ( 움직임 감지용 )
- Vec2 m_vScale : 실제 크기
- Vec2 m_vRenderScale : 렌더링 시 적용되는 크기 ( 애니메이션 효과 )
- 컴포넌트 : 모든 객체마다 가질 수도 있고 안가질 수도 있는 특성들은 컴포넌트로 생각하여 필요할때 Create() 해주는 방식이다.
- CCollider* m_pCollider : 충돌체 컴포넌트
- CAnimator* m_pAnimator : 애니메이션 처리 컴포넌트
- CRigidbody* m_pRigidBody : 물리 처리 컴포넌트 ( 중력, 힘, 가속도 적용 )
- CGravity* m_pGravity : 중력 처리 컴포넌트
- vector<CImage* > m_pImages : 이미지 렌더링 컴포넌트 벡터
- CTextUI* m_pTextUI : 텍스트 UI 출력 컴포넌트
- 상태 관리
- bool m_bAlive : 객체가 살아있는지 여부 (false일 경우 삭제 대상이라 업데이트 하지 않음)
- bool m_bEnable : 일시적 활성화 / 비활성화
- bool m_bFlipX : 좌우 반전 여부
CObject 주요 함수
public:
void SetPos(Vec2 _vPos) { m_vPos = _vPos; }
void SetScale(Vec2 _vScale) {
m_vScale = _vScale;
m_vRenderScale = _vScale;
}
GROUP_TYPE GetObjType() { return m_ObjType; }
Vec2 GetPos() { return m_vPos; }
virtual Vec2 GetFinalPos() { return m_vPos; }
Vec2 GetScale() { return m_vScale; }
Vec2 GetRenderScale() { return m_vRenderScale; }
void SetObjType(GROUP_TYPE _eType) { m_ObjType = _eType; }
void SetName(const wstring& _strName) { m_ObjName = _strName; }
void SetWaveDuration(float _fWaveTime) { m_fWaveDuration = _fWaveTime; }
void SetRenderScale(Vec2 _vScale) { m_vRenderScale = _vScale; }
void SetFlipX(bool _bFlipX) { m_bFlipX = _bFlipX; }
bool GetFlipX() { return m_bFlipX; }
const wstring& GetName() { return m_ObjName; }
CCollider* GetCollider() { return m_pCollider; }
CAnimator* GetAnimator() { return m_pAnimator; }
CRigidbody* GetRigidbody() { return m_pRigidBody; }
CGravity* GetGravity() { return m_pGravity; }
CTextUI* GetTextUI() { return m_pTextUI; }
vector<CImage*>& GetImages() { return m_pImages; }
size_t GetImageCount() { return m_pImages.size(); }
CImage* GetImage(int _Idx);
void DeleteImage() { Safe_Delete_Vec(m_pImages); }
void SetImage(CImage* _image, int _idx) { m_pImages[_idx] = _image; }
void AddImage(ID2D1Bitmap* _bitmap);
void CreateCollider();
void CreateAnimator();
void CreateRigidBody();
void CreateGravity();
void CreateImage();
void CreateTextUI(const wstring& _text, Vec2 _offsetLT, Vec2 _offsetRB //text , 좌상단 offset, 우하단 offset
, int _fontSize, D2D1::ColorF _colorText //글자크기, 글자색상(ColoF)
, bool _bDrawOutline //글자외곽선 여부(false라면 다 0으로 작성할것)
, float _fOutlineThickness, D2D1::ColorF _colorOutline //글자외곽선 두께, 글자외곽선 색상
, FONT_TYPE _eType //폰트 타입(DEFAULT, KR)
, TextUIMode _eMode, int _iStartNum); //(TEXT,NUMBER)모드 , NUMBER모드일 경우 감소시작할 숫자(타이머)
virtual void OnCollision(CCollider* _pOther) {};
virtual void OnCollisionEnter(CCollider* _pOther) {};
virtual void OnCollisionExit(CCollider* _pOther) {};
bool IsDead() {
return !m_bAlive;
}
bool IsMove() {
if (m_vPos.x != m_vPrevPos.x || m_vPos.y != m_vPrevPos.y) return true;
return false;
}
void ShakeScale();
private:
void SetDead() { m_bAlive = false; }
public:
virtual void start() {};
virtual void update();
virtual void finalupdate();
virtual void render(ID2D1HwndRenderTarget* _pRender);
void component_render(ID2D1HwndRenderTarget* _pRender);
virtual CObject* Clone() = 0;
public:
CObject();
CObject(const CObject& _origin);
virtual ~CObject();
- virtual void update()
- 매 프레임 호출되는 업데이트 함수
- 흔들림 효과(ShakeScale)를 적용하고 이전 위치를 저장하여 이동 여부 체크 준비.
- virtual void finalupdate()
- 모든 객체가 일반 업데이트 후 마지막으로 호출되는 함수
- 각종 컴포넌트의 최종 업데이트 수행( 애니메이션, 충돌 박스 등 위치 조정 )
- virtual void render(ID2D1HwndRenderTarget*)
- Direct2D를 이용하여 렌더링
- 각 클래스들은 오버라이딩 하여 사용하지만 하지 않을 경우 사각형을 그려주는 작업 수행
- component_render( ID2D1HwndRenderTarget*)
- 각 컴포넌트들 렌더링
- 충돌 처리 이벤트 함수 ( OnCollision, OnCollisionEnter, OnCollisionExit )
- 충돌이 일어날 때 호출되는 가상함수로 자식 클래스에서 재정의하여 각 클래스에 맞는 행동 구현
CObject.cpp , CObject.h
#pragma once
#include "global.h"
#include "CCamera.h"
class CCollider;
class CAnimator;
class CRigidbody;
class CGravity;
class CImage;
class CTextUI;
class CObject
{
private:
GROUP_TYPE m_ObjType;
wstring m_ObjName;
Vec2 m_vPos;
Vec2 m_vPrevPos;
Vec2 m_vScale;
Vec2 m_vRenderScale;
//Component
CCollider* m_pCollider;
CAnimator* m_pAnimator;
CRigidbody* m_pRigidBody;
CGravity* m_pGravity;
vector<CImage*> m_pImages;
CTextUI* m_pTextUI;
bool m_bAlive; //자기 자신이 활성화 or 비활성화. (삭제 전용)
bool m_bEnable; //일시적인 활성화 or 비활성화.
bool m_bFlipX;
//Render Wave용 Float
float m_fWaveDuration;
float m_fWaveElapsed;
public:
void SetPos(Vec2 _vPos) { m_vPos = _vPos; }
void SetScale(Vec2 _vScale) {
m_vScale = _vScale;
m_vRenderScale = _vScale;
}
GROUP_TYPE GetObjType() { return m_ObjType; }
Vec2 GetPos() { return m_vPos; }
virtual Vec2 GetFinalPos() { return m_vPos; }
Vec2 GetScale() { return m_vScale; }
Vec2 GetRenderScale() { return m_vRenderScale; }
void SetObjType(GROUP_TYPE _eType) { m_ObjType = _eType; }
void SetName(const wstring& _strName) { m_ObjName = _strName; }
void SetWaveDuration(float _fWaveTime) { m_fWaveDuration = _fWaveTime; }
void SetRenderScale(Vec2 _vScale) { m_vRenderScale = _vScale; }
void SetFlipX(bool _bFlipX) { m_bFlipX = _bFlipX; }
bool GetFlipX() { return m_bFlipX; }
const wstring& GetName() { return m_ObjName; }
CCollider* GetCollider() { return m_pCollider; }
CAnimator* GetAnimator() { return m_pAnimator; }
CRigidbody* GetRigidbody() { return m_pRigidBody; }
CGravity* GetGravity() { return m_pGravity; }
CTextUI* GetTextUI() { return m_pTextUI; }
vector<CImage*>& GetImages() { return m_pImages; }
size_t GetImageCount() { return m_pImages.size(); }
CImage* GetImage(int _Idx);
void DeleteImage() { Safe_Delete_Vec(m_pImages); }
void SetImage(CImage* _image, int _idx) { m_pImages[_idx] = _image; }
//void AddImage(const wstring& tag);
//void AddImage(const wstring& tag, ID2D1Bitmap* _bitmap);
void AddImage(ID2D1Bitmap* _bitmap);
CTextUI* GetTextUI() { return m_pTextUI; }
void CreateCollider();
void CreateAnimator();
void CreateRigidBody();
void CreateGravity();
void CreateImage();
void CreateTextUI(const wstring& _text, Vec2 _offsetLT, Vec2 _offsetRB //text , 좌상단 offset, 우하단 offset
, int _fontSize, D2D1::ColorF _colorText //글자크기, 글자색상(ColoF)
, bool _bDrawOutline //글자외곽선 여부(false라면 다 0으로 작성할것)
, float _fOutlineThickness, D2D1::ColorF _colorOutline //글자외곽선 두께, 글자외곽선 색상
, FONT_TYPE _eType //폰트 타입(DEFAULT, KR)
, TextUIMode _eMode, int _iStartNum); //(TEXT,NUMBER)모드 , NUMBER모드일 경우 감소시작할 숫자(타이머)
virtual void OnCollision(CCollider* _pOther) {};
virtual void OnCollisionEnter(CCollider* _pOther) {};
virtual void OnCollisionExit(CCollider* _pOther) {};
bool IsDead() {
return !m_bAlive;
}
bool IsMove() {
if (m_vPos.x != m_vPrevPos.x || m_vPos.y != m_vPrevPos.y) return true;
return false;
}
void ShakeScale();
private:
void SetDead() { m_bAlive = false; }
public:
virtual void start() {};
virtual void update();
virtual void finalupdate();
virtual void render(ID2D1HwndRenderTarget* _pRender);
void component_render(ID2D1HwndRenderTarget* _pRender);
virtual CObject* Clone() = 0;
public:
CObject();
CObject(const CObject& _origin);
virtual ~CObject();
friend class CEventMgr;
};
#include "pch.h"
#include "CObject.h"
#include "CTimeMgr.h"
#include "CCollider.h"
#include "CAnimator.h"
#include "CResMgr.h"
#include "CTexture.h"
#include "CRigidbody.h"
#include "CGravity.h"
#include "CImage.h"
#include "Direct2DMgr.h"
#include "CTextUI.h"
#include "CPathMgr.h"
CObject::CObject()
: m_vPos{}
, m_ObjType(GROUP_TYPE::END)
, m_vScale{}
, m_pCollider(nullptr)
, m_pAnimator(nullptr)
, m_pRigidBody(nullptr)
, m_pGravity(nullptr)
, m_pImages{}
, m_pTextUI(nullptr)
, m_bAlive(true)
, m_bEnable(true)
, m_fWaveDuration(1.f)
, m_fWaveElapsed(0.f)
, m_bFlipX(false)
{
m_vRenderScale = m_vScale;
}
CObject::CObject(const CObject& _origin)
: m_ObjName(_origin.m_ObjName)
, m_ObjType(_origin.m_ObjType)
, m_vPos(_origin.m_vPos)
, m_vScale(_origin.m_vScale)
, m_pCollider(nullptr)
, m_bAlive(true)
, m_bEnable(true)
, m_pAnimator(nullptr)
, m_pRigidBody(nullptr)
, m_pGravity(nullptr)
, m_pImages{}
, m_pTextUI(nullptr)
, m_bFlipX(false)
, m_fWaveDuration(_origin.m_fWaveDuration)
, m_fWaveElapsed(0.f)
{
m_vRenderScale = m_vScale;
if (_origin.m_pCollider != nullptr) {
m_pCollider = new CCollider(*_origin.m_pCollider);
m_pCollider->m_pOwner = this;
}
if (_origin.m_pAnimator != nullptr) {
m_pAnimator = new CAnimator(*_origin.m_pAnimator);
m_pAnimator->m_pOwner = this;
}
if (_origin.m_pGravity != nullptr) {
m_pGravity = new CGravity(*_origin.m_pGravity);
m_pGravity->m_pOwner = this;
}
if (_origin.m_pRigidBody != nullptr) {
m_pRigidBody = new CRigidbody(*_origin.m_pRigidBody);
m_pRigidBody->m_pOwner = this;
}
if (_origin.m_pTextUI != nullptr) {
m_pTextUI = new CTextUI(*_origin.m_pTextUI);
m_pTextUI->m_pOwner = this;
}
}
CObject::~CObject() {
if (m_pCollider != nullptr) delete m_pCollider;
if (m_pAnimator != nullptr) delete m_pAnimator;
if (m_pGravity != nullptr) delete m_pGravity;
if (m_pRigidBody != nullptr)delete m_pRigidBody;
Safe_Delete_Vec(m_pImages);
m_pImages.clear();
if (m_pTextUI != nullptr) delete m_pTextUI;
}
void CObject::update()
{
ShakeScale();
m_vPrevPos = m_vPos;
}
void CObject::finalupdate()
{
if (m_pAnimator) m_pAnimator->finalupdate();
if (m_pGravity) m_pGravity->finalupdate();
if (m_pRigidBody)m_pRigidBody->finalupdate();
if (m_pCollider) m_pCollider->finalupdate();
if (!m_pImages.empty())
{
for (size_t i = 0; i < m_pImages.size(); ++i)
m_pImages[i]->finalupdate();
}
}
void CObject::render(ID2D1HwndRenderTarget* _pRender)
{
//진짜 좌표.
if (GetImages().empty() && nullptr == GetAnimator()) {
Vec2 vRenderPos = CCamera::GetInstance()->GetRenderPos(m_vPos);
float left = vRenderPos.x - m_vRenderScale.x / 2.f;
float top = vRenderPos.y - m_vRenderScale.y / 2.f;
float right = vRenderPos.x + m_vRenderScale.x / 2.f;
float bottom = vRenderPos.y + m_vRenderScale.y / 2.f;
D2D1_RECT_F rect = D2D1::RectF(left, top, right, bottom);
ID2D1SolidColorBrush* pBrush = nullptr;
HRESULT hr = _pRender->CreateSolidColorBrush(
D2D1::ColorF(D2D1::ColorF::Black), &pBrush);
if (SUCCEEDED(hr))
{
_pRender->DrawRectangle(rect, pBrush);
pBrush->Release();
}
}
component_render(_pRender);
}
void CObject::component_render(ID2D1HwndRenderTarget* _pRender)
{
if (m_pAnimator != nullptr) m_pAnimator->render(_pRender);
if (m_pCollider != nullptr) m_pCollider->render(_pRender);
if (!m_pImages.empty())
{
for (size_t i = 0; i < m_pImages.size(); ++i)
m_pImages[i]->render(_pRender);
}
if (m_pTextUI != nullptr) m_pTextUI->render(_pRender);
}
void CObject::CreateCollider()
{
m_pCollider = new CCollider;
m_pCollider->m_pOwner = this;
}
void CObject::CreateAnimator()
{
m_pAnimator = new CAnimator;
m_pAnimator->m_pOwner = this;
}
void CObject::CreateRigidBody()
{
m_pRigidBody = new CRigidbody;
m_pRigidBody->m_pOwner = this;
}
void CObject::CreateGravity()
{
m_pGravity = new CGravity;
m_pGravity->m_pOwner = this;
}
CImage* CObject::GetImage(int _Idx)
{
size_t tmp = m_pImages.size() - 1;
if (tmp < _Idx) return nullptr;
else return m_pImages[_Idx];
}
void CObject::AddImage(ID2D1Bitmap* _bitmap)
{
Direct2DMgr* pD2DMgr = Direct2DMgr::GetInstance();
CImage* tmp = new CImage;
tmp->SetBitmap(_bitmap);
tmp->m_pOwner = this;
m_pImages.push_back(tmp);
}
void CObject::CreateTextUI(const wstring& _text, Vec2 _offsetLT, Vec2 _offsetRB //text , 좌상단 offset, 우하단 offset
, int _fontSize, D2D1::ColorF _colorText //글자크기, 글자색상(ColoF)
, bool _bDrawOutline //글자외곽선 여부(false라면 다 0으로 작성할것)
, float _fOutlineThickness, D2D1::ColorF _colorOutline //글자외곽선 두께, 글자외곽선 색상
, FONT_TYPE _eType //폰트 타입(DEFAULT, KR)
, TextUIMode _eMode, int _iStartNum) //(TEXT,NUMBER)모드 , NUMBER모드일 경우 감소시작할 숫자(타이머))
{
m_pTextUI = new CTextUI;
m_pTextUI->m_pOwner = this;
m_pTextUI->SetMode(_eMode);
m_pTextUI->SetText(_text);
m_pTextUI->SetFontSize(_fontSize);
m_pTextUI->SetTextColor(_colorText);
m_pTextUI->SetDrawOutline(_bDrawOutline);
m_pTextUI->SetOutlineThickness(_fOutlineThickness);
m_pTextUI->SetOutlineColor(_colorOutline);
m_pTextUI->SetFontType(_eType);
m_pTextUI->SetOffsetLT(_offsetLT);
m_pTextUI->SetOffsetRB(_offsetRB);
m_pTextUI->SetTime(_iStartNum);
}
void CObject::ShakeScale()
{
Vec2 OriginalScale = GetScale();
Vec2 ScaleWave = OriginalScale * 0.3f;
float waveScale = 1.f;
if (IsMove()) {
waveScale = 1.5f;
}
m_fWaveElapsed += (fDT * waveScale);
float angle = 2 * PI * m_fWaveElapsed / m_fWaveDuration;
float delta = 0.2f * sin(angle);
m_vRenderScale.x = m_vScale.x + ((1.f + delta) * ScaleWave.x);
m_vRenderScale.y = m_vScale.y + ((1.f - delta) * ScaleWave.y);
if (m_fWaveElapsed > m_fWaveDuration) {
m_fWaveElapsed = 0.f;
}
}
예시
// 플레이어 객체 생성 예시
CPlayer* pPlayer = new CPlayer();
pPlayer->SetName(L"Hero");
pPlayer->SetPos(Vec2(100.f,200.f));
pPlayer->SetScale(Vec2(50.f,50.f));
pPlayer->CreateCollider();
pPlayer->CreateAnimator();
이렇게 생성된 객체들은 Scene에서 다음과 같은 코드에서 update()와 finalupdate() 그리고 render()가 호출된다.
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);
}
}
}
}
'WinApi > Brotato 모작' 카테고리의 다른 글
[Win32 API Brotato 모작] 7. Scene_Select_Character (0) | 2025.03.13 |
---|---|
[Win32 API Brotato 모작] 6. Scene_Main (0) | 2025.03.13 |
[Win32 API Brotato 모작] 4. Core & Scene (0) | 2025.03.11 |
[Win32 API Brotato 모작] 3. Main & Core & Manager (3) (0) | 2025.03.11 |
[Win32 API Brotato 모작] 2. Main & Core & Manager (2) (0) | 2025.03.11 |