Deeper Learning

[WinAPI] Object 클래스 설계 본문

Game Development/WinAPI

[WinAPI] Object 클래스 설계

Dlaiml 2024. 6. 5. 20:02

이전 포스팅에서 Core Class와 매니저 클래스에 대해 다루었다.

 

[WinAPI] PeekMessage, Core Class 설계 (슈팅게임)

Win32API를 활용해서 간단한 슈팅게임을 만드려고 한다.  1. GetMessage -> PeekMessage2. Core Class 소개 및 전체 클래스 설계 요약 GetMessage -> PeekMessage 이전 포스팅에서 설명하였던 것처럼 메시지 루프에

dlaiml.tistory.com

 

이번에는 Scene에 배치되는 Object 클래스의 구현, 상속 설계에 대해 포스팅하려 한다.

 

Scene Manager

씬에 배치되는 객체를 위한 클래스인 Object 클래스에 대해 알아보기 전에 현재 프로젝트의 구조에 대해 다시 한 번 짧게 짚고 넘어가자.

 

메인 함수의 메시지 루프에 있는 PeekMessage에서 사용가능한 메시지가 없을 경우 Core 클래스의 Progress가 수행되며 Progress의 코드는 아래와 같다.

 

void CCore::Progress()
{
	// =====================
	// || Update Managers ||
	// =====================
	CTimeMgr::GetInstance()->Update();
	CKeyMgr::GetInstance()->Update();
	CCamera::GetInstance()->Update();
	CSceneMgr::GetInstance()->Update();
	CCollisionMgr::GetInstance()->Update();
	CUIMgr::GetInstance()->Update();

	// ===============
	// || Rendering ||
	// =============== 
	Rectangle(m_hSubDC, -1, -1, m_ptResolution.x + 1, m_ptResolution.y + 1);
	CSceneMgr::GetInstance()->Render(m_hSubDC);
	BitBlt(m_hDC, 0, 0, m_ptResolution.x, m_ptResolution.y,
		m_hSubDC, 0, 0, SRCCOPY);


	// ==========================
	// || Event Delay Handling ||
	// ==========================
	CEventMgr::GetInstance()->Update();

}

  

크게 세 파트로 나눌 수 있다.

  • Update - 키보드 입력, 시간 경과 등에 따른 렌더링 전 업데이트 (캐릭터 이동, 삭제 예정 객체 삭제, 발사된 탄환 이동)
  • Render - Scene Manager에서 수행하며 현재 Scene에 배치된 Object들을 모두 Render
  • Event Delay Handling - 아래 포스팅에서 다루었던 객체 생성, 삭제, 씬 변경 이벤트 처리
 

[WinAPI] 이벤트 지연 처리

한 프레임 단위로 Update, Render가 일어나는 WinAPI 프로젝트에서 객체의 생성, 삭제를 코드 순서대로 처리하려 하였으나 문제가 있다. 예를 들어 적 오브젝트를 참조하여 적의 위치를 따라 매 프레

dlaiml.tistory.com

 

Scene Manager는 Update에서 현재 Scene 클래스의 Update를 호출한다.

Scene 클래스의 Update, Render는 모두 아래 코드처럼 배치된 Object의 Update, Render를 호출한다.

 

void CScene::Update()
{
	for (UINT i = 0; i < (UINT)GROUP_TYPE::END; ++i)
	{
		for (size_t j = 0; j < m_arrObj[i].size(); ++j)
		{
			if (!m_arrObj[i][j]->IsDead())
			{
				m_arrObj[i][j]->Update();
			}
		}
	}
}

 

Object 클래스도 Core 클래스에서의 로직 처리 구조를 그대로 따르는 것을 알 수 있다.

 

Object

순수 가상 함수를 포함하는 추상 클래스인 Object이다.

멤버로는 이름, 위치, 크기, 컴포넌트인 Collider&Animator, 삭제 예정 마킹 변수가 선언되어 있다.

 

#pragma once
#include "global.h"

#include "CCamera.h"

class CCollider;
class CAnimator;

class CObject
{

private:	
	wstring m_strName;

	Vec2 m_vPos;
	Vec2 m_vScale;

	CCollider* m_pCollider;
	CAnimator* m_pAnimator;

	bool m_bAlive;

public:
	virtual void Update() = 0;
	virtual void PostUpdate();
	virtual void Render(HDC _hdc);

	virtual CObject* Clone() = 0;

	void CreateCollider();
	void CreateAnimator();
	void RenderComponent(HDC _hdc);
	bool IsDead() const { return !m_bAlive; }

	virtual void OnCollision(CCollider* _pOther) {};
	virtual void OnCollisionBegin(CCollider* _pOther) {};
	virtual void OnCollisionEnd(CCollider* _pOther) {};


	Vec2 GetPos() const { return m_vPos; }
	Vec2 GetScale() const { return m_vScale; }
	CCollider* GetCollider() const { return m_pCollider; }
	CAnimator* GetAnimator() const { return m_pAnimator; }
	const wstring& GetName() const { return m_strName; }

	void SetPos(Vec2 _vPos) { m_vPos = _vPos; }
	void SetScale(Vec2 _vScale) { m_vScale = _vScale; }
	void SetName(const wstring& _ws) { m_strName = _ws; }

private:
	void SetDead() { m_bAlive = false; }


public:
	CObject();
	virtual ~CObject();

	CObject(const CObject& _cobj);

	friend class CEventMgr;

};

 

Getter와 Setter를 제외하고 함수를 보면

파생 클래스의 성질에 따라 특정 구현이 필요한 Update, Clone은 순수 가상 함수로 설정하였다.

파생 클래스의 성질에 따라 추가 구현, Override가 필요할 것으로 예상되는 함수들은 가상 함수로 구현하였다 (ex. UI 클래스는 Offset에 따라 PostUpdate에서 추가로 위치 수정 처리가 필요, 파생 클래스에서 충돌 처리가 모두 다르기 때문에 Collision 관련 함수도 가상함수로 구현)

 

충돌을 담당하는 Collider와 텍스쳐, 애니메이션을 담당하는 Animator는 컴포넌트 구조를 차용하였다.

컴포넌트 구조는 확장성, 유연성에서 장점이 있는데, Collider와 Animator 처럼 Object의 기능을 캡슐화하여 컴포넌트로 만들면 이를 객체에 붙여 충돌 처리와 애니메이션 기능을 추가할 수 있다. 이는 코드의 재사용성을 높이고, 각 컴포넌트를 독립적으로 개발할 수 있다는 장점이 있다.

 

Derived Class

Player, Enemy, Bullet, UI, Tile 등 Scene에 배치하는 모든 클래스가 Object를 상속받고 있다.

 

Enemy 클래스와 Bullet 클래스의 코드를 비교해보자.

 

Player와 Enemy가 Spawn하는 Bullet 클래스에 추가로 구현이 필요한 항목은 속력, 방향, 적-플레이어와 충돌이다.

Update에는 속도에 따라 위치를 업데이트, Render에는 원하는 모양으로 (또는 텍스쳐를 입혀) 객체를 버퍼에 그리는 기능을 추가로 구현하였다.

// Bullet.h

#pragma once

#include "CObject.h"

class CBullet :
    public CObject
{
private:
    float m_fSpeed;
    float m_fTheta;
    Vec2 m_vDirection;

public:
    virtual void Update() override;
    virtual void Render(HDC _hdc) override;

    void SetDirection(float _f) {m_fTheta = _f;}
    void SetDirection(Vec2 _vec) { m_vDirection = _vec.Normalize(); }

    virtual void OnCollisionBegin(CCollider* _pOther);

public:
    CBullet();
    ~CBullet();
};

 

 

Enemy 클래스에는 순찰 액션을 위한 포지션, 속도 정보와 체력, 충돌과 관련된 멤버, 함수들을 추가하였다.

#pragma once

#include "CObject.h"

class CCollider;

class CEnemy : public CObject
{

private:
	Vec2  m_vCenterPos;
	float m_fSpeed;
	float m_fPatrolDistance;
	int   m_iDirection;
	int	  m_iHP;

public:
	float GetSpeed() const { return m_fSpeed; }
	void SetSpeed(float _f) { m_fSpeed = _f; }
	void SetPatrolDistance(float _f) { m_fPatrolDistance = _f; }
	void SetCenterPos(Vec2 _vec) { m_vCenterPos = _vec; }

	virtual void OnCollision(CCollider* _pOther);
	virtual void OnCollisionBegin(CCollider* _pOther);
	virtual void OnCollisionEnd(CCollider* _pOther);



public:
	virtual void Update() override;
	virtual CLONE(CEnemy);

public:
	CEnemy();
	~CEnemy();

};

 

정리

Scene에 배치하는데 필수적인 정보인 위치, 크기를 멤버로 가진 Object 클래스를 Base 클래스로 만들고, 이를 상속받도록 하여 기능에 따라 여러 클래스를 구현하였다.

 

Player 클래스는 키보드 입력에 따라 조작이 가능한 클래스로 Update 메서드를 통해 매 프레임마다 키보드 입력을 처리하고, 입력에 따라 위치를 변경하는 로직이 구현되어 있다.

이는 언리얼 엔진에서 조작에 따른 이동 기능이 구현되어 있는 ACharacter(or APawn) 클래스와 유사하다.

 

Enemy 클래스는 순찰 기능과 체력이 추가되었다. Update에서는 순찰 로직에 따라 위치를 업데이트하고, 플레이어의 위치를 참고하는 과정이 수행된다.

언리얼 엔진 에서처럼 적의 움직임을 정의하는 AIController와 같은 Object를 만들어 Enemy 클래스에 Possess 하도록 설계를 변경해도 괜찮을 것 같다.

 

Bullet 클래스 속도와 충돌 관련 로직이 추가되었고 Update에서는 매 프레임마다 속도에 따라 위치를 갱신하고, 충돌 이벤트를 처리하여 대미지를 적용한다.

이전 언리얼 엔진 프로젝트에서 Projectile 클래스를 따로 만들어 대미지 처리, 텍스쳐, 각종 특수효과와 관련된 코드를 추가하였는데 이와 비슷하다.

 

 

 

코드는 학습중인 유튜브[1] 의 예제를 가져왔다.

Reference

[0] https://learn.microsoft.com/ko-kr/windows/win32/api/

[1] https://www.youtube.com/watch?v=dlFr-OnHlWU&list=PL4SIC1d_ab-ZLg4TvAO5R4nqlJTyJXsPK

 

Comments