[팁][GPG2 - 1.7][GameObject 속성 관리 팁] 범용 C++ 멤버 접근을 위한 속성 클래스

Binceline 2014. 1. 10. 16:48

프로젝트마다 매번 동일한 코드를 다시 작성하지 않을 수 있도록 재사용이 가능한 핵심 편집기 모듈을 개발할 수 있어야 한다.하지만 게임마다 서로 다른 객체들이 존재한다는 점을 생각해 보면 이는 어려운 문제다. 

이를 해결하기 위해 클래스의 내부에 대한 접근을 가능하게 하는 범용 인터페이스를 제공해야 한다.

...등이 GPG에 나온  내용이다. 여기서 제시하는 관리법은 다음과 같다.


인터페이스는 총 2개의 클래스로 구성된다. 이는 Property, PropertySet 클래스이다. Property는 여러가지 자료형들에 대한 포인터들의 공용체(C에선 union) 와 데이터 형 식별을 위한 열거형(enum) 그리고 속성의 이름을 담는 string 객체 하나를 가진다.


class Property

{

protected:

union Data

{

int* m_pInt;

float* m_pFloat;

std::string* m_pString;

bool* m_pBool;

};


enum Type

{

EMPTY,

INT,

FLOAT,

STRING,

BOOL

};


protected:

Data m_data;

Type m_type;

std::string m_name;


private:

Property& Copy(const Property& obj);

Property& operator= (const Property& obj);


protected:

void EraseType();

void Register(int* value);

void Register(float* valute);

void Register(std::string* new_string);

void Register(bool* valute);


public:

Property();

Property(const Property& source);

Property(std::string const& name);

Property(std::string const& name, int* value);

Property(std::string const& name, float* value);

Property(std::string const& name, std::string* value);

Property(std::string const& name, bool* value);


public:

bool SetUnknownValue(std::string const& value);

bool Set(int value);

bool Set(float value);

bool Set(std::string const& value);

bool Set(bool value);

void SetName(std::string const& name);

std::string GetName() const;

int GetInt();

float GetFloat();

std::string GetString();

bool GetBool();


std::string GetValue();

};


여기서 중요한 점은, 속성 클래스 객체들은 오직 원래 데이터에 대한 포인터들만 저장한다는 것, 그리고 속성은 자신을 위한 메모리도 할당하지 않으므로 속성을 통해서 객체를 다룬다는 것은  원래 데이터의 메모리를 다룬다는 의미이다.

모든 속성들은 PropertySet 클래스를 통해 생성/조작 된다. 이 클래스는 등록된 속성들의 목록을 가지고, 등록/조회 메서드를 제공한다.


class PropertySet

{

protected:

HashTable<Property> m_properties;

private:

PropertySet& Copy(const PropertySet& source_object);

PropertySet& operator= (const PropertySet& source_object);

public:

PropertySet();

PropertySet(const PropertySet& source)

{

Copy(source);

}

virtual ~PropertySet();

void Register(std::string const& name, int* value); 

void Register(std::string const& name, float* value); 

void Register(std::string const& name, std::string* value); 

void Register(std::string const& name, bool* value); 

Property* Lookup(std::string const& name);

bool SetValue(std::string const& name, std::string const& value);

// use these if you know the data type

bool Set(std::string const& name, std::string const& value);

bool Set(std::string const& name, int value);

bool Set(std::string const& name, float value);

bool Set(std::string const& name, bool value);

bool Set(std::string const& name, char* value);

};


이 클래스를 사용하는 예를 보면, 


class GameObject : public PropertySet

{

int m_test;

};


이런 식이다.

공개적으로 노출시킬 속성들은 다음과 같이 반드시 등록해야 한다. 


Register("test_value", &m_test);


이 속성을 사용하는 객체는 LookUp 메서드를 통해서 속성의 데이터에 접근한다.


모든 게임 객체들이 PropertySet을 상속하고, 하나의 마스터 갱신 목록(PropertySet이나.. 모든 게임 객체들이 상속하는 최상위 클래스 목록을 저장하는 객체)에 저장되어 있다면 목록의 포인터를 게임 내 편집기로 넘겨서 속성들을 처리하면 된다.


새로운 객체의 종류가 필요하다면 PropertySet을 상속하고 필요한 멤버들을 속성으로 등록하면 되는데, 이 때 편집기는 객체의 형에 상관없이 그 속성들을 자동적으로 인식하게 된다. 또한 속성들 사이의 관계에 대한 메타 정보를 포함시키고 편집기에서 이 속성 목록을 트리 스타일로 표시할 수 있도록 한다면 객체를 시각적으로 편집하는 작업이 더욱 간단해진다.


이러한 설계법을 Concenttric Ring(동심성 고리) 라고 한다.

PropertySet 클래스는 Property 클래스가 없으면 쓸 수 없지만, Property 클래스는 PropertySet 클래스 없이도 사용 가능하다.


그 외의 용도로는 다음과 같이 쓰일 수도 있다.

- 외부에서 호출될 수 있는 메서드들을 공개적으로 노출하기 위한 용도

- Save 기능을 위한 Save 상태 객체

- 네트워크를 통한 객체 메시지 핸들링

- Send(string xml), Receive(string xml) 같은 메서드들을 추가하면 XML 메시지들을 인코딩/디코딩하는 것도 쉽게 구현 가능.


즉 이렇게 구현하면 속성을 사용하는 코드를 작성할 때 속성의 자료형에 대해 알아야 할 필요가 없으므로 결과적으로 서로 다른 자료형들을 좀 더 유연하게 다룰 수 있게 된다. 

하지만 단점은 약간의 속도 감소이다.

반응형