在前一篇日志里,我决定使用Silverlight来开发虚拟实验室引擎。接下来,我将从上到下,构建这个引擎。
确定实验室中的对象
构建一个虚拟的产品,首先映入脑海的就是该产品的具体表现形式。根据这个,我把实验室中的对象分成两个个部分:场景和物品。
所谓场景,实际上相当于背景,像地板这些永远不会覆盖在人物之上的东西统统可以归咎于场景。在渲染上,场景也是最好对待的,它往往不会改变位置,永远位于物品下方,不存在空间排序问题及遮挡问题。
物品,大概相当于前景。例如桌子、柜子等等。这些东西在渲染的时候,可能需要按照它们所处的空间位置排列,造就遮挡的效果。根据唯物主义理论,人物也是一种物品,不过和桌子比,人物会动,需要特别处理。
有了上面的想法,我就能用一个UML类图来描述它们的继承关系:
VObject
VObject(Virtual Object)是虚拟实验室引擎中所有虚拟对象的祖宗。里面包括一些诸如所在位置(X,Y),大小(Width,Height),配置文件等场景和物品都拥有的属性。
VItem
除了继承自VObject,VItem(Virtual Item)可能还包括一组动作图片(例如场景中的小白兔会不停地跳跳跳,因此需要用一组跳跃的图片来构造这个物品)。此外,如果该物品有多个动作,还需要一个动作列表来记录这些动作。VItem还有一个VPlace的指针来指向它属于哪个场景。
VPlace
VPlace(Virtual Place)除了用一组图片(和VItem的图片不同,由于场景很大,如果使用单幅图片来渲染场景,如果客户端网速较慢,会造成场景长时间得不到渲染。因此,我采用图片拼接的方式来渲染场景:把一张大图分割成若干张小图)来渲染自身,还需要用一个列表来记录该场景中的VItem。
VPerson和VRoom
进一步抽象,具体到人物和实验室本身了。
用XML来记录虚拟对象(Virtual Object)
这里我不想长篇大论使用XML的好处,我只需要申明,通过XElement类来读取XML数据是意见非常非常容易的事情。因此,我决定用XML作为载体,在引擎和服务器间传递VObject对象的信息,例如我可以这样描述一个物品:
<person name='周慊' width='147' height='144' centerX='73' centerY='144' x='1100' y='780' file='person.xml' id='80d76e6b-08d6-4225-bdcc-bf68ca19ca58' actionid='default' />
还可以用这样一个XML来描述一张地图:
<place name='大地图' width='1800' height='1200' centerX='0' centerY='0' x='0' y='0' file='place.xml' />
上面两个XML对象中的file,就是这两个虚拟对象的配置文件,关于配置文件,请参考下一节。
素材及配置文件
前面说到,VPlace和VItem都有图片素材,二者的区别可以表示如下:
物品
我用一张图片,里面包含了该物品能拥有的动作:
我使用一个XML配置文件来描述动作列表,以及每一个动作,需要用到以上图片哪一部分(person.xml):
<item name='person' image='person.png'> <action name='faceright,default' next='faceright'> <rectangle width='147' height='144' x='0' y='0' /> <rectangle width='147' height='144' x='147' y='0' /> <rectangle width='147' height='144' x='294' y='0' /> <rectangle width='147' height='144' x='441' y='0' /> <rectangle width='147' height='144' x='588' y='0' /> <rectangle width='147' height='144' x='735' y='0' /> <rectangle width='147' height='144' x='882' y='0' /> <rectangle width='147' height='144' x='1029' y='0' /> </action> <action name='facerightdown' next='facerightdown'> <rectangle width='147' height='144' x='0' y='144' /> <rectangle width='147' height='144' x='147' y='144' /> <rectangle width='147' height='144' x='294' y='144' /> <rectangle width='147' height='144' x='441' y='144' /> <rectangle width='147' height='144' x='588' y='144' /> <rectangle width='147' height='144' x='735' y='144' /> <rectangle width='147' height='144' x='882' y='144' /> <rectangle width='147' height='144' x='1029' y='144' /> </action> <!--…………--> </item>
场景
场景采用图片拼接的方式构成:
同样的,我也用一个XML配置文件来记录,如何拼接这些图片(place.xml):
<place width="100" height="100"> <mappart left="0" top="0" image="0-0.jpg" /> <mappart left="100" top="0" image="0-1.jpg" /> <mappart left="200" top="0" image="0-2.jpg" /> <mappart left="300" top="0" image="0-3.jpg" /> <mappart left="400" top="0" image="0-4.jpg" /> <mappart left="500" top="0" image="0-5.jpg" /> <mappart left="600" top="0" image="0-6.jpg" /> <mappart left="700" top="0" image="0-7.jpg" /> <mappart left="800" top="0" image="0-8.jpg" /> <mappart left="900" top="0" image="0-9.jpg" /> <!--…………--> </place>
渲染这些对象
和VObject类似,采用UIObject层级关系作为渲染对象: