(转)Delta3D源码分析
最近学习Delta3D, 2.4版忙着发布,一直不能成功编译SimCore, 索性静下心来看看源码,官网上竟然提供了几个重要组建的软件设计说明书(SDD),虽说基本都是2005版了,不过我看了后觉得主要构架仍然没有改变, 这几份SDD对于学习Delta3D具有很好的参考价值。
分析源码要从顶层着手,所以在看了GM部分的SDD后,我翻出源码看了下,顺便画几张序列图,算是做个笔记。
分析主要根据TankTargetTutorial(这也是我现在唯一能跑的DEMO),源码版本是09-10-12日的SVN。
Delta3D上层构架主要由 GameManager, GameActor, GameActorProxy, Components这几个组建构成:
总体构架图
由上图可看出 GameActor,Components等其他组建都统一由GM(GameManager 下同)管理。
GameManage: 统一管理游戏中的各种资源。完成程序运行时消息处理配送等重要工作,是一个枢纽类
GameActor:主要是游戏中的实际对象,如坦克,飞机等,它需要完成处理与自己相关的消息,更新自我状态等任务。
GameActorProxy: 和GameActor是一对一的关系,既用户每定义一个GameActor,就需要定义一个预期相关的GameActorProxy。其主要任务是提供一个统一的对GameActor的各个属性进行存取操作的接口。接口的统一方便了GM的实现。同时在GM构架中GameActorProxy还起到了消息处理中间层的重要作用。
Components: 是一些游戏的辅助系统,主要功能是在一个较高层次上统一对系统中的信息进行处理(如实现网络通信,用户输入处理等)。
简单的说,使用Delta3D编写游戏,我们只需要实现自己的GameActor,以及Components以完成本应用需要的特定任务,然后按照一定的接口规范将其交给GM管理即可。我们来看一下大致的情况
第一步: 导入Actor
在现有系统下,Delta3D要求用户将自己的GameActor编译成插件形式(Windows下为DLL文件),进而实现能动态载入系统的功能。
导入Actor
创建Actor
GameManage: 统一管理游戏中的各种资源。完成程序运行时消息处理配送等重要工作,是一个枢纽类
GameActor:主要是游戏中的实际对象,如坦克,飞机等,它需要完成处理与自己相关的消息,更新自我状态等任务。
GameActorProxy: 和GameActor是一对一的关系,既用户每定义一个GameActor,就需要定义一个预期相关的GameActorProxy。其主要任务是提供一个统一的对GameActor的各个属性进行存取操作的接口。接口的统一方便了GM的实现。同时在GM构架中GameActorProxy还起到了消息处理中间层的重要作用。
Components: 是一些游戏的辅助系统,主要功能是在一个较高层次上统一对系统中的信息进行处理(如实现网络通信,用户输入处理等)。
简单的说,使用Delta3D编写游戏,我们只需要实现自己的GameActor,以及Components以完成本应用需要的特定任务,然后按照一定的接口规范将其交给GM管理即可。我们来看一下大致的情况
第一步: 导入Actor
在现有系统下,Delta3D要求用户将自己的GameActor编译成插件形式(Windows下为DLL文件),进而实现能动态载入系统的功能。
导入Actor
上图显示了Delta3D导入用户定义的GameActor的流程,用户除了要实现自己的GameActor外,还需实现一个ActorPluginRegistry类来作为插件的接口来完成插件注册的工作。很自然,一个ActorPluginRegistry类可以包含多个GameActor对象。
第二步:创建Actor
通过第一步我们将GameActor集以插件的形式导入进来,这时程序就可以创建我们需要的GameActor对象了。
第二步:创建Actor
通过第一步我们将GameActor集以插件的形式导入进来,这时程序就可以创建我们需要的GameActor对象了。
创建Actor
从上图我们可以看出创建GameActor又分两步,第一步是CreateActor, 这一步目的是从刚才导入并注册到系统的插件中找到并创建一个我们想要的GameActor对象。
第二部为AddCreate, 这一步可以理解为将得到的Actor对象加入到游戏场景中。在这一步我们需要完成该GameActor对象的初始化操作, 最重要的任务是对该GameActor需要处理的消息注册到GM中。这一步也是由用户完成的,这样加强了一定的灵活性。
第三步:消息处理流程
有了上面两步,这一步基本上就是Delta3D自己的事情了,我们来看一下Delta3D是如何处理消息的。
消息处理
所有基于Delta3D的程序都要有一套Application处理机制,我们来看下它的实现。
类图
第二部为AddCreate, 这一步可以理解为将得到的Actor对象加入到游戏场景中。在这一步我们需要完成该GameActor对象的初始化操作, 最重要的任务是对该GameActor需要处理的消息注册到GM中。这一步也是由用户完成的,这样加强了一定的灵活性。
第三步:消息处理流程
有了上面两步,这一步基本上就是Delta3D自己的事情了,我们来看一下Delta3D是如何处理消息的。
消息处理
上图显示了Delta3D在GM构架下处理消息的流程(仅仅是GameActor接受消息部分)。 一般处理是流程1,这是是GM维护一个消息和GameActor的映射关系表。
然而Delta3D也提供了一定的灵活性,允许GameActor自己同时也维护一个消息和自身的映射表来更灵活的处理消息。如流程2是所示。Delta3D有两套消息处理框架,一套是较低层的以Application类为核心的处理机制,大部分源码包中的Example采用该机制。 一套是较高层的以GameManager类为核心的处理机制,为源码中的Demo所采用。
然而Delta3D也提供了一定的灵活性,允许GameActor自己同时也维护一个消息和自身的映射表来更灵活的处理消息。如流程2是所示。Delta3D有两套消息处理框架,一套是较低层的以Application类为核心的处理机制,大部分源码包中的Example采用该机制。 一套是较高层的以GameManager类为核心的处理机制,为源码中的Demo所采用。
所有基于Delta3D的程序都要有一套Application处理机制,我们来看下它的实现。
类图
在上图中需要说明的是, 低层消息基于Base类的消息传送机制(BASE类消息机制可见OSGCHINA社区Delta3D版的一篇文章), BaseABC是一个抽象类,定义了四个纯虚函数。其OnMessage函数执行了将消息分配到这四个函数的工作。 Application类通过实现该四个纯虚函数完成消息处理,同时Application拥有键盘和鼠标的监听器,通过实现并注册相应的回调函数完成键盘,鼠标的输入响应。 GameApplication与Application类的唯一区别就是引入了GM。
程序运行时,GameApplication除了完全照搬执行Application的消息循环外,还通过GM进行高层消息处理循环,也即其同时拥有两套消息机制。
需要注意的是,GM继承自Base,他在构建时向System类进行消息注册,可以直接和系统底层消息队列打交道。
程序运行时,GameApplication除了完全照搬执行Application的消息循环外,还通过GM进行高层消息处理循环,也即其同时拥有两套消息机制。
需要注意的是,GM继承自Base,他在构建时向System类进行消息注册,可以直接和系统底层消息队列打交道。
Delta3D采用OSG进行渲染工作。我们来看一下其低层接口是如何设计的。
接口类图
接口类图
上图中绿色的类直接提供了对OSG相关对象的封装。 DeltaDrawable类是Delta3D中所有可显示对象的基类(例如Actor, GameActor,Object等),它封装了一个osg:Node。
Delta3D通过Scene类进行场景管理,可以看到Scene和View继承自Base,因而也都拥有了接受,发送低层消息的能力。 因为Delta3D在低层内置了ODE进行物理模拟,Scene类在初始化时向System进行了注册,现在主要是进行接受每帧System发出的消息,调用ODE接口完成物理计算操作。而View类没有通过封装osg::View进行相关视口显示工作,没有重载OnMessage函数,因而不接受消息,主要工作配合Application类处理用户交互的输入。
程序启动时, 有关函数进行关联操作,来看下序列图:
Delta3D通过Scene类进行场景管理,可以看到Scene和View继承自Base,因而也都拥有了接受,发送低层消息的能力。 因为Delta3D在低层内置了ODE进行物理模拟,Scene类在初始化时向System进行了注册,现在主要是进行接受每帧System发出的消息,调用ODE接口完成物理计算操作。而View类没有通过封装osg::View进行相关视口显示工作,没有重载OnMessage函数,因而不接受消息,主要工作配合Application类处理用户交互的输入。
程序启动时, 有关函数进行关联操作,来看下序列图: