Skip to content

zenoengine/ComponentBasedDesign

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

4 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Component based design

This project was made for C++ component based game design study.

Main

The basic structure of the whole game is Initialize, Run, and Shutdown.

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR pScmdline, int iCmdshow)
{
	unique_ptr<CGame> spGame = make_unique<CGame>();
	assert(spGame != nullptr);
	if(spGame->Initialize(hInstance, pScmdline, iCmdshow))
	{
		spGame->Run();
	}
	spGame->Shutdown();
	spGame.reset();

	return 0;
}

CGame

The CGame class is as follows.

class CGame
{	
public:
	CGame();
	~CGame();

	bool Initialize(HINSTANCE hInstance, LPSTR pScmdline, int iCmdshow, LPCWSTR applicationName = L"Zenity");
	void Shutdown();
	void Run();

	LRESULT CALLBACK MessageHandler(HWND, UINT, WPARAM, LPARAM);

private:
	bool InitializeWindow(LPSTR pScmdline, int iCmdShow, int& outScreenWidth, int& outScrennHeight);
	void ShutdownWindow();

	bool Frame();
	bool Update();
	void Draw();
};

public


bool Initialize(HINSTANCE hInstance, LPSTR pScmdline, int iCmdshow, LPCWSTR applicationName = L"Zenity");

Initialized resources in the game.

void Run()

Run the game, It runs looping inside a function until the end of the game.

void Shutdown()

Release resources in the game.

private


InitializeWindow

Set the screen size.

bool Frame()

It is called every frame. Internally, It call the Update and Draw functions.

bool Update()

Process and update logical operations in the game. Like a character movement, physical conflict, and so on.

Invoke GameObject's Update function.

void Draw()

Process the operations to be rendered on the screen.

Scene

Each Scene has a GameObject Hierachy.

You can use different GameObjects and Components for each scene to create completely different rules and spaces.

Through this concept, you can use on your scenes such as LoginScene, BattleScene, and EndingScene.

class CGameObject;

class ISceneBase abstract
{
public:
	virtual ~ISceneBase() {};
	virtual void Init() = 0;
	virtual void Clear() = 0;

	virtual void Update() = 0;
	virtual void Draw() = 0;
	
	virtual void AppendChild(shared_ptr<CGameObject> spGameObject) = 0;
	virtual void RemoveChild(shared_ptr<CGameObject> spGameObject) = 0;
	virtual void RemoveChild(const string& name) = 0;
	virtual void RemoveChild(const string tagName) = 0;

};

You can override the above virtual functions to implement different scenes.

class CBeginScene : public CSceneBase
{
private:
	void InitPlayerCharacter();
	void InitBulletSpawner();

public:
	CBeginScene();
	virtual ~CBeginScene();

	void Init() override;
	void Clear() override;
};

static SceneRegistrar<CBeginScene> registrar("BeginScene");

Use the last line of the SceneRegistrar to register the Scene created in the SceneMgr and allow the SceneMgr to be called by name.

static SceneRegistrar<CBeginScene> registrar("BeginScene");

SceneRegistrar is a utility template object that makes SceneMgr easy to register Scene. It is implemented using lambda as follows.

template<class T>
class SceneRegistrar {
public:
	SceneRegistrar(const string& sceneName)
	{
		// register the class factory function 
		CSceneMgr::GetInstance()->RegisterScene(sceneName,
			[](void) -> shared_ptr<ISceneBase> 
			{
				return make_shared<T>(); 
			}
		);
	}
};

public


virtual void Init()

Initialize the necessary resources in the scene.

virtual void Clear()

Release the resources used by the scene.

virtual void Update()

Process and update the logical operations of the game. FirstDepth calls Update of GameObject, and GameObjects recalls Update of Child Game Object.

virtual void Draw()

If the Game Object has a Renderer, the Renderer performs the Draw process. GameObjects re-invoke their Child Game Object Draw.

void AppendChild(shared_ptr spGameObject)

Add the First Depth Child GameObject of the Scene Hierarchy.

void RemoveChild(shared_ptr spGameObject);

Remove the Scene Hierachy's First Dept Child Game Object.

SceneMgr

SceneMgr registers and manages scenes. You can also raise and lower the required Scene.

You can load multiple scenes and combine the required scenes together.

class CSceneMgr
{
	friend class CGame;

private:
	map<string, shared_ptr<ISceneBase>> m_loadedSceneMap;
	map<string, std::function<shared_ptr<ISceneBase>(void)>> m_sceneFactoryRegistry;

private:
	CSceneMgr();
	~CSceneMgr();

	void UnloadAllScene();

public:
	static CSceneMgr* GetInstance();

	bool Init();
	void Release();
	void LoadScene(const string& name);
	void UnloadScene(const string& name);
	void RegisterScene(const string& sceneName, std::function<shared_ptr<ISceneBase>(void)> generatorFunc);
};

public


bool Init()

Initialize SceneMgr.

void Release()

Unload all scenes in SceneMgr and release all other resources.

void LoadScene(const string& name)

Scene is loaded through the name of the scene.

void UnloadScene(const string& name)

Scene unloaded through the name of the scene.

void RegisterScene(const string& sceneName, std::function<shared_ptr(void)> generatorFunc)

Register the scene, and param should contain the name of the scene and the function to create and return the scene.

GameObject

A game object is a basic unit that constitutes a scene.

Programmers can create objects by attaching various components to the GameObject.

GameObjects have their own uuids and can add child objects.

class CGameObject final
{
public:
	CGameObject();
	~CGameObject();

	void AppendChild(shared_ptr<CGameObject> spGameObject);
	void RemoveChild(shared_ptr<CGameObject> spGameObject);

	CGameObject& SetTagName(const std::string& tagName);
	inline const std::string& GetTagName() const { return m_strTagName; };

	CGameObject& SetName(const std::string& tagName);
	inline const std::string& GetName() const { return m_strName; };
	
	void AddComponent(shared_ptr<CComponentBase> spComponent);

	inline void SetActive(bool active) { m_bIsActive = active; }
	inline const bool IsActive() const { return m_bIsActive; }

	template<class T>
	weak_ptr<T> GetComponent()
	{
		for (auto it : m_componentVec)
		{
			weak_ptr<T> spComponent = dynamic_pointer_cast<T>(it);
			if (spComponent.lock() != nullptr)
			{
				return spComponent;
			}
		}
		
		weak_ptr<T> empty;
		return empty;
	};
};

public


void AppendChild(shared_ptr spGameObject)

Add the Child Game Object of the GameObject.

void RemoveChild(shared_ptr spGameObject);

Remove the Child Game Object of the GameObject.

void AddComponent(shared_ptr spComponent);

Add a Component to the GameObject.

weak_ptr GetComponent()

Contrast with the Comopnent that the GameObject has.

Examples of use are as follows.

weak_ptr<CTransform> wpTransform = spGameObject->GetComponent<CTransform>();

ComponentBase

The base class for Component. You can inherit this class to create various components.

class CComponentBase
{
protected:
	CGameObject* m_thisGameObject;

public:
	CComponentBase();
	virtual ~CComponentBase();

	virtual const ComponentFamilyType GetFamilyComponentType() const;
	
	virtual void Init() {};
	virtual void Update() {};
	virtual void Clear() {};

	void SetParent(CGameObject* pGameObject);
};

public


virtual const ComponentFamilyType GetFamilyComponentType() const;

Sometimes an object does not have more than one Component. For example, you can not have two CTransform Components.

Since you can insert the ComponentFamilyType of the existing Component by comparing it with the ComponentFamilyType of the added Component

If you want to avoid duplicates, you should override this function.

virtual void Init() {};

It is a function that initializes the resources that the component has.

virtual void Update() {};

It implements the internal behavior of the Component when called on every frame.

virtual void Clear() {};

This function is called when the Component is cleared.

Transform

It is a predefined Component. You can have only one per object, and take charge of the rotation and position of the Object.

class CTransform : public CComponentBase
{
private:
	D3DXVECTOR3 m_vPosition;
	D3DXVECTOR3 m_vRotation;

public:
	CTransform();
	virtual ~CTransform();

	const ComponentFamilyType GetFamilyComponentType() const final;
	void Init() override;
	void Update() override;
	void Clear() override;

	inline void Translate(const D3DXVECTOR3& addPos) { m_vPosition += addPos; }
	inline void SetPosition(const D3DXVECTOR3& pos) { m_vPosition = pos; }

	const D3DXVECTOR3& GetPosition() const;
	const D3DXVECTOR3& GetRotation() const;
};

Component based design

이 프로젝트는 C++ 컴포넌트 기반 게임 설계를 이해하기 위해 만들어졌습니다.

Main

전체 게임의 기본 구조는 Initialize, Run, Shutdown 의 3단 구조로 이루어 진다.

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR pScmdline, int iCmdshow)
{
	unique_ptr<CGame> spGame = make_unique<CGame>();
	assert(spGame != nullptr);
	if(spGame->Initialize(hInstance, pScmdline, iCmdshow))
	{
		spGame->Run();
	}
	spGame->Shutdown();
	spGame.reset();

	return 0;
}

CGame

CGame 클래스는 다음과 같이 이루어져 있다.

class CGame
{	
public:
	CGame();
	~CGame();

	bool Initialize(HINSTANCE hInstance, LPSTR pScmdline, int iCmdshow, LPCWSTR applicationName = L"Zenity");
	void Shutdown();
	void Run();

	LRESULT CALLBACK MessageHandler(HWND, UINT, WPARAM, LPARAM);

private:
	bool InitializeWindow(LPSTR pScmdline, int iCmdShow, int& outScreenWidth, int& outScrennHeight);
	void ShutdownWindow();

	bool Frame();
	bool Update();
	void Draw();
};

public


bool Initialize(HINSTANCE hInstance, LPSTR pScmdline, int iCmdshow, LPCWSTR applicationName = L"Zenity");

게임 내 자원들을 초기화한다.

void Run()

게임을 실행시킨다. 함수 안에서 while 루프를 돌며 게임이 종료되기 전까지 실행되고 있다.

void Shutdown()

게임 내 자원을들 해제한다.

private


InitializeWindow

화면의 크기를 설정한다.

bool Frame()

매 프레임마다 호출되는 함수다. 내부적으로는 Update와 Draw 함수를 호출한다.

bool Update()

게임 내 논리적인 연산을 처리하고 갱신한다. 캐릭터의 이동, 물리 충돌 등이 이에 해당하며

GameObject들의 Update 함수를 호출한다.

void Draw()

화면에 렌더링될 연산들을 처리한다.

Scene

각 Scene은 GameObject Hierachy를 가진다.

각 Scene마다 다른 GameObject와 Component들을 사용해 전혀 다른 규칙과 공간을 만들어 낼 수있다.

이와 같은 개념을 통해 LoginScene, BattleScene, EndingScene 등 원하는 장면을 나누어서 사용할 수 있다.

class CGameObject;

class ISceneBase abstract
{
public:
	virtual ~ISceneBase() {};
	virtual void Init() = 0;
	virtual void Clear() = 0;

	virtual void Update() = 0;
	virtual void Draw() = 0;
	
	virtual void AppendChild(shared_ptr<CGameObject> spGameObject) = 0;
	virtual void RemoveChild(shared_ptr<CGameObject> spGameObject) = 0;
	virtual void RemoveChild(const string& name) = 0;
	virtual void RemoveChild(const string tagName) = 0;

};

위의 virtual function들을 override 해 다른 Scene을 구현할 수 있다.

class CBeginScene : public CSceneBase
{
private:
	void InitPlayerCharacter();
	void InitBulletSpawner();

public:
	CBeginScene();
	virtual ~CBeginScene();

	void Init() override;
	void Clear() override;
};

static SceneRegistrar<CBeginScene> registrar("BeginScene");

마지막 줄의 SceneRegistrar 이용해 SceneMgr에 만든 Scene을 등록하고 SceneMgr가 이름으로 호출 할 수 있게 한다.

static SceneRegistrar<CBeginScene> registrar("BeginScene");

SceneRegistrar는 SceneMgr에 Scene을 등록하기 편하게 해주는 유틸성 템플릿 객체로 다음과같이 lambda를 사용하여 구현되어 있다.

template<class T>
class SceneRegistrar {
public:
	SceneRegistrar(const string& sceneName)
	{
		// register the class factory function 
		CSceneMgr::GetInstance()->RegisterScene(sceneName,
			[](void) -> shared_ptr<ISceneBase> 
			{
				return make_shared<T>(); 
			}
		);
	}
};

public


virtual void Init()

Scene에서 필요한 자원들을 초기화한다.

virtual void Clear()

Scene에서 사용한 자원들을 해제한다.

virtual void Update()

게임의 논리적인 연산을 처리하고 갱신한다. FirstDepth GameObject의 Update를 호출하며, GameObject들은 본인의 Child Game Object의 Update를 재호출한다.

virtual void Draw()

Game Object가 Renderer를 가지고 있다면 Renderer가 Draw 처리를 한다. GameObject들은 본인의 Child Game Object의 Draw를 재호출한다.

void AppendChild(shared_ptr spGameObject)

Scene Hierachy의 First Dept Child Game Object 를 추가한다.

void RemoveChild(shared_ptr spGameObject);

Scene Hierachy의 First Dept Child Game Object 를 제거한다.

SceneMgr

SceneMgr는 Scene을 등록하고 관리한다. 또한 필요한 Scene을 올리고 내릴 수 있다.

여러개의 Scene을 로드해서 필요한 Scene끼리 조합 할 수 있다.

class CSceneMgr
{
	friend class CGame;

private:
	map<string, shared_ptr<ISceneBase>> m_loadedSceneMap;
	map<string, std::function<shared_ptr<ISceneBase>(void)>> m_sceneFactoryRegistry;

private:
	CSceneMgr();
	~CSceneMgr();

	void UnloadAllScene();

public:
	static CSceneMgr* GetInstance();

	bool Init();
	void Release();
	void LoadScene(const string& name);
	void UnloadScene(const string& name);
	void RegisterScene(const string& sceneName, std::function<shared_ptr<ISceneBase>(void)> generatorFunc);
};

public


bool Init()

SceneMgr를 초기화한다.

void Release()

SceneMgr의 모든 Scene을 Unload 하고 기타 자원을 모두 해제한다.

void LoadScene(const string& name)

Scene의 이름을 통해 Scene을 올린다.

void UnloadScene(const string& name)

Scene의 이름을 통해 Scene을 내린다.

void RegisterScene(const string& sceneName, std::function<shared_ptr(void)> generatorFunc)

Scene을 등록하며 param에는 Scene의 이름과, Scene을 생성해 반환할 함수를 넣어줘야한다.

GameObject

게임 오브젝트는 Scene을 구성하는 기본 단위이다.

프로그래머는 GameObject에 다양한 Component들을 붙여서 객체를 만들어 갈 수 있다..

GameObject는 각자 고유한 uuid를 가지며 Child Object를 추가 할 수 있다.

class CGameObject final
{
public:
	CGameObject();
	~CGameObject();

	void AppendChild(shared_ptr<CGameObject> spGameObject);
	void RemoveChild(shared_ptr<CGameObject> spGameObject);

	CGameObject& SetTagName(const std::string& tagName);
	inline const std::string& GetTagName() const { return m_strTagName; };

	CGameObject& SetName(const std::string& tagName);
	inline const std::string& GetName() const { return m_strName; };
	
	void AddComponent(shared_ptr<CComponentBase> spComponent);

	inline void SetActive(bool active) { m_bIsActive = active; }
	inline const bool IsActive() const { return m_bIsActive; }

	template<class T>
	weak_ptr<T> GetComponent()
	{
		for (auto it : m_componentVec)
		{
			weak_ptr<T> spComponent = dynamic_pointer_cast<T>(it);
			if (spComponent.lock() != nullptr)
			{
				return spComponent;
			}
		}
		
		weak_ptr<T> empty;
		return empty;
	};
};

public


void AppendChild(shared_ptr spGameObject)

해당 GameObject의 Child Game Object 를 추가한다.

void RemoveChild(shared_ptr spGameObject);

해당 GameObject의 Child Game Object 를 제거한다.

void AddComponent(shared_ptr spComponent);

해당 GameObject 에 Component를 추가한다.

weak_ptr GetComponent()

해당 GameObject가 가지고있는 Comopnent를 반한환다.

사용 예는 다음과 같다.

weak_ptr<CTransform> wpTransform = spGameObject->GetComponent<CTransform>();

ComponentBase

Component의 기본 클래스이다. 이 클래스를 상속받아 다양한 컴포넌트들을 만들 수 있다.

class CComponentBase
{
protected:
	CGameObject* m_thisGameObject;

public:
	CComponentBase();
	virtual ~CComponentBase();

	virtual const ComponentFamilyType GetFamilyComponentType() const;
	
	virtual void Init() {};
	virtual void Update() {};
	virtual void Clear() {};

	void SetParent(CGameObject* pGameObject);
};

public


virtual const ComponentFamilyType GetFamilyComponentType() const;

한 오브젝트에서 여러개의 Component가 붙으면 안되는 경우가 있다. 예를 들면 CTransform Component가 두개가 붙을수 없다.

기존에 가지고있는 Component들의 ComponentFamilyType과 추가되는 Component의 ComponentFamilyType 비교하여 삽입 할수 있게 되기 때문에

만약 중복되지 않게 처리하고싶다면 이 함수를 override 해야 한다.

virtual void Init() {};

컴포넌트가 가지고 있는 자원들을 초기화해주는 함수이다.

virtual void Update() {};

매 프레임 마다 호출되며 Component의 내부 동작을 구현한다.

virtual void Clear() {};

Component가 지워질 때 호출되는 함수이다.

Transform

미리 정의된 Component이다. 오브젝트당 하나만을 가질 수 있으며, Object의 회전, 위치 값을 담당한다.

class CTransform : public CComponentBase
{
private:
	D3DXVECTOR3 m_vPosition;
	D3DXVECTOR3 m_vRotation;

public:
	CTransform();
	virtual ~CTransform();

	const ComponentFamilyType GetFamilyComponentType() const final;
	void Init() override;
	void Update() override;
	void Clear() override;

	inline void Translate(const D3DXVECTOR3& addPos) { m_vPosition += addPos; }
	inline void SetPosition(const D3DXVECTOR3& pos) { m_vPosition = pos; }

	const D3DXVECTOR3& GetPosition() const;
	const D3DXVECTOR3& GetRotation() const;
};

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published