By Yuanxin Li
目的
在这个例程中,你将开始学习如何在Wonderland项目里创建一个新的单元类型。单元是一个3D卷;创建新的单元类型的主要意义是开发者扩展Wonderland的功能。这个例程是五个例程中的第一个。在这个第一个例程中,你将学会如何编写一个组成新的单元类型的重要类,以及如何通过WFS在世界里创建一个实例。
这个例程是为Wonderland项目的v0.3和v0.4版本设计的。
预备知识
在开始这个例程之前,你应该首先成功下载和编译Wonderland项目。关于下载和运行Wonderland的说明可以在这里找到。你需要下载和编译主要的Wonderland工作区(lg3d-wonderland)以及可选的模块工作区(wonderland-modules)。wonderland-modules工作区提供了编译你的新单元类型的必要框架。
另外,在你开始学习这个例程之前,你还需要熟悉Wonderland的架构(这里)以及Wonderland文件系统WFS的元素(查看这个例程)。
单元架构
Wonderland里面的可见3D基础构建块被称为单元cells,由服务器端和客户端组成(图1)
服务器端由一个单独的类(MyCellGLO,图1)组成,这个类维护了世界中各个客户端参与者之间的共享状态。它有一个“GLO”的后缀,表明它是一个参与Darkstar游戏引擎机制的一个特别的对象。客户端(MyCell,图1)负责在世界里通过加载一个模型资源或者使用Java3D API直接描绘对象来渲染单元。单元的实例通过一个包含XML格式的单元描述文件——WFS(mycell-wlc.xml)来加载到世界当中,这个文件提供了一个单元的可配置参数的值。
Shape单元:一个新的单元类型
在这个例程中,你将学会如何在世界里创建一个描绘3D形状的新的单元类型。形状的类型(方形还是圆形)是通过WFS里的XML格式化的单元描述文件里的一个参数设置的。
设置好你的构建环境
如果你还没有做好,你需要下载和编译好lg3d-wonderland和wonderland-modules这两个工作区。这个例程假设你对Netbeans IDE很熟悉——因为Netbeans的编译机制是基于ant的,你也可以使用其他使用ant来构建工程的IDE来完成这个例程,或者你也可以使用命令行工具。
当你编译好你的工作区后,在Netbeans提供过文件->打开项目 来打开wonderland-modules工作区。
1、在Netbeans窗口的左上方点击打开“文件”视图。
2、展开节点以使wonderland-modules/src/modules/apps/3d路径被完全展开。
3、在3d文件夹里面,单击右键,选择 新建-文件夹,在文件夹名称中输入shapecell,点击完成。
在你的新文件夹中,你将创建两个文件:build.xml和project.xml。
1、在shapcell文件夹中,单击右键选择新建-其他。
2、在对话框中选择 其他-空文件,点击下一步。
3、将文件命名为build.xml,点击完成。
复制以下XML文件到你的build.xml文件中,并且保存:
<!-- Use my-build.properties to override default values in build.properties --> <!-- set the project name --> <!-- import the common build attributes --> <!-- the name of the client jar file --> <!-- all files that should be built as part of the client jar file --> <!-- the name of the server jar file --> <!-- all files that should be built as part of the server jar file --> <!-- extra classes for compiling the server --> <!-- <!-- copy targets so NetBeans will recognize them --> <!-- You can override default methods to add functions at various points. See module-common.xml for more information: -pre-init: before init -post-init: after init -pre-compile-client: before compiling client -post-compile-client: after compiling client -pre-compile-server: before compiling server -post-compile-server: after compiling server -pre-jar-client: before jar client -post-jar-client: after jar client -pre-jar-server: before jar server -post-jar-server: after jar server -pre-clean: before clean -post-clean: after clean --> deprecation="${build.showdeprecation}" destdir="${module.classes.dir}" srcdir="${module.javasrc.dir}" nowarn="true" source="1.5" target="1.5"> |
下一步,你将创建project.xml文件,这个文件将帮助你创建一个Netbeans工程。
1、右键单击shapecell文件夹,选择新建-文件夹,将其命名为nbproject,点击完成。
2、在nbproject子文件夹中,创建一个空白文件并命名为project.xml,拷贝以下代码并保存:
<!-- Do not use Project Properties customizer when editing this file manually. --> |
源代码包结构
当你的项目和ant文件建立好以后,你就可以创建放置源代码的目录了。每个单元的源代码被分为3个包:client,common以及server。client包里面的类将会编译到Wonderland的客户端中,server包里面的类将会被编译到Wonderland服务器中,而common包里面的类将会同时编译到服务器和客户端中。
1、右键单击shapecell文件夹,选择新建-文件夹,命名为src/classes,点击完成。
2、在classes文件夹节点中单击右键选择新建-Java包,命名为org.jdesktop.lg3d.wonderland.shapecell.client。
3、在classes文件夹节点中单击右键选择新建-Java包,命名为org.jdesktop.lg3d.wonderland.shapecell.common。
4、在classes文件夹节点中单击右键选择新建-Java包,命名为org.jdesktop.lg3d.wonderland.shapecell.server。
ShapeCellGLO服务器类
首先你需要创建服务器端的类来表示这个单元,ShapeCellGLO。这个服务器端类的目的是储存由WFS里的XML单元描述文件定义的基础配置信息。它同时为所有客户端管理单元的共享状态。为了帮助管理客户端的状态,包括单独的,同步的更新状态,ShapeCellGLO类应该参与到Darkstar事务型游戏架构的机制中。然而,在这个第一个例子里,ShapeCellGLO并不与其他客户端共享状态,而且不使用Darkstar的事务机制。(在以后的例子中会加入这些功能。)
1、右键单击server文件夹,选择新建-Java类,命名为ShapeCellGLO,点击完成。
Netbeans会创建一个类的架构,类似:
package org.jdesktop.lg3d.wonderland.shapecell.server;
public class ShapeCellGLO {
}
首先,拷贝这些适当的导入语句到你的Java类中(在package语句后)
import org.jdesktop.lg3d.wonderland.darkstar.server.cell.*;
import org.jdesktop.lg3d.wonderland.darkstar.server.setup.*;
import org.jdesktop.lg3d.wonderland.shapecell.common.ShapeCellSetup;
import javax.media.j3d.*;
下一步,修改你的Java类定义为:
public class ShapeCellGLO extends StationaryCellGLO implements BeanSetupGLO {
这个类继承了org.jdesktop.lg3d.wonderland.darkstar.server.cell 包中的StationaryCellGLO类,这个类是所有固定单元的基类,它自身继承了CellGLO类。ShapeCellGLO是Darkstar上下文的一个“托管对象”,因为CellGLO类实现了ManagedObject接口。ShapeCellGLO类同时实现了BeanSetupGLO接口,以及它的三个方法:setupCell(),reconfigureCell(), 以及 getCellGLOSetup()。 你将很快实现这三个方法。
ShapeCellGLO类维护了一个可配置的信息:一个描述描绘该形状的字符串:BOX或者SPHERE。你将在ShapeCellGLO里储存这个字符串:
private String shapeType = null;
CellGLO类声明了一个虚函数,getSetupData,你必须实现它。这个方法应该返回一个表示你的单元可配置数据类的一个实例(其自身是CellSetup的一个子类)。在这里,你可以简单地创建和返回ShapeCellSetup的一个新的实例:
@Override
public ShapeCellSetup getSetupData() {
ShapeCellSetup setup = new ShapeCellSetup();
setup.setShapeType(this.shapeType);
return setup;
}
实现BeanSetupGLO类的方法如下:
public void setupCell(CellGLOSetup setupData) {
super.setupCell((BasicCellGLOSetup>) setupData);
BasicCellGLOSetup
this.shapeType = setup.getCellSetup().getShapeType();
/* Setup bounds based upon shape of the object */
if (this.shapeType.compareTo("BOX)") == 0) {
super.setBounds(new BoundingBox());
}
else if (this.shapeType.compareTo("SPHERE") == 0) {
super.setBounds(new BoundingSphere());
}
}
setupCell()方法在ShapeCellGLO类的实例被创建(用于初始化单元的可配置参数)时被调用。这些可配置参数通常是在WFS里的XML单元描述文件设置的——如何配置这个文件将会在稍后讨论。这个方法传递了实现CellGLOSetup接口的一个类,同时也是一个BasicCellGLOSetup类的一个实例。BasicCellGLOSetup类储存了所有单元类型的共同信息——原点,旋转,缩放以及范围。新的单元类型不需担心存储这些可配置的参数——StationaryCellGLO类通过自己的setupCell()方法处理好这些参数。
BasicCellGLOSetup类有一个自己的成员(称为cellSetup),一个将参数储存到你的新的单元类型中的类。你将在稍后定义这个类(ShapeCellSetup)。ShapeCellSetup类存储了形状的类型——你将使用这个值来初始化ShapeCellGLO类里面的shapeType字符串。同时,你还需要使用它来适当地初始化单元的范围。
下一步,实现reconfigureCell() 和 getCellGLOSetup() 方法。reconfigureCell()方法在服务器运行的过程中,WFS里的XML单元描述文件被改变时被调用。我们先不实现这个方法,稍后会回来定义它:
public void reconfigureCell(CellGLOSetup setupData) {
}
getCellGLOSetup() 方法返回了一个新的BasicCellGLOSetup类的对象(由ShapeCellSetup类设置其参数):
public CellGLOSetup getCellGLOSetup() {
return new BasicCellGLOSetup
getOrigin(), getClass().getName(), getSetupData());
}
最后,实现getClientCellClassName() 方法。这个方法返回客户端单元类的全称类名(稍后会实现它):
@Override
public String getClientCellClassName() {
return "org.jdesktop.lg3d.wonderland.shapecell.client.ShapeCell";
}
ShapeCellSetup公共类
下一步,你将实现ShapeCellSetup类,这个类将编译到服务器端和客户端中。这个类遵循Java Bean的模式,简单的作为将可配置信息从服务器传递到客户端的容器。
1、右键单击common包,选择新建-Java类,命名为ShapeCellSetup,点击完成:
package org.jdesktop.lg3d.wonderland.shapecell.common;
import org.jdesktop.lg3d.wonderland.darkstar.common.CellSetup;
public class ShapeCellSetup implements CellSetup {
private String shapeType = null;
public ShapeCellSetup() {}
public String getShapeType() {
return shapeType;
}
public void setShapeType(String shapeType) {
this.shapeType = shapeType;
}
}
这个类以字符串的形式存储形状的类型,提供了一个默认的构造方法以及标准的get和set的方法。如果你希望对单元形状类型的功能进行扩展,例如包含一个半径或者颜色等可配置的参数,你也可以在这个类中包含这些新的参数。
ShapeCell客户端类
最后,你将实现ShapeCell客户端类,这个类负责在世界中描绘形状。
1、右键单击client包,选择新建-Java类,命名为ShapeCell。
实现为:
package org.jdesktop.lg3d.wonderland.shapecell.client;
import javax.media.j3d.*;
import com.sun.j3d.utils.geometry.*;
import javax.vecmath.Matrix4d;
import org.jdesktop.j3d.util.SceneGraphUtil;
import org.jdesktop.lg3d.wonderland.darkstar.client.cell.Cell;
import org.jdesktop.lg3d.wonderland.darkstar.common.*;
import org.jdesktop.lg3d.wonderland.shapecell.common.ShapeCellSetup;
public class ShapeCell extends Cell {
public ShapeCell(CellID cellID, String channelName, Matrix4d origin) {
super(cellID, channelName, origin);
}
@Override
public void setup(CellSetup setupData) {
BranchGroup bg = new BranchGroup();
bg.setCapability(BranchGroup.ALLOW_CHILDREN_EXTEND);
bg.setCapability(BranchGroup.ALLOW_CHILDREN_WRITE);
SceneGraphUtil.setCapabilitiesGraph(bg, false);
if (((ShapeCellSetup)setupData).getShapeType().compareTo("BOX") == 0) {
Box box = new Box();
SceneGraphUtil.setCapabilitiesGraph(box, false);
bg.addChild(box);
}
else if (((ShapeCellSetup)setupData).getShapeType().compareTo("SPHERE") == 0) {
Sphere sphere = new Sphere();
SceneGraphUtil.setCapabilitiesGraph(sphere, false);
bg.addChild(sphere);
}
cellLocal.addChild(bg);
}
}
ShapeCell类继承Cell类,Cell类提供了客户端单元的基本功能。每个子类必须覆盖setup()方法来获取服务器端的配置信息,并且在世界中描绘单元。在这里,CellSetup对象是ShapeCellSetup类的一个实例。使用指定的形状类型,setup()方法描绘了一个盒子或者球(半径为1)。
在世界中创建一个形状单元
要在世界中创建你的新形状单元类型的一个实例,你可以在你世界的WFS中创建一个XML单元描述文件。在这个例程中,你将创建一个只包含这个单元的WFS,然而,你可以随意地放置许多个这个单元到已有的世界中去。
1、编辑lg3d-wonderland目录下的build.properties文件。将wonderland.wfs.root属性设置为${src.dir}/worlds/shape-wfs。
2、在${src.dir}/worlds 目录下(一般是lg3d-wonderand/src/worlds/)中创建shape-wfs 目录。
3、在shape-wfs 目录下,创建一个名为shape-wlc.xml 的文件。
将以下内容拷贝到你的shape-wlc.xml 里并保存:
这个文件根据Java Bean持久化机制(JSR-57, 查看http://java.sun.com/products/jfc/tsc/articles/persistence3/)来格式化。它包含了BasicCellGLOSetup类中所有的单元属性(在这里是其原点),以及在cellGLOClassName 属性中指定了要创建的单元的名称。像之前讨论的那样,BasicCellGLOSetup包含了一个成员,cellSetup,包含了特定的单元类型配置信息。在这里,我们将这个成员描述为一个ShapeCellSetup 类的实例,这个类只有一个属性,shapeType ——用于存储要描绘的形状。
形状的原点被设为(x, y, z) = (50.0, 2.0, 40.0) 一遍让这个球出现在我们的初始位置前。
编译、运行
lg3d-wonderland的一个很好的特性是,它会自动地查找和编译wonderland-modules目录。事实上,为了正确地编译你的模块(以便类路径被正确包含所有Wonderland相关的JAR包),你必须从lg3d-wonderland目录里编译。要编译和运行你的新类型,在命令行工具里运行:
% cd lg3d-wonderland
% ant run-sgs
(注意:在日至文件中,你可能会看见一些警告信息:aliases.xml和version.xml文件丢失。这些警告消息是无害的,这个两个文件的功能有待实现。)
在另外一个命令行工具中,运行:
% cd lg3d-wonderland
% ant run
Wonderland客户端出现并登陆后,你应该可以在你面前看到一个黑色的球:
额外尝试
如果你希望扩展一下这个新的单元类型,你可以使用这个对象的半径或颜色的配置。这些改变会包含在客户端和服务器端的类,ShapeCellSetup 以及相应的XML单元描述文件中。你可能需要添加其他类型的形状的支持,例如立方体,圆锥等,这些基础的类型可以从Java3D 中获得,你可以自由地进行实验。
下一步
在下一个例程中,你将学习如何在运行时处理你的单元类型的可配置参数的改变。在将来的一些例程中,你还会学习如何为你的形状添加材质,改善它的外观等。在使用材质的时候,你还会学习如何与模型库交互。在将来的例程中,你还会学习如何如何接收事件,在很多客户端中管理和更新你的单元的共享状态。
第二部分:动态处
来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/14734416/viewspace-528445/,如需转载,请注明出处,否则将追究法律责任。