[DirectX] Event 매니저


define 헤더


enum class EVENT_TYPE
{
    CREATE_OBJECT,
    DELETE_OBJECT,
    ADD_CHILD,

    DELETE_RESOURCE,

    LEVEL_CHANGE,
}

이벤트 옵션을 담은 enum class를 define 헤더파일에 추가해준다.

EventMgr 클래스


// 헤더파일

#pragma once
#include "CSingleton.h"



class CEventMgr :
    public CSingleton< CEventMgr>
{
    SINGLE(CEventMgr);
private:
    vector<tEvent>  m_vecEvent;

public:
    void AddEvent(const tEvent& _evn) { m_vecEvent.push_back(_evn); }

public:
    void tick();
};
// cpp 파일

#include "pch.h"
#include "CEventMgr.h"

#include "CLevelMgr.h"
#include "CLevel.h"
#include "CGameObject.h"

CEventMgr::CEventMgr()
{

}

CEventMgr::~CEventMgr()
{

}

void CEventMgr::tick()
{
	for (size_t i = 0; i < m_vecEvent.size(); ++i)
	{
		switch (m_vecEvent[i].Type)
		{
		// wParam : GameObject, lParam : Layer Index
		case EVENT_TYPE::CREATE_OBJECT:
		{
			CGameObject* NewObject = (CGameObject*)m_vecEvent[i].wParam;
			int iLayerIdx = (int)m_vecEvent[i].lParam;

			CLevelMgr::GetInst()->GetCurLevel()->AddGameObject(NewObject, iLayerIdx, false);
		}
			break;
		case EVENT_TYPE::DELETE_OBJECT:


			break;
		case EVENT_TYPE::ADD_CHILD:


			break;
		case EVENT_TYPE::DELETE_RESOURCE:


			break;
		case EVENT_TYPE::LEVEL_CHANGE:


			break;		
		}
	}

	m_vecEvent.clear();
}

레이어 이름을 지정하기


한편, 레벨의 레이어 별로 이름을 지을 수 있도록 레벨매니저의 코드를 아래와 같이 작성해줄 수 있다.

// CLevelMgr.cpp 파일의 init함수 중에서

	m_pCurLevel = new CLevel;

	// Layer 이름설정
	m_pCurLevel->GetLayer(0)->SetName(L"Default");
	m_pCurLevel->GetLayer(1)->SetName(L"Player");
	m_pCurLevel->GetLayer(2)->SetName(L"Monster");
	m_pCurLevel->GetLayer(3)->SetName(L"PlayerProjectile");
	m_pCurLevel->GetLayer(4)->SetName(L"MonsterProjectile");

그리고 오버로딩 함수를 이용해서 CLevel클래스이 AddGameObject함수를 레이어의 이름을 받을 수 있도록 코드를 작성해준다.

FindLayerByName함수와 AddGameObject함수.

// CLevel.cpp 파일 중에서

void CLevel::AddGameObject(CGameObject* _Object, int _iLayerIdx, bool _bMove)
{
	m_arrLayer[_iLayerIdx]->AddGameObject(_Object, _bMove);
}

미사일 쏴보기


CMissileScript 클래스


미사일이라는 객체를 생성해주는 것이므로 미사일 스크립트 클래스를 새로 생성해준다.

// 헤더파일

#pragma once
#include "CScript.h"
class CMissileScript :
    public CScript
{
private:
    Vec3        m_vDir;     // 이동 방향
    float       m_fSpeed;   // 이동 속력

public:
    void SetSpeed(float _Speed) { m_fSpeed = _Speed; }

public:
    virtual void tick() override;

    CLONE(CMissileScript);
public:
    CMissileScript();
    ~CMissileScript();
};



// cpp 파일

#include "pch.h"
#include "CMissileScript.h"

CMissileScript::CMissileScript()
	: m_vDir(Vec3(0.f, 1.f, 0.f))
	, m_fSpeed(100.f)
{
}

CMissileScript::~CMissileScript()
{
}

void CMissileScript::tick()
{
	Vec3 vPos = Transform()->GetRelativePos();

	vPos += m_vDir * DT * m_fSpeed;

	Transform()->SetRelativePos(vPos);
}

해당 방향으로 미사일을 발사하는 기능이 담겨져있다.

CPlayerScript 클래스


미사일을 쏘는 코드를 플레이어 스크립트에 아래와 같이 추가한다.

Shoot 함수.

// 헤더파일

private:
    void Shoot();
// cpp파일
void CPlayerScript::Shoot()
{
	// 미사일 오브젝트 생성
	CGameObject* pMissile = new CGameObject;

	pMissile->AddComponent(new CTransform);
	pMissile->AddComponent(new CMeshRender);
	pMissile->AddComponent(new CMissileScript);

	pMissile->Transform()->SetRelativePos(Transform()->GetRelativePos() + Vec3(0.f, 0.5f, 0.f) * Transform()->GetRelativeScale() );
	pMissile->Transform()->SetRelativeScale(Vec3(50.f, 50.f, 50.f));

	pMissile->MeshRender()->SetMesh(CResMgr::GetInst()->FindRes<CMesh>(L"RectMesh"));
	pMissile->MeshRender()->SetMaterial(CResMgr::GetInst()->FindRes<CMaterial>(L"Std2DMtrl"));

	CMissileScript* pMissileScript = pMissile->GetScript<CMissileScript>();
	if (nullptr != pMissileScript)
		pMissileScript->SetSpeed(500.f);

	// 레벨에 추가
	CLevel* pCurLevel = CLevelMgr::GetInst()->GetCurLevel();
	pCurLevel->AddGameObject(pMissile, L"PlayerProjectile", false);
}



게임 오브젝트로부터 스크립트 가져오기


CGameObject 클래스에서 아래와 같이 코드를 추가해준다.

GetScript 템플릿 함수

// 헤더 파일

template<typename T>
inline T* CGameObject::GetScript()
{
    for (size_t i = 0; i < m_vecScript.size(); ++i)
    {
        T* pScript = dynamic_cast<T*> (m_vecScript[i]);
        if (nullptr != pScript)
            return pScript;
    }

    return nullptr;
}


동기화 처리


tick함수가 도는 도중에 즉 프레임 도중에 레벨에 오브젝트를 추가하는 행위는 매우 위험한 행위이다.

그래서 이번 프레임에서 생성을 예약하고, 다음 프레임에서 객체를 생성하고 함께 돌 수 있도록 조치를 취해야한다.

Prefab


매번 리소스를 new해주지 않고, 복사생성자를 활용하는 방법을 사용하면 된다.

그리고 객체는 각 객체마다의 Transform을 가져야 하므로 복사생성자를 사용하는 과정에서 같은 Transform을 가지지 않도록 조치를 해주어야한다.

이때 리소스 중에서 Prefab을 활용한다.

게임 오브젝트를 리소스화 시켜서 원형 프로젝트를 필요할 때마다 복사해서 사용하는 방식이다.

// 헤더파일

#pragma once
#include "CRes.h"

class CGameObject;

class CPrefab :
    public CRes
{
private:
	CGameObject*	m_ProtoObj;

public:
	CGameObject* Instantiate();

private:
	virtual int Load(const wstring& _strFilePath) { return S_OK; }
public:
	virtual int Save(const wstring& _strRelativePath) { return S_OK; }

private:
	virtual void UpdateData() override {}

public:
	void RegisterProtoObject(CGameObject* _Proto);

public:
	CPrefab();
	~CPrefab();
};


// cpp 파일

#include "pch.h"
#include "CPrefab.h"

#include "CGameObject.h"

CPrefab::CPrefab()
	: CRes(RES_TYPE::PREFAB)
	, m_ProtoObj(nullptr)
{
}

CPrefab::~CPrefab()
{
	if (nullptr != m_ProtoObj)
		delete m_ProtoObj;
}

CGameObject* CPrefab::Instantiate()
{
	return m_ProtoObj->Clone();	
}

void CPrefab::RegisterProtoObject(CGameObject* _Proto)
{
	// 원본 오브젝트는 레벨 소속이 아니여야 한다.
	assert(-1 == _Proto->GetLayerIndex());

	m_ProtoObj = _Proto;
}

RegisterProtoObject 함수를 통해서 원본 오브젝트를 등록한다.

이때, 해당 함수가 받는 _Proto 인자는 레벨에 등록되어 있지 않은 인자여야 한다.

따라서 GetLayerIndex의 결과가 -1이 아닐 경우에는 assert 처리를 해준다.

CResMgr


한편, 미리 필요한 Prefab을 리소스 매니저에서 제작해준다.

CreateDefaultPrefab 함수가 그 역할을 한다.

// 헤더 파일 중에서

private:
    void CreateDefaultPrefab();
// cpp 파일 중에서

#include "CGameObject.h"
#include "components.h"
#include "CMissileScript.h"
void CResMgr::CreateDefaultPrefab()
{
	CGameObject* pMissile = new CGameObject;

	pMissile->AddComponent(new CTransform);
	pMissile->AddComponent(new CMeshRender);
	pMissile->AddComponent(new CMissileScript);
		
	pMissile->Transform()->SetRelativeScale(Vec3(50.f, 50.f, 50.f));

	pMissile->MeshRender()->SetMesh(CResMgr::GetInst()->FindRes<CMesh>(L"RectMesh"));
	pMissile->MeshRender()->SetMaterial(CResMgr::GetInst()->FindRes<CMaterial>(L"Std2DMtrl"));

	CMissileScript* pMissileScript = pMissile->GetScript<CMissileScript>();
	if (nullptr != pMissileScript)
		pMissileScript->SetSpeed(500.f);

	Ptr<CPrefab> MissilePrefab = new CPrefab;
	MissilePrefab->RegisterProtoObject(pMissile);
	AddRes<CPrefab>(L"MissilePrefab", MissilePrefab);
}


CPlayerScript 클래스


플레이어 스크립트 클래스에서는 이제는 Shoot 함수에서 Prefab을 복사해와서 생성하는 방식으로 사용하게 된다.


void CPlayerScript::Shoot()
{
	// 미사일 프리팹 참조
	Ptr<CPrefab> pMissilePrefab = CResMgr::GetInst()->FindRes<CPrefab>(L"MissilePrefab");
	Vec3 vMissilePos = Transform()->GetRelativePos() + Vec3(0.f, 0.5f, 0.f) * Transform()->GetRelativeScale();
	CGameObject* pCloneMissile = pMissilePrefab->Instantiate();

	// 레벨에 추가
	SpawnGameObject(pCloneMissile, vMissilePos, L"PlayerProjectile");
}

그래서 위와 같이 shoot 함수가 변경이 된다.


© 2022.07. by Wookey_Kim

Powered by Hydejack v7.5.2