• 一个简单的游戏开发框架(五.对象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(0) { _value.pstr = nullptr; }
        Property(const Property& rhs)
        {
            _typeID = rhs._typeID;
            if (3 == _typeID)
            {
                _value.pstr = new std::string;
                *_value.pstr = *rhs._value.pstr;
            }
            else
            {
                _value = rhs._value;
            }
        }
        Property::Property(int32 val)
        {
            _typeID = 1;
            _value.i32 = val;
        }
        Property::Property(uint32 val)
        {
            _typeID = 1;
            _value.i32 = (int32)val;
        }
        Property::Property(float val)
        {
            _typeID = 2;
            _value.f = val;
        }
        Property::Property(std::string& val)
        {
            _typeID = 3;
            _value.pstr = new std::string;
            *_value.pstr = val;
        }
    
        ~Property()
        {
            if (3 == _typeID) delete _value.pstr;
        }
    
    public:
        Property& Property::operator=(int32 val)
        {
            SMART_ASSERT(0 == _typeID || 1 == _typeID).Msg("错误的类型转换").Debug(AssertLevels::WARNNING);
            _typeID = 1;
            _value.i32 = val;
            return *this;
        }
        Property& Property::operator=(uint32 val)
        {
            *this = (int32)val;
            return *this;
        }
        Property& Property::operator=(float val)
        {
            SMART_ASSERT(0 == _typeID || 2 == _typeID).Msg("错误的类型转换").Debug(AssertLevels::WARNNING);
            _typeID = 2;
            _value.f = val;
            return *this;
        }
        Property& Property::operator=(const char* val)
        {
            SMART_ASSERT(0 == _typeID || 3 == _typeID).Msg("错误的类型转换").Debug(AssertLevels::FATAL);
            if (!_value.pstr)
                _value.pstr = new std::string;
            _typeID = 3;
            *_value.pstr = val;
            return *this;
        }
    
        operator int32() const
        {
            SMART_ASSERT(1 == _typeID).Msg("错误的类型转换").Debug(AssertLevels::WARNNING);
            return _value.i32;
        }
        operator uint32() const
        {
            SMART_ASSERT(1 == _typeID).Msg("错误的类型转换").Debug(AssertLevels::WARNNING);
            return (uint32)_value.i32;
        }
        operator float() const
        {
            SMART_ASSERT(2 == _typeID).Msg("错误的类型转换").Debug(AssertLevels::WARNNING);
            return _value.f;
        }
        operator const std::string&() const
        {
            SMART_ASSERT(3 == _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<3, 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<3, Null> { void operator()(Object* pObject, typename PropertyGroup<PropertyGroupID>::type& values) { typename PropertyGroup<PropertyGroupID>::type oldValues = Get<3>()(pObject); pObject->_SetProperty(PropertyGroup<PropertyGroupID>::_1, std::get<0>(values)); pObject->_SetProperty(PropertyGroup<PropertyGroupID>::_2, std::get<1>(values)); pObject->_SetProperty(PropertyGroup<PropertyGroupID>::_3, std::get<2>(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<0>(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 = 3 }; typedef float type1; typedef float type2; typedef float type3; typedef std::tuple<float, float, float> type; };
  • 相关阅读:
    自学Linux Shell5.2-shell内建命令history alias
    自学Linux Shell5.1-shell父子关系
    vue2.0 实现导航守卫(路由守卫)
    vue2.0 关于Vue实例的生命周期
    vue2.0 正确理解Vue.nextTick()的用途
    vue2.0 项目build后资源文件报错404的解决方案
    vue2.0 vetur插件提示 'v-for' directives require 'v-bind:key' directives 的解决办法
    vue2.0 微信oauth认证的正确调用位置
    vue2.0 样式表引入的方法 css sass less
    vue2.0 配置sass
  • 原文地址:https://www.cnblogs.com/bnudc/p/3423833.html
Copyright © 2020-2023  润新知