1、创建角色教程1 概念综述现在我们已经掌握了 STAGE 的用法,并通过引入和布置静态 mesh 建立了有趣的虚拟世界。但为完成一个应用程序,我们需要更多的东西。毕竟,光有一些静态 mesh 和一个天空盒是不能成为一个游戏的。我们需要可以运动的卡车,对运动敏感的警卫,甚至是一个火箭发射台。总之,我们应该能够定制自己的角色!本教程告诉我们如何定制自己的角色。通过教程中的 3 个部分,我们将学习如何创建一面飘动的旗帜,并且我们还可以在游戏编辑器中使用它。我们将学到如何创建一个复杂且富于变化的角色,并通过一个角色代理(ActorProxy)来表现他的尺寸,速度和高度属性,甚至使我们创建的旗帜能够通过编
2、辑器进行访问。本教程假定你已经使用过游戏编辑器(STAGE ) ,假如你还没有使用过,请参考教程“Getting to know the Editor”。注意本教程使用的词语“游戏” , “仿真”及“练习程序”是可以相互替代的,也就是说,他们在本教程中有共同的语义。Part 1角色,角色代理和属性在开始创建飘动的旗帜之前,我们有必要知道 STAGE 是如何建立起来的。Delta3D 的游戏编辑器是建立在一个动态的结构之上的,事实上,STAGE 一开始设计的初衷就是支持用户自定义游戏对象。因为无法预见用户需要的是何种类型的对象,所以我们创建一个一般的体系结构而让用户在此基础上建立自己的游戏对象!
3、下面是我们必须知道的 3 个关键概念。角色 角色就是任何一个我们想在编辑器中使用的具有一定属性的游戏对象。角色可以是你能想到的任何东西,从翱翔的飞机到到跳动的心脏,从可以丢弃的枪支到可怕的绿色怪兽。角色就是用于解释我们的游戏或仿真的专门的对象。在教程的第 2 部分,我们将创建我们的第一个角色“TesselationActor” 。角色代理 角色代理就是角色的(*) 。代理类型是简单的,面向数据的类,它有两项主要工作。首先,它提供一个能被编辑器“理解”的一个共有的,一致的类。其次,它知道它所(*)的角色的一切信息,尤其是角色的属性。在教程的第 2 部分,我们将会为我们的旗帜创建一个角色代理“Te
4、sselationActorProxy” 。角色属性 角色属性就是在角色编辑器中所显示的角色的属性。角色属性使设计者能够改变对象的行为。赋予对象的属性越多,我们的对象用处就越多。在我们给对象设置了大量的重要属性之后,能使设计者不用修改代码就可以充分利用我们的对象。在教程的第 2部分,我们将为对象设置的属性有“宽度” 、 “高度” 、 “周期” 、 “振幅” 、 “相位”和“纹理” 。通过改变这些属性的值,我们可以改变旗帜的尺寸,使它运动得更快,具有更多的波纹,甚至改变它的纹理。下面的图片显示了编辑器中的部分属性。Figure 1 Tesselation Actor Properties既然我们
5、已经知道了角色和角色代理的基本概念,那就只剩下最后一件工作要做了。编辑器没有自动找到我们的角色代理类的能力,因此我们需要把我们的新对象告知给它。我们要做的就是把我们的角色代理放到一个编辑器可以找到的库中。一旦我们创建了这个库我们就可以直接把它导入到编辑器中。这听起来很难但实际并非如此,事实上,在教程的第3 部分将会看到,这只需要 5 行的代码。另外,我们的角色 ALL 只需要一个库。好了,让我们来总结一下!为 STAGE 创建一个角色需要的几个步骤:创建我们的角色 这是我们的专长!创建角色代理 简单的数据包装创建一个角色库 只需一次将代理添加到库中 大约 5 行代码现在,我们已经掌握了 STA
6、GE 的基本结构,可以开始工作了!但在此之前,如果你只想通过示例来学些东西的话,现在就可以转到教程的第 2 部分。然而,如果你是属于技术型的并且真的想知道接下来还有什么,你会看到前面有着更为重要的信息。碰巧,在我们一行代码也没写之前动态角色层(DAL)已经设计好了,下面就是 DAL 构造中的一些设计细节。2.动态角色层(DAL)动态角色层(DAL)为在 C+中设置游戏角色的属性提供了一种灵活的, (*)的机制。DAL 设计中的两个主要的组件是角色代理和角色属性。代理组件是对底层游戏角色的包装,它保存角色个体的所有属性。属性组件通过采集器和设置器函数对象来为单个的游戏角色属性进行赋值。角色代理知
7、道角色的所有属性,这些属性知道如何访问自己的数据。通过这两个组件不必对游戏角色的源代码进行修改就可以设置所有的底层数据。使用这些数据驱动的组件而不是底层的游戏角色,提高了数据封装和代码的可重用性。Figure 2 - Overview动态角色层和编辑器体系结构的视图如上图所示。编辑器主要是用来在一个给定的场景中操纵角色。为支持这一目的,DAL 提供一个泛型模板使得编辑器( STAGE)可以把角色当成泛型对象来使用。这一思想的核心是,通过使用角色的名字,ID 号和属性的集合来以一种一般的方式对待每个角色。对 DAL 组件的细节描述如下。3.角色代理如上所述,角色是我们的练习程序(游戏)中的任何对
8、象,它们有自己的行为,我们要在编辑器中操纵它们。由于角色可以是任何事物,因此编辑器必须对每个角色的内部活动提供保护。为达到这个目的,每个角色都有一个相应的代理类来对角色的内部属性进行访问。本质上,角色代理是一个角色与外部进程进行沟通的管道,或是角色间进行协调通信的协议。图 3 给出了角色代理,角色(DeltaDrawable)和角色属性的类图。Figure 3 - ActorProxy Class Diagram下面对图中描述的类进行说明。DeltaDrawable这是系统中所有角色的基类。DeltaDrawable (dtCore:DeltaDrawable)是游戏场景中所有Delta3D
9、对象的基类,另外,它用一个标签名或是一个唯一的 ID 号作为跟踪信息。MyTruck这是一个用户创建的角色类实例。有些角色类是作为编辑器的一部分预先创建好的,而很多角色类需要由开发人员自己去创建,这样的类也只能被他们自己的应用程序识别。角色类可以创建为任何事物,它们可以有光照设备,转动的零件,机械装置,声音效果,开关及 Delta3D 可以处理的任何其他的东西;它们可以在 Delta3D 体系中以任何方式来绘制自己;它们可以拥有很多属性,也可能一个没有;它们也可以使用共享的资源。ActorProxy这是所有角色代理的基类。代理类的主要用途是将角色类的特征表现为一系列的属性。它有用于获取和设置属
10、性,获取角色和角色类型的操作接口。每个角色必须有一个代理。编辑器会周期性地通过名字对属性进行访问或是用 setValue()操作来改变属性值。MyTruckProxy这是一个用户创建的角色代理类的实例。在这个例子中,truck 有一个叫做速度(speed )的属性,因此,truck 的代理 Truck Proxy 会将一个叫做“speed ”的属性告知给编辑器。于是,Truck Proxy 就可以处理一个请求,诸如通过调用 myTruck.setSpeed (value)来响应setProperty(“speed”, value)。在本例中,TruckProxy 知道 Truck 类的内部活动
11、。它知道“speed”实际上是一个浮点型的数值。大多时候,代理只是作为数据传递的通道。有时,代它也需要对数据进行操作。例如,速度本来是用浮点型的向量(xspeed, yspeed, and zspeed)来表示的,那么代理要向编辑器传递 3 个属性值而实际上底层的速度数据只有一个,因此它将其转换为三维的向量。4.角色属性如上所言,角色属性类为访问角色的属性提供了一个采集器/设置器机制。图 4 对角色属性类进行了详尽的描述(包括一个特别的数据类型的子集) 。Figure 4 - ActorProperty Class Diagram角色属性类的采集器和设置器部分是用来对一个给定的属性类进行数据的
12、采集和设置的算子(函数对象) 。因此,当一个角色属性对象建立以后,它知道如何存储和重新找回它所代表的数据。角色属性类也向编辑器传递角色的属性信息。它有一个可以显示名字的域(name) ,一个描述域(description)和一个枚举型的域。如果属性本身就是枚举型的,属性类就会有一系列枚举域。例如,truck 可能有一个速度(speed )属性,而该属性又有缓慢,适中和快速三种模式;光照可能有一种属性可以设置为定向,闪光,脉冲等等。5.角色库(ActorLibraries)角色库是可分类的组件,它用于将一些有关联的角色和角色代理进行打包。角色库是可以加载到编辑器或其他应用程序的 C+动态链接库。
13、图 5 给出了角色库子系统的类图,下面给出了各组成部分的详细描述。Figure 5 - Dynamic Actor Layer ComponentsLibraryManager这个类是库管理器中主要的类。它有一个已注册类的列表,同时还有一个每个库可以创建的角色类型列表。它也是创建角色代理的主要工具。ActorPluginRegistry这是开发人员用来对其进行扩展以建立自己的注册表的基类。注册表中最重要的操作就是选择一个角色类型并通过角色代理创建一个新的角色。注册表知道它可以建立何种类型的角色。编辑器对对象的类型一无所知,因此注册表负责指明其类型。注册表基类还有一个角色工厂(actorfactory)用来将 ActorPluginRegistry 中的绝大多数(即使并非所有)的角色类型提供给角色映射。MyPluginRegistry