上一篇:J2EE中使用Display标记库来展示表格 >>
用Java开发3D游戏之创建场景
图1中左边的快照显示出程序起始的视图;右边的显示出用户移动一点后的场景图。
|
图1.起始和后来的视图 |
Checkers3D游戏展示了Java 3D编程中许多共同之处及一些技巧。例如,3D场景的显示是使用Java 3D Canvas3D类完成的-这个类必须与Java的Swing组件集成到一起。所有的Java 3D应用程序要求一个场景图,而Checkers3D展示了怎样添加基本的形状、光源(环境光和有向光)和背景。该场景图用作文档的一种可视化形式,并且借助于Daniel Selman的Java3dTree包可以容易地生成其信息的一种文本版本。
地板和球利用了Java 3D的QuadArray、Text2D和球体几何体类。地板是在一个QuadArray中的一系列的四边形;而标签是利用Text2D对象沿着地板的主轴放置的。球体的实现将向用户展示怎样着色,点亮和放置一个3D形状。用户从一种视图来观察该3D世界。你将看到如何在初始化过程中确定球体的位置,以及如何在执行期间通过使用Java 3D的OrbitBehavior类来移动该球体的。
一、 Checkers3D的类图
图2中的类图显示该Checkers3D应用程序的所有的公共和私有数据及方法。
|
图2.Checkers3D的类图 |
Checkers3D是该应用程序最顶层的JFrame。WrapCheckers3D是一拥有场景图的JPanel,该场景图可经由一Canvas3D对象来观看。 CheckerFloor创建地板子图(如瓷砖,轴,等等),这里相同颜色的瓷砖是用一个ColoredTiles对象描述的。
二、 集成Java 3D和Swing
Checkers3D是一个JFrame-如果必要的话,可以把GUI控件,例如Swing文本域和按钮等放置到它上面。在本文的实例中,我创建了一个WrapCheckers3D(一个JPanel)的实例并把它放到一个BorderLayout的中央:
| c.setLayout( new BorderLayout( ) ); WrapCheckers3D w3d = new WrapCheckers3D( );//3D画布的面板 c.add(w3d, BorderLayout.CENTER); |
在该场景上的Canvas3D视图是在WrapCheckers3D中创建的:
| public WrapCheckers3D( ){ setLayout( new BorderLayout( ) ); //另外的初始化代码 GraphicsConfiguration config =SimpleUniverse.getPreferredConfiguration( ); Canvas3D canvas3D = new Canvas3D(config); add("Center", canvas3D); //另外的初始化代码 } |
当使用Canvas3D时必须小心,因为它是一个轻量级的GUI元素(在一个OS生成的窗口之上的薄层)。重量级的组件无法容易地与轻量级的Swing控件相结合;这些控件大部分由Java生成。如果把Canvas3D对象嵌入到Jpanel中就可以避免这些问题;那么该面板就可以安全地与基于Swing构建的应用程序的其它部分集成到一起。
提示 在j3d.org(http://www.j3d.org/tutorials/quick_fix/swing.html)上有关于把Canvas3D和Swing相结合的详细讨论。
与前面的章节中的应用程序相比,这里没有更新/绘制动画循环。这是不必要的,因为Java 3D包含它自己的机制来监视场景变化并且初始化着色。下面是该算法的伪码形式:
| while(true){ 处理用户输入; if (存在请求) break; 执行行为; if (场景图发生变化) 遍历场景图并着色; } |
行为是一些场景图结点。它们包含能够影响图中其它部分的代码,例如移动形状或改变灯光。它们可以用于监控图形,从而把细节信息传递到应用程序中的非3D部分。
有关细节可能要比这个伪代码中所建议的更为复杂,例如,Java 3D使用多线程来执行并行遍历和着色。然而,了解一下这个过程的大致思想将有助于你理解本文后面的代码。
三、 创建场景图
这个场景图是通过WrapCheckers3D的构造器创建的:
| public WrapCheckers3D( ){ //初始化代码 GraphicsConfiguration config =SimpleUniverse.getPreferredConfiguration( ); Canvas3D canvas3D = new Canvas3D(config); add("Center", canvas3D); canvas3D.setFocusable(true); //聚焦画布 canvas3D.requestFocus( ); su = new SimpleUniverse(canvas3D); createSceneGraph( ); initUserPosition( ); //设置用户的观察点 orbitControls(canvas3D); //控制移动观察点 su.addBranchGraph( sceneBG ); } |
该Canvas3D对象被从getPreferredConfiguration()中得到的配置初始化;这个方法查询有关硬件的着色信息。一些老式的Java 3D程序并不初始化一个GraphicsConfiguration对象,它们使用null作为到Canvas3D构造器的参数。这是一种不好的编程方法。
聚焦于canvas3D会使得键盘输入事件被发送到场景图中的行为中。行为经常是通过键的按下与释放来激活的,但是它们也可以由定时器、帧变化和由Java 3D内部生成的事件来触发。在Checkers3D中不存在任何行为,所以没有必要聚焦。我把这相应的几行代码保留下来,因为它们在我们后面将要讨论的几乎每种其它程序中都要使用。
SimpleUniverse对象创建一标准视图分支图和场景图的VirtualUniverse及Locale结点。createSceneGraph()方法设置灯光、天空背景、地板及浮动的球体;initUserPosition()和orbitControls()负责处理观察者问题。在该方法的最后,结果BranchGroup被添加到该场景图上:
| private void createSceneGraph( ){ sceneBG = new BranchGroup(); bounds = new BoundingSphere(new Point3d(0,0,0), BOUNDSIZE); lightScene( ); //添加灯 addBackground( ); //添加天空 sceneBG.addChild( new CheckerFloor( ).getBG( ) );//添加地板 floatingSphere( ); //添加浮动的球体 sceneBG.compile( ); //修改场景 } //createSceneGraph()结束 |
各种方法把子图添加到sceneBG上以构建内容分支图。一旦该图被终结化并允许Java 3D对它进行优化,sceneBG就被编译。这种优化包含生成图、重分组和组合结点。例如,一串包含不同平移的TransformGroup结点可能组合到单个的结点中。另一种可能是把所有的形状用相同的外观属性分组,这样它们可以更快地着色。
边界是一个全局的BoundingSphere,用来指定对于灯光、背景和OrbitBehavior对象等环境结点的影响。边界球体放置在场景的中央并影响BOUNDSIZE个单位半径内的一切。边界盒和边界多面体在Java 3D中都是可用的。
在WrapCheckers3D( )执行最后的场景图显示在图3中。
其中的"Floor Branch"结点是我的发明,用来隐藏一些细节直到最后。图3中没有显示的是场景图的视图分支部分。
四、 点亮场景
一个环境灯光和两个有向灯光被通过lightScene()方法添加到该场景上。一个环境灯光可以达到世界中的每个角落并同等程度地照亮一切。
| Color3f white = new Color3f(1.0f, 1.0f, 1.0f); //设置环境灯光 AmbientLight ambientLightNode = new AmbientLight(white); ambientLightNode.setInfluencingBounds(bounds); sceneBG.addChild(ambientLightNode); |
这里环境光源是沿着边界创建的并且被添加到场景中。Color3f()函数中使用了红/绿/蓝色,范围为0.0f~1.0f。
有向灯光模仿了具有一定距离的灯光的效果,从一个特定方向来照亮物体的表面。其与环境光的主要区别在于,它需要一个有关的矢量。
| Vector3f light1Direction=new Vector3f(-1.0f,-1.0f,-1.0f); //左面,下面,后面 DirectionalLight light1 = new DirectionalLight(white, light1Direction); light1.setInfluencingBounds(bounds); sceneBG.addChild(light1); |
|
图3.Checkers3D中的部分场景图 |
方向是连接点(0,0,0)和点(-1,-1,-1)的矢量;而灯光可以被想象成是方向与该矢量一致的多条平等线。
点光源和斑点光源是其它形式的Java 3D光。点光源位置在空间上,其方向朝各个方向发出。斑点光源是聚集的点光源,它指向一个特定方向。
一个场景的背景可以用一个固定的颜色(如下)、一静态的图像或一个纹理贴图几何体例如一个球体来指定:
| Background back = new Background( ); back.setApplicationBounds( bounds ); back.setColor(0.17f, 0.65f, 0.92f); //天空颜色 sceneBG.addChild( back ); |
五、 浮动的球体
球体是一个工具类,来自于Java 3D的com.sun.j3d.utils.geometry包,这是一个Primitive类的子类,而Primitive类是一个Group结点-它有一个Shape3D子结点(见图3)。它的几何体在一个Java 3D TriangleStripArray中相邻-它指定球体是一个相连接的三角形的数据。我不必调整这个几何体,但是该球体的外观和位置确实需要改变。
Appearance结点是一个包含大量参考信息-包括色彩、线、点、多边形、着色、透明度和材质属性-的容器。
ColouringAttributes修正一个形状的颜色并且不受场景灯光的影响。对于一个要求颜色和光相交互的形状来说,需要使用Material组件。要使光影响一个形状的颜色,必须满足三个条件:
·该形状的几何体必须包括法线。
·该形状的Appearance结点必须有一个Material组件。
·该Material组件必须用setLightingEnable()启动了灯光效果。
工具球体类能自动地创建法线,因此第一个条件很容易满足。
六、 给球体加上颜色
Java 3D Material组件控制一个形状当被不同的灯光点亮时展示什么颜色:
| Material mat = new Material(ambientColor, emissiveColor, diffuseColor, specularColor, shininess); |
ambientColor参数指定当被环境光点亮时形状的颜色:这使得对象具有一个统一的颜色。emissiveColor代表形状产生的颜色;这个参数经常被置为黑色(等于off)。diffuseColor是对象点亮时的颜色,其亮度依赖于光柱与形状的表面形成的角度的大小。
提示: 散射和环境颜色常被设置为相同色,这与真实世界中的大多数物体被颜色点亮时的方式相匹配。
specularColor参数与形状与它的发光区的反射程度相关。这个值与亮度参数结合在一起。
提示: 镜面光的颜色常被设置为白色,这与真实世界中的由大多数物体生成的镜面光的颜色相匹配。
在Checkers3D中,有两个有向光源-它们在浮动球体的顶部创建两个闪亮的光环(见图1)。地板瓦还没有点亮,因为它们的颜色是用形状的几何体来设置的(见后面)。
在floatingSphere()中管理球体的外观的代码如下:
| Color3f black = new Color3f(0.0f, 0.0f, 0.0f); Color3f blue = new Color3f(0.3f, 0.3f, 0.8f); Color3f specular = new Color3f(0.9f, 0.9f, 0.9f); //近乎白色 Material blueMat= new Material(blue, black, blue, specular, 25.0f); blueMat.setLightingEnable(true); Appearance blueApp = new Appearance( ); blueApp.setMaterial(blueMat); |
七、 放置球体
放置一个形状几乎总是一直通过把它的场景图结点放到一个TransformGroup(见图3中的球体Group)的下方来实现的。可以用一个TransformGroup来放置、旋转和缩放放在它下面的结点,这里变换是用Java 3D Transform3D对象来定义的:
| Transform3D t3d = new Transform3D(); t3d.set( new Vector3f(0,4,0)); //放在(0,4,0) TransformGroup tg = new TransformGroup(t3d); tg.addChild(new Sphere(2.0f, blueApp)); //设置球体的半径和外观 //并缺省地设置其法线 sceneBG.addChild(tg); |
这个set()方法把球体的中心放在(0,4,0)并且重置任何以前的旋转或缩放。set()可以用来在重置其它变换的同时实现缩放和旋转。方法setTranslation(),setScale()和setRotation()仅影响给定的变换。
不象其它一些3D绘图包,Java 3D中的y轴在垂直方向上,而地面是由XZ平面定义的,如图4所示。
在Checkers3D中球体的位置被设置为(0,4,0),这把它的中心放置到XZ平面上方4个单位的位置。
|
图4.在Java 3D中的轴向 |
下一篇:JMX+J2SE5.0实现Web应用的安全管理 >>
相关文章:
- · 谈谈J2SE中的序列化之一个感性的认识
- · 利用Java 3D技术播放动画之QTJ技术
- · “2005Java中国开发者大会”即将召开
- · 现代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安装配置
