http://bbs.osgchina.org/forumdisplay.php?fid=17
作者:阿威
声明:如果有引用或借用他人文字会在文章末尾表明。本文章只是个人学习交流使用,如果转载请保持文章的完整性。
dtCore::Base
dtCore::RefPtr< T >
说到delta3d自然不能不讨论osg,毕竟osg是他的最重要的部分,没有osg其他的一切都是不存在的。但实际上在delta3d的使用中大部分情况下并不会感觉到osg的存在,所以osg的一些特性和组织方式或者是对事件的处理方式在delta3d中是没有必然联系的。如果没有必要我尽量的不提osg的事情。
这里要提到的dtCore::Base 就是组成delta3d的最重要的一个类。从继承关系上看,几乎所有重要的类型都是从这里继承出来的。从dtCore::Base的定义上看我们知道他主要完成了两个功能:消息机制、实例的管理、智能指针。当然他也允许给对象设置名称和id,这也是为前三者服务的。
先说说消息机制,他并没有使用osg里的callback方式,为什么。因为callback方式只是在被调用的时候被执行,而我们不能有效的对他随意的调用。而且如果callback方式想对其他对象进行操作的时候,必须去得到对方的指针,每次初始化都要这么做,总是要用到Visitor类型,总是很痛苦。最重要的原因是callback有很多个,你没办法把他们统一成一个简单的类型用来继承,也不能方便的扩展他。所以dtCore::Base 简单的解决了这个问题,只需要从他继承出来,就可以方便的处理任何你想让他处理的消息,谁是发送者,谁是接收者,都随你使用。(具体的使用方法参见下面的代码片段说明)。
实例的管理就是可以使用类的静态方法对一个类的所有实例进行添加删除和查找的管理功能。这样可以只知道类型和名称就轻松的得到一个实例对象的指针。这个功能是通过几个宏定义来完成的。(参见下面的代码片段说明)
智能指针的功能实现是和dtCore::RefPtr< T >有精密关系的,直接说dtCore::RefPtr< T >吧。
现在说说dtCore::RefPtr< T > ,这个类是从osg::ref_ptr< T >继承出来的,但没有对其功能做实质的改动。至于为什么要使用智能指针,这个解释起来可以很长也可以很短,我的解释很简单,虽然感觉有点怪怪的(因为很多操作会增加或者减少引用数量,需要认真的搞清楚),但是如果你想在执行期间动态的添加删除场景中的物体,那不使用他的话会直接崩溃的。还有需要说明的就是不是只有dtCore::Base 的继承类可以使用osg::ref_ptr< T >来作为指针。原因很简单,osg::ref_ptr< T >对应的是osg::Referenced ,只要继承自osg::Referenced就应该可以作为指针的对象。但是目前讨论的是dtCore::Base而dtCore::Base也是从osg::Referenced继承来的,所以他可以被作为dtCore::RefPtr< T >所指的对象(参见下面的代码片段说明)
如何从dtCore::Base继承一个类
如何使用实例对象管理(不必要的代码都忽略掉了)
dtcore/system.h文件中
class DT_CORE_EXPORT System : public Base
{
DECLARE_MANAGEMENT_LAYER(System)
};
dtcore/system.cpp文件中
IMPLEMENT_MANAGEMENT_LAYER(System)
System::System()
{
RegisterInstance(this);
}
System::~System()
{
DeregisterInstance(this);
}
这里的dtCore::System就是继承自dtCore::Base的一个自类,那么具体做了什么呢。
DECLARE_MANAGEMENT_LAYER(T) 是一个宏定义,仔细看他的细节:
#define DECLARE_MANAGEMENT_LAYER(T) /
private: /
static std::vector<T*> instances; /
static void RegisterInstance(T* instance); /
static void DeregisterInstance(T* instance); /
public: /
static int GetInstanceCount(); /
static T* GetInstance(int index); /
static T* GetInstance(std::string name);
主要就是对实例对象的操作,一个instances的数组,RegisterInstance注册和 DeregisterInstance销毁,GetInstanceCount得到实例的数量。GetInstance选择一个实例返回他的指针。非常简单的功能。
IMPLEMENT_MANAGEMENT_LAYER(T) 是对上面宏定义的方法做的实现,看一下就明白了,这里不再罗列代码。
在dtCore::System的构造和销毁函数中分别调用了 RegisterInstance(this); 和 DeregisterInstance(this); 是不是很简单。只是添加了两个洪定义和两个函数的调用就完成了对dtCore::Base全部功能的继承。
如何使用消息机制(不必要的代码都忽略掉了)
继承自dtCore::Base的类都可以使用消息机制,主要是通过下面4个方法的操作来实现的:
void AddSender( Base *sender );
void RemoveSender( Base *sender );
virtual void OnMessage(MessageData *data) {}
void SendMessage(const std::string& message="", void *data=0);
examples/testphysics/testphysics.cpp文件中
class Updater : public Base
{
public:
Updater(Scene* scene) :
mScene(scene)
{
AddSender( mScene.get() );
}
protected:
virtual ~Updater()
{
RemoveSender( mScene.get() );
}
public:
virtual void OnMessage( MessageData* data )
{
if( data->message == "collision" )
{
}
}
private:
dtCore::RefPtr<Scene> mScene;
};
Updater 在这里的作用实际上是一个接收事件的处理器,但是遗憾的是本范例中没有实际对事件做任何的反映,因为 if( data->message == "collision" ) 的内容被注释掉了。不过这不影响我们的理解。
这个类的实例对象是在 updater = new Updater( GetScene() ); 这一行被建立的,GetScene得到的是当前的场景的指针(这个以后的章节回讲到),作为Updater初始化的参数了。而updater的初始化过程中执行了AddSender( mScene.get() );一行来把场景添加到自己的发送者列表中去,这样就建立了一个消息连接,也就是说如果场景发出消息,update就可以接收到。RemoveSender( mScene.get() );一行正好相反,把这个消息连接给打断了。而且需要说明的是,消息的连接是有方向的,但是多个连接之间是没有方向或者个数限制的,就是说想怎么连接都可以。不同的发送者完全可以使用同样的消息,处理的时候也可以清晰的分别处理。
现在说说建立连接之后,消息是怎么触发的,怎么来处理他们。从上面的代码可以看出 OnMessage( MessageData* data )是实际的消息接收和处理的唯一入口。MessageData* data 就是接收到的消息,我们只要对他进行判断和处理就可以了,是不是很简单。那么怎么触发消息呢,本范例中是没有明显的提示的,因为发送者是 dtCore::Scene 这个是delta3d提供的核心类型我们只能从他的定义中去找答案了。搜索一下我们在 dtcore/scene.cpp 文件中找到了 scene->SendMessage("collision", &cd); 一行。就是从这里发送出去的消息,这个消息回被 update 的 OnMessage 处理。
Deleta3d的消息是在触发的时候就立即执行的,这是值得注意的。
如何使用智能指针(不必要的代码都忽略掉了)
我觉得这个问题没有太多必要解释。简单的说就是在最后一个被引用的指针也不再指向自己的时候,这个对象就被销毁了,等于是delete操作。主要的麻烦是在你想销毁一个对象的时候,他总是不能和你想象的一样死掉。实际使用的过程中我遇到很多这样的问题,好象没什么窍门。只能多做做实验。
对智能指针的操作也不复杂,下面是常用的几个
get() 用来得到一个普通的指针。
valid() 测试是否指向了有效的对象。
swap() 交换两个指针指向的对象。
release() 释放指针,注意这不是释放对象,而是不在指向对象。
实际上只要记住一点,就是一个对象只要被一个智能指针只向自己,他就不会被释放,如果他没有象你想象的一样被销毁,那就是找找到底是谁还指向他。不过如果这个智能指针不是建立的,那就非常恼火了,比如dtCore::Scene就给场景中的物体建立一个智能指针的列表,除非你使用RemoveDrawable方法,不然这个对象是删除不了的。