꽃미남 프로그래머 김포프가 창립한 탑 프로그래머 양성 교육 기관 POCU 아카데미 오픈!
절찬리에 수강생 모집 중!
프로그래밍 언어 입문서가 아닌 프로그래밍 기초 개념 입문서
문과생, 비전공자를 위한 프로그래밍 입문책입니다.
jobGuid 꽃미남 프로그래머 "Pope Kim"님의 이론이나 수학에 치우치지 않고 실무에 곧바로 쓸 수 있는 실용적인 셰이더 프로그래밍 입문서 #겁나친절 jobGuid "1판의내용"에 "새로바뀐북미게임업계분위기"와 "비자관련정보", "1판을 기반으로 북미취업에 성공하신 분들의 생생한 경험담"을 담았습니다.
Posted by 알 수 없는 사용자

이글은 "게임 오브젝트 설계.. 나도 잘하고 싶다! #3" 에서 이어지는 글입니다.


안녕하세요! 두달만에 돌아온 끼로입니다!! (자랑이냐 퍽퍼퍼퍼퍽)

오늘 올릴 내용은 컴포넌트! 외부파일과의 결합! 입니다...


일단 저번시간까지 했던 것들을 이용하여 게임 오브젝트를 구성하려고 해봅시다.

pEntity->InsertComponent<ModelComponent>( "모델파일" );

pEntity->InsertComponent<AnimationComponent>( ... );

pEntity->InsertComponent<CombatComponent>( ... );

필요한 컴포넌트들을 게임 오브젝트에 추가하면 이제 원하는 게임 오브젝트가 구성이 될 것입니다!


하지만!!! 그럼 필요한 모든 조합을 코드에 추가해야 하는걸까요..?

플레이어! 몬스터! 프랍!

이렇게 간단하게 나누어진다면 상관없겠지만..

같은 플레이어여도 같은 몬스터여도 하는 일이나 속성에 따라

조합되는 컴포넌트가 달라져야 합니다!!

그렇지 않다면 컴포넌트 기반 설계의 힘을 별로 발휘하지 못할 것 같습니다.


작업하시는 분이 퐆풍 하드코딩을 통해 모든 조합을 다 만들어내는 

그런 Create 함수들을 만든다면 모르겠지만요..




<?xml version="1.0" encoding="utf-8" ?>
<entity version="1.0">
    <components>
        <script_component version="1.0" script="Player_Man.lua" />
        <animation_event_component version="1.0">
            ...
        </animation_event_component>
        <model_component version="1.0 " render_group="1" model="pc_M_big.nif" scale="1.000000">
            <animation_set ID="1" name="pc_M_big_AAA.kfm">
            ...
            </animation_set>
             <animation_set ID="2" name="pc_M_big_BBB.kfm">
            ...
            </animation_set>
             <animation_set ID="4" name="pc_M_big_CCC.kfm">
            ...
            </animation_set>
             <animation_set ID="5" name="pc_M_big_DDD.kfm">
            ...
            </animation_set>
        </model_component>
    </components>
</entity>

이렇게 xml을 통해 하나의 게임 오브젝트를 구성하는데 필요한 컴포넌트들의 목록을 읽어와서

필요한 컴포넌트들이 조합된 게임 오브젝트를 생성하는것이 오늘 쓸 포스팅의 목적입니다!!


이것을 구현하기 위해서 제가 선택한 방법은 다음과 같습니다.


간단하게 컴포넌트들이 생성 관련된 함수들을 매니저에 등록해두고 이 등록된 함수들을 사용하여 게임 오브젝트를 생성하는 것입니다.


그렇다면 등록은 어떤걸?? 어떻게???!


class ModelComponent : public ComponentBase
{
DeclareRTTI( ModelComponent );

public:
static ComponentBase* CreateComponent( const TiXmlElement* pNode, const Encoding& encoding );
static bool WriteComponent( const ComponentBase* pComponent, TiXmlElement* pNode, const Encoding& encoding );

public:
ModelComponent();
virtual ~ModelComponent();

...

bool LoadFromXml( const TiXmlElement* pNode, const Encoding& encoding );
bool WriteToXml( TiXmlElement* pNode, const Encoding& encoding ) const;

...
};


매니저에 등록해야 하니 static으로 생성함수와 저장함수를 만들고.. 생성과 저장에서 사용할 LoadFromXml이랑 WriteToXml 같은 함수를 컴포넌트에 넣고...


ComponentBase* ModelComponent::CreateComponent( const TiXmlElement* pNode, const Encoding& encoding )
{
ModelComponent* pModelComponent = new ModelComponent();
if( !pModelComponent->LoadFromXml( pNode, encoding ) )
{
return NULL;
}

return pModelComponent;
}

bool ModelComponent::WriteComponent( const ComponentBase* pComponent, TiXmlElement* pNode, const Encoding& encoding )
{
const ModelComponent* pModelComponent = RTTI::DynamicCast<const ModelComponent>( pComponent );
if( pModelComponent == NULL )
{
return false;
}

return pModelComponent->WriteToXml( pNode, encoding );
}


구현부도 이렇게 만들고..


등록은?! 매니저에서?! 어디에서 하지?! 어플리케이션 이니셜라이즈에서?! 컴포넌트 만들때마다 등록하는 부분을 찾아서 거기도 코드를 추가?! 때릴까?!




이,일단 등록부터 어떻게 해결을 해봅시다..


class ModelComponent : public ComponentBase
{
...

private:
class ScriptRegister
{
public:
ScriptRegister();
};
static ScriptRegister s_scriptRegister;
...


이렇게 내부 클래스를 하나 만들고 그 객체를 static으로 선언합니다.


ModelComponent::ScriptRegister::ScriptRegister()
{
매니저에 CreateComponent, WriteComponent 함수들 등록
}

ModelComponent::ScriptRegister ModelComponent::s_scriptRegister;


이렇게 하면 프로세스가 실행될 때 static 객체가 생성되고 여기에서 매니저에 등록을 해주면 될 것 같습니다..

후.. 이렇게 하면 등록도 할 수 있고 그럼 생성도 할 수 있고 그럼 이제 끝!!!


...



컴포넌트를 만들때마다 이짓을 하라고?!


그래서 저는 매..매크로를.. 이용합니다.......


#define DeclareComponentScript \
private: \
class ScriptHelper \
{ \
public: \
ScriptHelper(); \
static ComponentBase* CreateComponent( const TiXmlElement* pXmlNode, const Encoding& encoding ); \
static bool WriteComponent( const ComponentBase* pComponent, TiXmlElement* pXmlNode, const Encoding& encoding ); \
}; \
static ScriptHelper s_ScriptHelper


#define ImplementComponentScript( ClassName, ScriptName ) \
ClassName::ScriptHelper::ScriptHelper() \
{ \
매니저.RegisterComponent( ScriptName, RTTI::GetTypeID<ClassName>(), CreateComponent, WriteComponent ); \
} \
ComponentBase* ClassName::ScriptHelper::CreateComponent( const TiXmlElement* pXmlNode, const Encoding& encoding ) \
{ \
ClassName* pComponent = new ClassName(); \
if( !pComponent->LoadFromXml( pXmlNode, encoding ) )  return NULL; \
return pComponent; \
} \
bool ClassName::ScriptHelper::WriteComponent( const ComponentBase* pComponent, TiXmlElement* pXmlNode, const Encoding& encoding ) \
{ \
const ClassName* pCastComponent = RTTI::DynamicCast<const ClassName>( pComponent ); \
if( pCastComponent == NULL ) return false; \
return pCastComponent->WriteToXml( pXmlNode, encoding ); \
} \
ClassName::ScriptHelper ClassName::s_ScriptHelper


그리고 이 매크로를 사용하여 외부 파일에서의 조작이 필요한 컴포넌트에만 매크로를 넣어줍니다.


class ModelComponent : public ComponentBase

{

DeclareRTTI( ModelComponent );

DeclareComponentScript;

...


ImplementRTTI( ModelComponent );

ImplementComponentScript( ModelComponent, "model_component" );


ModelComponent::ModelComponent()

...


저는 이런식으로 매크로를 사용하여 xml에서 게임 오브젝트의 컴포넌트를 조합하고 그 xml파일로 게임 오브젝트를 생성하고 있습니다.




아.. 마무리를 어떻게 해야 하는거였더라.. 글을 오랜만에 쓰니 글쓰는 법도 까먹었...


그럼 진짜 끝!!


다음엔 컴포넌트와 스크립트의 연동이나 메시지통신이나 등등 그때 상황과 기분에 따라서..

반응형
,