이글은 "게임 오브젝트 설계.. 나도 잘하고 싶다! #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을 통해 하나의 게임 오브젝트를 구성하는데 필요한 컴포넌트들의 목록을 읽어와서
필요한 컴포넌트들이 조합된 게임 오브젝트를 생성하는것이 오늘 쓸 포스팅의 목적입니다!!
이것을 구현하기 위해서 제가 선택한 방법은 다음과 같습니다.
간단하게 컴포넌트들이 생성 관련된 함수들을 매니저에 등록해두고 이 등록된 함수들을 사용하여 게임 오브젝트를 생성하는 것입니다.
그렇다면 등록은 어떤걸?? 어떻게???!
{
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 같은 함수를 컴포넌트에 넣고...
{
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으로 선언합니다.
{
매니저에 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파일로 게임 오브젝트를 생성하고 있습니다.
아.. 마무리를 어떻게 해야 하는거였더라.. 글을 오랜만에 쓰니 글쓰는 법도 까먹었...
그럼 진짜 끝!!
다음엔 컴포넌트와 스크립트의 연동이나 메시지통신이나 등등 그때 상황과 기분에 따라서..
'프로그래밍' 카테고리의 다른 글
char* 문자열 버퍼 초기화의 내부 (13) | 2012.05.20 |
---|---|
자기만의 게임엔진을 만들어보자! #1 (20) | 2012.05.18 |
사고뭉치를 위한 디버깅 방법 #02 (6) | 2012.05.09 |
WPF로 카와이하게 툴을 만들어볼게요. (44) | 2012.05.08 |
[NDC12] (다양한 유저 PC에 대응하기 위한) 유연한 렌더링 파이프라인 시스템 (6) | 2012.04.27 |