上一篇:Java中正则表达式使用方法详解 >>
用Java开发3D游戏之创建浮动的球体
一、 地板
地板是由瓷砖(用我的ColouredTiles类创建)和轴标签(用Java 3D中的Text2D工具类构建)组成的。图5显示了地板分支子图,以前隐藏在图3中的一个"Floor Branch"方框中。
|
图5.场景图的地板分支子图 |
这个地板子图是用我的CheckerFloor类的一个实例构建的,它可以通过调用getBG()方法来使用:
| sceneBG.addChild(new CheckerFloor().getBG());//添加地板 |
这个CheckerFloor()构造器使用嵌套的循环来初始化两个ArrayLists。blueCoords列表包含蓝色瓷砖相关的所有坐标,而greenCoords包含绿色瓷砖相关的坐标。一旦填充完ArrayLists,连同用于生成瓷砖的颜色被一起传递到ColouredTiles对象中。ColouredTiles对象是一个Shape3D的子类,因此可以被直接添加到地板图中:
| floorBG.addChild( new ColouredTiles(blueCoords, blue) ); floorBG.addChild( new ColouredTiles(greenCoords, green) ); |
原点处的红色方格(见于图1)是用相似的方式构建的:
| Point3f p1 = new Point3f(-0.25f, 0.01f, 0.25f); Point3f p2 = new Point3f(0.25f, 0.01f, 0.25f); Point3f p3 = new Point3f(0.25f, 0.01f, -0.25f); Point3f p4 = new Point3f(-0.25f, 0.01f, -0.25f); ArrayList oCoords = new ArrayList( ); oCoords.add(p1); oCoords.add(p2); oCoords.add(p3); oCoords.add(p4); floorBG.addChild( new ColouredTiles(oCoords, medRed) ); |
该方格的中心在XZ平面的(0,0)处并且在y轴稍微向上(+0.01单位)的地方,这样就可以在瓷砖上看到它。
该方格的每一边的长度为0.5单位。在ArrayList中的四个Point3f点以逆时针方向存储。对于在blueCoords和greenCoords中的每一组的四个点都是如此。图6显示出方格中的点的顺序。
|
图6.从上方看上去的OrigMarker |
二、 着色的瓷砖
我的ColouredTiles类扩展了Shape3D并用相同的颜色定义瓷砖的几何体和外观。该几何体使用一个Java 3D QuadArray来描述瓷砖为一系列的四边形。其构造器是:
| QuadArray(int vertexCount, int vertexFormat); |
vertexFormat是一个静态整数集合-它指定稍后被初始化的该四边形的各种信息如坐标、颜色和法线。在ColouredTiles中,QuadArray平面是用下面一行代码创建的:
| plane=new QuadArray(coords.size(),GeometryArray.COORDINATES|GeometryArray.COLOR_3); |
size()方法返回在ArrayList中的坐标数目。坐标和颜色数据是在createGeometry()中提供的:
| int numPoints=coords.size(); Point3f[] points=new Point3f[numPoints]; coords.toArray(points);//ArrayList->数组 plane.setCoordinates(0,points); Color3f cols[]=new Color3f[numPoints]; for(int i=0;i<numPoints;i++) cols[i]=col; plane.setColors(0,cols); |
一个四边形的坐标顺序的正确指定是极为重要的。一个多边形的前面是指顶点形成一个逆时针方向环时的那一面。区别开前面与后面对于光线和隐藏面的选择是非常重要的;并且默认情况下,在一个场景中只有多边形的前面是可见的。在这个应用程序中,瓷砖是有向的-它们的前面朝向y轴。
必须确保一个凸的平面多边形的每个四边形中的顶点可以是折衷的(compromised)。然而,在坐标数组中的每个四边形不需要连接到邻近其它的四边形-正好适于表达瓷砖。既然一个四边形的几何体不包括法线信息,那么一个Material结点组件不可能被用来指定该四边形的在照亮时的颜色。我可以使用一个ColoringAttributes,但是第三种选择是设置几何体中的颜色,例如使用语句"plane.setColors(0,cols);"。这个颜色将固定不变-不受场景中灯光的影响。
一旦完成,用下列语句设置Shape3D的几何体:
| setGeometry(plane); |
形状的外观是由createAppearance()管理的,这个方法使用一个Java 3D PolygonAttribute组件来隐藏后面的显示。PolygonAttribute可能被用于以点或线形式生成多边形(也就是以线框架形式),并且用于翻转后面形状的法线:
| Appearance app=new Appearance(); PolygonAttributes pa=new PolygonAttributes(); pa.setCullFace(PolygonAttributes.CULL_NONE); app.setPolygonAttributes(pa); |
一旦外观全部指定,它就被用下列语句固定在形状中:
| setAppearance(app); |
三、 地板的轴标签
地板的轴标签是在CheckerFloor()中用labelAxes()和makeText()方法生成的。labelAxes()使用两个循环来沿着x和z轴创建标签。每个标签是由makeText()构建的,然后被添加到地板的BranchGroup(见图5):
| floorBG.addChild(makeText(pt,""+i)); |
makeText()使用Text2D工具类创建一个2D串来指定颜色、字体、点大小及字体风格:
| Text2D message=new Text2D(text,white,"SansSerif",36,Font.BOLD); //36点粗体的Sans Serif |
Text2D对象是一个具有一个四边形几何体(一矩形)的Shape3D对象,并且外观是由一个字符串表达的材质贴图(图像)来确定的-贴图被放在前面。默认地,后面是隐藏的;如果用户移动到轴标签的后面,那么对象成为不可见的。
点大小被转换成虚拟世界单位-方法把其是与256相除。通常,在Text2D()构造器使用太大的点是一种糟糕的主意,因为这有可能导致文字的不正确着色。我建议把一个TransformGroup放置在形状上方并且把它缩放到必要的大小。
每个标签的放置是由在形状上方的一个TransformGroup来实现的:
| TransformGroup tg=new TransformGroup( ); Transform3D t3d=new Transform3D(); t3d.setTranslation(vertex);//标签的位置 tg.setTransform(t3d); tg.addChild(message); |
setTranslation()仅仅影响该形状的位置。TransformGroup tg被添加到地板场景图。
四、 观察者位置
图3中的场景图并不包括视图分支图;该分支图显示在图7中。
|
图7.视图分支图 |
这个分支是通过在WrapCheckers3D()构造器中调用SimpleUniverse构造器构建的:
| su=new SimpleUniverse(canvas3D); |
SimpleUniverse提供到视图分支图的简化存取-这是经由ViewingPlatform和Viewer类实现的,而且这两个类被映射到图上(在图7中显示为用点画线绘制的矩形)。
ViewingPlatform被用在initUserPosition()中以存取ViewPlatform结点上方的TransformGroup:
| ViewingPlatform vp=su.getViewingPlatform(); TransformGroup steerTG=vp.getViewPlatformTransform(); |
steerTG相应于图7中的TG结点。它的Transform3D组件分别用lookAt()和invert()方法加以提取和改变:
| Transform3D t3d=new Transform3D(); steerTG.getTransform(t3d); t3d.lookAt( USERPOSN,new Point3d(0,0,0),new Vector3d(0,1,0)); t3d.invert(); steerTG.setTransform(t3d); |
lookAt()是在虚拟的世界中设置观察者位置的一种方便的方法。这个方法需要观察者的所在位置、他的观察点和一个指定向上方向的矢量。在这个应用程序中,观察者的位置是USERPOSN((0,5,20)坐标);他向着原点(0,0,0)观看,并且沿着正向y轴向上看。这可以由图8展示。
|
图8.lookAt()的图形描述 |
既然位置是相对于观察者而不是相对于场景中的一个对象,所以必须调用invert()。
五、 观察者移动
用户能够通过场景移动-利用视图图中的Java 3D OrbitBehavior工具类实现。观察者的位置是通过控制键和鼠标按钮结合方式完成移动和旋转的。
行为是在WrapCheckers3D中的orbitControls()方法中建立的:
| OrbitBehavior orbit = new OrbitBehavior(c, OrbitBehavior.REVERSE_ALL); orbit.setSchedulingBounds(bounds); ViewingPlatform vp = su.getViewingPlatform( ); vp.setViewPlatformBehavior(orbit); |
REVERSE_ALL标志保证观察点的移动沿着与鼠标一样的方向。
提示: 还有其它许多标志和方法影响旋转、平移和缩放特性,详见OrbitBehavior类文档的有关解释。
MouseRotate、MouseTranslate和MouseZoom是一些经常出现在许多Java 3D例子中的相类似的行为类;它们与OrbitBehavior的主要的差别是它们影响场景中的对象而不是影响观察者。
提示: 大多数游戏,例如第一人称射手(FPS),要求较强地控制观察者的移动,甚至超出这些工具行为能提供的功能;因此我将在后面实现我自己的行为。
六、 观看场景图
本文中已经使用场景图来展示所讨论的编码技术,而场景图是一个理解(和检查)代码的相当有用的方法。
我使用Daniel Selman的Java3dTree包来帮助我实现绘图。它创建一个Jframe-它用一个文本树来描述场景图(图9)。
|
图9.Checkers3D场景图的Java3dTree描述 |
该树(一个JTree对象)在开始时是最小化的,并且可以通过点击子文件夹图标来检查分支。当前选择结点信息出现在底部窗口中。该包包含在j3dtree.jar中,它是从http://www.manning.com/selman/下载的源代码(Selman的"Java 3D编程文本")的一部分。
扩充代码来生成JTree是简单的。为了显示JFrame树,WrapCheckers3D必须导入j3dtree包并且声明一个全局变量:
| import com.sun.j3d.utils.behaviors.vp.*; private Java3dTree j3dTree; |
由WrapCheckers3D()构造器创建j3dTree对象:
| public WrapCheckers3D(){ //另外的代码 su = new SimpleUniverse(canvas3D); j3dTree = new Java3dTree( );//为SG创建一个显示树 createSceneGraph( ); initUserPosition( ); orbitControls(canvas3D); su.addBranchGraph( sceneBG ); j3dTree.updateNodes( su );//构建树显示窗口 } |
在完成场景图之后(也就是,在构造器的最后),该树的显示是用一行代码实现的:
| j3dTree.updateNodes( su); |
然而,在这之前,必须调整场景图结点的能力:
| j3dTree.recursiveApplyCapability(sceneBG); |
应该在完成内容分支组(sceneBG)之后而编译或使其成为现场的之前执行这个操作。在我的代码中,这意味着在createSceneGraph()中添加一行代码:
| private void ceateSceneGraph(){ sceneBG = new BranchGroup(); //创建场景的其它代码 j3dTree.recursiveApplyCapability( sceneBG ); sceneBG.compile( ); } |
不幸的是,你不能仅调用:
| j3dTree.recursiveApplyCapability(su); |
在此,没有产生错误-因为SimpleUniverse()构造器已经使得ViewingPlatform成为现场的,它可以防止进一步改变它的能力。
既然只有内容分支的能力得到调整,那么当遇到在Locale结点下的视图分支时,对updateNodes()的调用将生成一些警告消息。
注意 在编译和执行中必须把j3dtree.jar包括在classpath中。我比较喜欢的方式是用命令行参数来实现:
| javac -classpath "%CLASSPATH%;j3dtree.jar" *.java java -cp "%CLASSPATH%;j3dtree.jar" Checkers3D |
提示: 如果重复地输入classpath不适合你,象上面这样的命令行可以被隐藏在批文件或外壳脚本内部。
Java3dTree对象是一个文本方式的场景描述,这意味着我们必须自己绘制场景图。但其优点是树的生成对于程序的其它部分所产生的影响可以忽略。
另一种方法是使用Java 3D场景图编辑器(http://java3d.netbeans.org/j3deditor_intro.html)。这里显示场景图的一个图形化的版本但是也有其负面-其安装和用法是复杂的并且内存要求对于一些机器来说可能很苛刻。
下一篇:深入浅出Java设计模式之状态模式 >>
相关文章:
- · 现代Java Web开发架构分析
- · 用Java开源项目JOONE实现人工智能编程
- · ASP.NET 2.0移动开发入门之使用模拟器
- · 在ASP.NET中自动给URL加上超链接
- · ASP.NET Atlas对JavaScript的扩展
- · ASP.NET Atlas简单控件介绍之两个基类
- · ASP.NET Atlas简单控件介绍之四大控件
- · ASP.NET页面中标题单点解决方案
- · ASP.NET2.0导航功能之配置会员和角色
- · 在ASP.NET程序中实现语音合成
- · ASP.NET入门随想之吸星大法
- · 抢先试用ASP.NET 2.0中的新型安全控件
- · ASP.NET入门随想六之大航海家
- · ASP.NET2.0应用中定制安全凭证之理论篇
- · ASP.NET2.0应用中定制安全凭证之实践篇
- · ASP.NET入门随想之抽象的力量
- · 一道Google中国挑战赛竞赛题的解法
- · ASP.NET入门随想之开卷有益
- · ASP.NET入门随想之瘦子与胖子的故事
- · .NET 2.0远程传输数据集的优化方法
- · 英特尔:对驱动程序软件漏洞没必要担忧
- · 苹果发布Mac OS升级版 修复60个软件瑕疵
- · 边走边看 手机应用软件开发平台介绍
- · Windows Mobile 5.0最新体验
- · 解读VC++编程中的文件操作API和CFile类
- · 利用VC++实现局域网实时视频传输
- · 使用VC6.0实现窗口的任意分割
- · 抢占网络工具焦点阵地 几大浏览器纷纷变脸
- · 双缓冲技术及其在VC的GDI环境下的实现
- · VC++实现动画弹出/弹入式窗口
- · VC++中实现以复杂线条为基础的图形绘图
- · VC++实现工具栏上添加平面组合框控件
- · C++箴言:考虑支持不抛异常的swap
- · Visual C++实现对计算机远程监控
- · VC与Matlab接口编程之Matcom安装配置
- · VC++中使用图形程序设计和动画技术
- · VC环境下三菱PLC与微机的串行通信
- · 在MFC下如何定义全局变量和全局函数
