Entity 01 - 속성 반영

이번 장에서는 엔터티 시스템 혹은 게임 오브젝트 컴포넌트 시스템에 대해서 알아본다.  게임 객체를 관리할 때  C++ 상속을 사용하면 처음에는 편리하지만 갈수록 복잡도는 증가한다.  엔터티 시스템은 상속대신 구성요소들을 합성해서 만든다.

레고 블록처럼 필요한 기능을 추가할 수 있다.

<엔터티 시스템에 필요한 기능을 추가하기 위해 다음과 같이 진행 할 것이다.>

1장 속성 반영( GPG 5권의 템플릿을 이용한 C++ 반영 기능 구현을 참고한다.)

2장 객체가 되는 Entity 클래스 필요

3장 Entity에 추가되는 컴포넌트 필요

4장 Entity 혹은 컴포넌트끼리 통신하기 위한 메시지 시스템

이번장에서는 속성을 반영해 본다.

1. 속성 타입  : 클래스 템플릿과 정적인 멤버 변수를 통한 고유속성 만들기

PropertyType은 속성 객체(MyProperty)에서 반환할 속성 타입으로 데이터 형을 나타낸다.
GetTypeID()는 ePropertyType의 열거형 값을 반환하고 GetYpeName()은 문자열로 "bool"이나 "DWORD"를 반환한다.

//PropertyType.h 소스 일부분

enum ePropertyType

{

    ept_bool,

    ept_DWORD,

    ept_int,

    ept_float,

    ept_string,

    ept_ptr,

    eptMAX_PROPERTY_TYPES

};

 

template <class T> class PropertyType

{

public :

    // Returns type ID associated with the templatized type.

    static    ePropertyType    GetTypeID()

    {

        return ms_typeID;

    }

    static    char*            GetTypeName()

    {

        return ms_typeName;

    }

 

private:

    static    ePropertyType        ms_typeID;

    static  char*                ms_typeName;

};

 

template<class T> ePropertyType PropertyType<T>::ms_typeID        = ept_ptr;

template<class T> char*            PropertyType<T>::ms_typeName    = "ptr";

 

template<> ePropertyType PropertyType<bool>::ms_typeID            = ept_bool;

template<> char*        PropertyType<bool>::ms_typeName        = "bool";

 

template<> ePropertyType PropertyType<DWORD>::ms_typeID            = ept_DWORD;

template<> char*        PropertyType<DWORD>::ms_typeName        = "DWORD";

 

template<> ePropertyType PropertyType<int>::ms_typeID            = ept_int;

template<> char*        PropertyType<int>::ms_typeName            = "int";

 

template<> ePropertyType PropertyType<float>::ms_typeID            = ept_float;

template<> char*        PropertyType<float>::ms_typeName        = "float";

 

template<> ePropertyType PropertyType<char*>::ms_typeID            = ept_string;

template<> char*        PropertyType<char*>::ms_typeName        = "string";

클래스 템플릿의 특수화와 정적인 멤버 변수를 통해 컴파일시 고유의 속성값을 만들수 있다.
PropertyType을 사용하면  MyProperty에 추가적인 ePropertyType 값을 입력할 수고를 덜어 준다.
(MyProperty는 뒤에 나온다.) MyProperty 생성시 사용하는 템플릿 형을 PrpertyType에 전달하면, PropertyType은 자동으로 ePropertyType과 타입네임을 생성하게 된다.

정적인 멤버 함수의 사용은 다음과 같이 한다.

PropertyType<int>::GetTypeID()
PropertyType<bool>::GetTypeName()

2 속성 객체:  MyProperty

속성 객체는 데이터 값과 데이터 속성에 관련된 정보를 가지고 있다.  각 객체에 추가되는 속성의 단위가 될 것이다.
IProperty는 속성객체의 추상 클래스로 데이터 이름과 타입, 타입의 문자열에 관련된 인터페이스를 노출 시킨다.  인터페이스를 두고 하위 객체 MyProperty를 템플릿 클래스로 만들어 컨테이너에 담을수 있도록 한다.

MyProperty는 속성객체의 구현 클래스로 실제 데이터를 가지고 있다.

#include <string>

#include "PropertyType.h"

 

//인터페이스는 템플릿을 사용하지 않음으로써 Property를 컨테이너에 담을수 있다.

class IProperty

{

public:

    IProperty( const std::string& name ) : m_name( name )

    {

    }

    virtual ~IProperty()

    {

    }

    const std::string& GetName()

    {

        return m_name;

    }

    virtual char*    GetTypeName() const = 0;

    virtual ePropertyType    GetType() const = 0;

    virtual void*            GetData() = 0;

protected:

    std::string        m_name;

};

 

template<typename T>

class MyProperty : public IProperty

{

public:

    MyProperty( const std::string& name ) : IProperty( name )

    {

    }

    virtual ~MyProperty()

    {

    }

    ePropertyType GetType() const

    {

        return PropertyType<T>::GetTypeID();

    }

    char*    GetTypeName() const

    {

        return PropertyType<T>::GetTypeName();

    }

    virtual void* GetData()

    {

        return &m_data;

    }

    T& GetValue()

    {

        return m_data;

    }

 

protected:

    T    m_data;

};

사용법은 다음과 같다.

    //MyProperty 테스트

    MyProperty<float>* pValue = new MyProperty<float>("Health");

    pValue->GetValue() = 3.03f;

    std::cout << pValue->GetName() << "=" << pValue->GetValue() << std::endl;

    const char* szType = pValue->GetTypeName();  //float 형

 

     실행 결과 :  Health=3.03                 

3 클래스에 속성 적용 : 제어 클래스 MyPropertySystem

속성 객체인 MyProperty를  관리하거나 제어하기 위한 클래스가 필요하다.
속성을 리스트로 저장한다.

typedef std::list<  IProperty* >    PropertyList;
PropertyList                            m_properties;   

속성을 읽기만  한다  
IProperty*     GetProperty( const std::string& name );

4 속성의 추가 및 읽기  :  name에 해당하는 속성이 없으면 T타입의 MyEntity 속성을 리스트에 추가한다.
MyProperty<T>* GetProperty( const std::string& name )
T& GetPropertyValue( const std::string& name );

MyPropertySystem을 상속하여 속성  기능을 추가한다.

class CPlayer : public MyPropertySystem

{

public:

    CPlayer()

    {

        GetPropertyValue<int>("Damage") = 100;   //Damage라는 속성을 추가한다.

    }

};

사용법은 다음과 같다.

    //클래스에 MyProperty 적용한 사용법

    CPlayer player;

    IProperty* prop = player.GetProperty("Damage");

    int nDamage = player.GetPropertyValue<int>("Damage");

    std::cout << "CPlayer " << prop->GetName() << " " << prop->GetTypeName() << " " << nDamage << std::endl;

 

          실행 결과 : CPlayer Damage int 100      

MyPropertySystem을 상속한 CPlayer 객체에서 player.GetPropertyValue<int>("Damage")와 같이 데미지 속성을 추가 할 수 있다.

소스: property_test.zip

 

참고)

GPG 5권 1.3 구성요소 기반 객체 관리
GPG 5권 1.4 템플릿을 이용한 C++ 반영 기능 구현
GPG 2권 1.7 범용 C++ 멤버 접근을 위한 속성 클래스
http://irrlicht.sourceforge.net/phpBB2/viewtopic.php?p=208760&sid=e4e2ec7a742b91a25248b5a724146a40
http://www.ogre3d.org/forums/viewtopic.php?f=1&t=53302
http://svn2.xp-dev.com/svn/Trefall-ComponentSystem/Source/
Fowler - Noll - Vo (FNV)  해쉬 : http://isthe.com/chongo/tech/comp/fnv/