一个简单的游戏开发框架(五.对象Object)

前面提到我们把行为Action从对象Object中分离了出来,用各种不同的行为组合出对象的功能。大家都知道,面向对象的一个类,就是数据和操作的集合。操作(行为)被分离出来了,数据怎么办呢?操作依赖的数据从哪里取得?《游戏编程精粹5》"基于组件的对象管理"中提供了一个方案,把数据都放到操作(书中称为组件)中,当一个操作需要位于另一个操作的数据时,双方通过消息来通讯。个人不喜欢这种搞法,操作之间的依赖关系太复杂了,是网状的。数据应该仍然放在对象中,所有操作都只依赖于对象本身,这样的依赖关系是星状的,要简明得多。当一个对象中不存在某行为需要的数据时,表明这个行为压根就不应该在该对象上执行,这个时候可以用assert处理,也可以让行为什么都不做。

但是这样就出现了一个问题,怎样查询对象中是否存在某个特定属性(数据)呢?需要一个通用接口来操作对象属性,类似下面这种

R GetProperty(int32 propertyID);
void SetProperty(int32 propertyID, const R& property);

propertyID是唯一标识属性的常量,

typedef struct
{
enum E
{
NAME,
POSX,
POSY,
POSZ, COUNT
};
}Properties;

逻辑层可以接着往下定义更多属性。

那个R怎么办?我首先想到的是boost::variant,但是varient只支持最多20个属性。而且这个时候我发现,属性数据没有必要写死在对象里,可以用SetProperty给对象动态增加属性,可以把数据库读出的字段作为属性增加到对象里,也可以用配置文件配置对象属性。这种情况下variant显得太大太复杂,毕竟是大量频繁使用的东西,尽量还是小一点。最后自己写了个简单的:

class Property
{
public:
Property() : _typeID() { _value.pstr = nullptr; }
Property(const Property& rhs)
{
_typeID = rhs._typeID;
if ( == _typeID)
{
_value.pstr = new std::string;
*_value.pstr = *rhs._value.pstr;
}
else
{
_value = rhs._value;
}
}
Property::Property(int32 val)
{
_typeID = ;
_value.i32 = val;
}
Property::Property(uint32 val)
{
_typeID = ;
_value.i32 = (int32)val;
}
Property::Property(float val)
{
_typeID = ;
_value.f = val;
}
Property::Property(std::string& val)
{
_typeID = ;
_value.pstr = new std::string;
*_value.pstr = val;
} ~Property()
{
if ( == _typeID) delete _value.pstr;
} public:
Property& Property::operator=(int32 val)
{
SMART_ASSERT( == _typeID || == _typeID).Msg("错误的类型转换").Debug(AssertLevels::WARNNING);
_typeID = ;
_value.i32 = val;
return *this;
}
Property& Property::operator=(uint32 val)
{
*this = (int32)val;
return *this;
}
Property& Property::operator=(float val)
{
SMART_ASSERT( == _typeID || == _typeID).Msg("错误的类型转换").Debug(AssertLevels::WARNNING);
_typeID = ;
_value.f = val;
return *this;
}
Property& Property::operator=(const char* val)
{
SMART_ASSERT( == _typeID || == _typeID).Msg("错误的类型转换").Debug(AssertLevels::FATAL);
if (!_value.pstr)
_value.pstr = new std::string;
_typeID = ;
*_value.pstr = val;
return *this;
} operator int32() const
{
SMART_ASSERT( == _typeID).Msg("错误的类型转换").Debug(AssertLevels::WARNNING);
return _value.i32;
}
operator uint32() const
{
SMART_ASSERT( == _typeID).Msg("错误的类型转换").Debug(AssertLevels::WARNNING);
return (uint32)_value.i32;
}
operator float() const
{
SMART_ASSERT( == _typeID).Msg("错误的类型转换").Debug(AssertLevels::WARNNING);
return _value.f;
}
operator const std::string&() const
{
SMART_ASSERT( == _typeID).Msg("错误的类型转换").Debug(AssertLevels::FATAL);
return *_value.pstr;
} bool operator==(Property& rhs)
{
return _value.i32 == rhs._value.i32;
} private:
Property& operator=(const Property& rhs) { return *this; } private:
union
{
int32 i32;
float f;
std::string* pstr;
}_value; char _typeID;
};

以union为基础,只支持int, float, string3种类型,大多数情况都够用了。如果有必要,可以加上int64和double。

Object类基本上就是管理一个属性数组,如下:

class Object
{
public:
virtual ~Object(); public:
virtual bool LoadObject(const char* fileName);
virtual void OnPropertyChanged(uint32 propertyID, Property& oldValue) {} enum { OBJECT_TYPE = BaseObjects::OBJECT };
virtual uint32 GetType() { return OBJECT_TYPE; } public:
Property& GetProperty(uint32 propertyID)
{
SMART_ASSERT(_propertys.find(propertyID) != _propertys.end()).Msg("未支持的属性").Debug(AssertLevels::FATAL);
return _propertys[propertyID];
} template<typename T>
void SetProperty(uint32 propertyID, T value)
{
if (_propertys.find(propertyID) == _propertys.end())
return;
T oldValue = _propertys[propertyID];
if (oldValue == value)
return;
_propertys[propertyID] = value; Property val = oldValue;
OnPropertyChanged(propertyID, val);
} template<uint32 PropertyGroupID>
void OnPropertyGroupChanged(typename PropertyGroup<PropertyGroupID>::type& oldValues); template<uint32 PropertyGroupID>
void SetPropertyGroup(typename PropertyGroup<PropertyGroupID>::type& values)
{
typename _SetPropertyGroup<PropertyGroupID>::template Set<PropertyGroup<PropertyGroupID>::Count> writer;
writer(this, values);
}

void RunAction(Action* pAction);
void StopAction(uint32 actionID); protected:
template<uint32 PropertyGroupID>
struct _SetPropertyGroup
{
template<int count, typename Null = void>
struct Get
{
typename PropertyGroup<PropertyGroupID>::type operator()(Object* pObject) { assert(false); }
};
template<typename Null>
struct Get<, Null>
{
typename PropertyGroup<PropertyGroupID>::type operator()(Object* pObject)
{
typename PropertyGroup<PropertyGroupID>::type1 p1 = pObject->GetProperty(PropertyGroup<PropertyGroupID>::_1);
typename PropertyGroup<PropertyGroupID>::type2 p2 = pObject->GetProperty(PropertyGroup<PropertyGroupID>::_2);
typename PropertyGroup<PropertyGroupID>::type3 p3 = pObject->GetProperty(PropertyGroup<PropertyGroupID>::_3);
return std::move(std::make_tuple(p1, p2, p3));
}
}; template<int count, typename Null = void>
struct Set
{
void operator()(Object* pObject, typename PropertyGroup<PropertyGroupID>::type& values) { assert(false); }
};
template<typename Null>
struct Set<, Null>
{
void operator()(Object* pObject, typename PropertyGroup<PropertyGroupID>::type& values)
{
typename PropertyGroup<PropertyGroupID>::type oldValues = Get<>()(pObject); pObject->_SetProperty(PropertyGroup<PropertyGroupID>::_1, std::get<>(values));
pObject->_SetProperty(PropertyGroup<PropertyGroupID>::_2, std::get<>(values));
pObject->_SetProperty(PropertyGroup<PropertyGroupID>::_3, std::get<>(values)); pObject->OnPropertyGroupChanged<PropertyGroupID>(oldValues);
}
};
}; template<typename T>
void _SetProperty(uint32 propertyID, T value)
{
if (_propertys.find(propertyID) == _propertys.end())
return;
_propertys[propertyID] = value;
} protected:
std::map<uint32, Property> _propertys;
std::list<Action**> _runningActions;
}; template<>
inline void Object::OnPropertyGroupChanged<Propertys::POSX>(typename PropertyGroup<Propertys::POSX>::type& oldValues)
{
Property val(std::get<>(oldValues));
OnPropertyChanged(Propertys::POSX, val);
}

有两个比较奇怪的接口:

    template<uint32 PropertyGroupID>
void OnPropertyGroupChanged(typename PropertyGroup<PropertyGroupID>::type& oldValues); template<uint32 PropertyGroupID>
void SetPropertyGroup(typename PropertyGroup<PropertyGroupID>::type& values)
{
typename _SetPropertyGroup<PropertyGroupID>::template Set<PropertyGroup<PropertyGroupID>::Count> writer;
writer(this, values);
}

因为属性都被分拆成int, float, string三个类型,原来可能存在于对象中的结构都被打散了。如果希望把某些属性作为一个整体来操作,以避免反复不必要地调用OnPropertyChange,就需要用到SetPropertyGroup。逻辑层需要特化PropertyGroup类,象下面这样:

template<uint32 PropertyID> struct PropertyGroup {};

template<>
struct PropertyGroup<Properties::POSX>
{
enum Content
{
_1 = Properties::POSX,
_2 = Properties::POSY,
_3 = Properties::POSZ,
Count =
};
typedef float type1;
typedef float type2;
typedef float type3;
typedef std::tuple<float, float, float> type;
};
上一篇:thinkphp ajax 实例 实现


下一篇:Myeclipse8.5 subscription expired自己动手获取Myeclipse的注册码