`
king_tt
  • 浏览: 2109548 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
社区版块
存档分类
最新评论

jMonkeyEngine译文 FlagRush7(1)——拥抱大地让我们驾驶的不再是Box

 
阅读更多

注:本系列教程全部翻译完之后可能会以PDF的形式发布。

如果有什么错误可以到http://blog.csdn.net/kakashi8841留言或EMAILkakashi9bi@gmail.com给我。

jME版本 jME_2.0.1_Stable

开发工具:MyEclipse8.5

操作系统:Window7/Vista

迄今为止,我们拥有一个带驾驶参数的box。允许我们创建不同性能类型的vehiclebox在地形上表现得很好。我们开始看一些新的。我们开始获得一些玩游戏所必需的基础。所以,让我们花这一节课来让游戏中的事物变得好看点。是时候增加一些炫的啦。我们将让terrain看起来更真实,用一辆酷的未来主义vehicle代替box,而且让这个酷的未来主义vehicle跟随terrain得更好。让我们开始!

7.1、优化

就像之前的向导提到的一样,我们的action正在做一些相同的事,并重复很多代码。此刻,我们为了优化代码将做一些巩固。首先,AccelerateActionBrakeAction被融合进一个单一的类VehicleRotateRightAction。然而VehicleRotateLeftActionVehicleRotateRightAction被组合进VehicleRotateAction

ForwardAndBackActionupdate方法决定了我们想要让vehicle前进的方向,并调用vehicle相应的方法。它不再update translation。我过一会将说下这个。

/**

* 这个action调用vehicle

* accelerate brake 命令去调整它的速度

*

*/

@Override

public void performAction(InputActionEvent evt) {

if( direction == FORWARD){

node.accerate(evt.getTime());

}else if( direction == BACKWARD ){

node.brake(evt.getTime());

}

}

这里的FORWARDBACKWARD是类的常量,而一个int参数被加到构造方法去定义我们想要移动的方向。

相似的,VehicleRotationAction被改为:

/**

* vehicle的转弯速度转弯。

* 如果vehicle正在后退,方向相反。

*/

@Override

public void performAction(InputActionEvent evt) {

//影响方向

if(direction == LEFT) {

modifier = 1;

} else if(direction == RIGHT) {

modifier = -1;

}

//我们想根据我们运动的方向转向不同的方向

if(vehicle.getVelocity() < 0) {

incr.fromAngleNormalAxis(

-modifier * vehicle.getTurnSpeed()

* evt.getTime(),

upAxis

);

} else {

incr.fromAngleNormalAxis(

modifier * vehicle.getTurnSpeed()

* evt.getTime(),

upAxis

);

}

vehicle.getLocalRotation().fromRotationMatrix(

incr.mult(

vehicle.getLocalRotation().toRotationMatrix(tempMa),

tempMb

)

);

vehicle.getLocalRotation().normalize();

}

RIGHTLEFT做为常量被添加,而direction做为构造参数。modifier值定义了我们将转的方向,它是根据direction决定的。

最后,DriftAction被轻微更新了一下。

@Override

public void performAction(InputActionEvent evt) {

vehicle.drift(evt.getTime());

}

正如你看到的,translation代码被移除了。它被移往哪里?注意,我们将讲一下它。

既然action被修改了,我们需要修改一下handler使用它们。

ForwardAndBackwardAction forward =

new ForwardAndBackwardAction(

node,

ForwardAndBackwardAction.FORWARD

);

addAction(forward,"forward",true);

ForwardAndBackwardAction backward =

new ForwardAndBackwardAction(

node,

ForwardAndBackwardAction.BACKWARD

);

addAction(backward,"backward",true);

VehicleRotateAction rotateLeft =

new VehicleRotateAction(

node,

VehicleRotateAction.LEFT

);

addAction(rotateLeft,"turnLeft",true);

VehicleRotateAction rotateRight =

new VehicleRotateAction(

node,

VehicleRotateAction.RIGHT

);

addAction(rotateRight,"turnRight",true);

所以,最后的改变是Vehicle怎样移动它的位置。这个问题之前都是AccelerateAction / BrakeActionDriftAction在应用移位的。这实际上导致vehicle每次移动2次。因此,增加一个update方法给Vehicle并在handlerupdate方法中调用vehicleupdate

public void update(float time){

this.localTranslation.addLocal(

this.localRotation.getRotationColumn(2, tempVa)

.mult(velocity*time)

);

}

vehicle.update(time);

增加到handlerupdate方法。

现在,vehicle移动了它原来速度的一半,所以我将调整vehicle的性能。附加地,我也觉得它转得太快。所以,我也降低了转速。

player.setTurnSpeed(2.5f);

player.setMaxSpeed(25);

player.setMinSpeed(15);

就是那样。所以,让我们用好的素材继续做下去!

7.2Detail Texture(细节纹理)和TextureCombining(纹理组合)

我们将增加的第一个悦目的东西是detail Texture。一个detail Texture通常是由黑色和白色组合而成的图片,从而表现地面的特性。通常,黑色和白色的texture模拟了不光滑地面凹凸物形成的阴影。所以,这个texture被应用于terrain并和原始terraintexture组合。terrain已有的颜色texture和这个detail texture的组合允许颜色texture呈现出比它本身更多的细节。

为了完成这个,我们将使用jMEmultitexturing特性。这包含将第二张texture放入绘图卡/显卡(Graphics Card)的第二Texture单元,并为texture定义参数,指明它们2个是怎样被组合的。

首先,我们加载detail texture(在buildTerrain方法中进行):

//加载细节纹理并为2terraintexture设置组合模型

Texture t2 = TextureManager.loadTexture(

Lesson7.class.getClassLoader()

.getResource("res/Detail.jpg"),

Texture.MinificationFilter.Trilinear,

Texture.MagnificationFilter.Bilinear

);

ts.setTexture(t2, 1);

我们就像我们之前一样使用TextureManager加载一个Detail.jpg图像。我们接着设置它到我们的TextureState做为第二个textureunit 1 记住我们从0开始数)。这告诉TextureState ts去保存2Texturet1t2.

下一步,由于这是一种细节纹理,我们想让它……很好,细节。创建一个高分辨率的texture图像不会有什么影响,由于这只是提供为地面基础texture。因此,我们将用一张相当低的分辨率并让它在地面上重复(repeat)很多次。实际上,detail纹理将在terrain上的一边到另一边repeat16次。为了允许这个repeat,我们需要设置texturewrap模式。

t2.setWrap(Texture.WrapMode.Repeat);

现在,我们需要定义这2texture是怎样被组合的。Texture定义了很多组合模式(combine mode)。首先,我们将设置每个textureApplyMode.Combine,这告诉TextureState,那2Texture的颜色将被Combine进一个输出颜色。第一个texture将是我们的基础texture,所以我们让它用它的颜色(RGB)乘以第二张texture以得到最后颜色。我们这么做,因为第二张texture是黑色和白色。因此,细节全白,我们将得到完整颜色,而当细节完全黑色,输出颜色将是黑色。我们接着告诉texture 1,所有的输入源将从01,而且只适用颜色(RGB),我们不必关心Alpha值。

所以,我们主要的texturet1)看起来将是这样:

t1.setApply(Texture.ApplyMode.Combine);

t1.setCombineFuncRGB(Texture.CombinerFunctionRGB.Modulate);

t1.setCombineSrc0RGB(Texture.CombinerSource.CurrentTexture);

t1.setCombineOp0RGB(Texture.CombinerOperandRGB.SourceColor);

t1.setCombineSrc1RGB(Texture.CombinerSource.PrimaryColor);

t1.setCombineOp1RGB(Texture.CombinerOperandRGB.SourceColor);

接下来,我们需要修改t2去应用它的颜色值给之前的texturet1.一切都和t1一样(定义颜色来自哪里),但我们设置CombineFuncRGBAddSigned,这意味着它将把自己的加到其它的texture单元上,该texture单元在和原始texture相乘之前就存在。

t2.setApply(Texture.ApplyMode.Combine);

t2.setCombineFuncRGB(Texture.CombinerFunctionRGB.AddSigned);

t2.setCombineSrc0RGB(Texture.CombinerSource.CurrentTexture);

t2.setCombineOp0RGB(Texture.CombinerOperandRGB.SourceColor);

t2.setCombineSrc1RGB(Texture.CombinerSource.Previous);

t2.setCombineOp1RGB(Texture.CombinerOperandRGB.SourceColor);

所以,现在已经设置了这2texture,我们的render到屏幕的结果texture单元将是这2个美丽的结合。

然而,我们仍然没定义怎样将texture应用到terrain本身。那就是,纹理坐标(texture coordinate)。我们本应该为TerrainBlock定义texture coordinate,但幸运的是,我们不必这么做。使用setDetailTexture方法,我们能让它为我们完成。所以,调用:

tb.setDetailTexture(1, 16);

这告诉terrain去设置texture单元1 repeat 16.那就是沿着texture的高度和宽度repeat 16次。

现在,当你运行例子,我们的box将飞奔在一个看起来有更多细节的terrain上。

7.3Terrain法向(Normal)和朝向(Orientation

既然我们的terrain看起来更有细节,让我们让box也在地面上跟得更好。box保持它的高度在terrain上以便它能跟着山的高度变化自己的高度,这很好,但是,它看起来却不是很正确,因为它一直保持相同的朝向(Orientation)。

我们所要做的是使用2个工具方法:getSurfaceNormalrotateUpTogetSurfaceNormal方法给我们沿着TerrainBlock上任何点的normal。就像getHeight它将玩家的位置插入height map最近的点并计算那点的normal。这个normal是保存在一个提供的Vector对象中,所以你可以增加:

//保存terrain的任何一个给出点的法向

private Vector3f normal = new Vector3f();

到应用程序的顶部。我们将于每次update循环期间把这个和玩家的位置做为输入传入getSurfaceNormal方法。这将在每帧给我们一个玩家当前位置的normal

现在,我们知道我们正处的terrain的角度,我们想要调整玩家对齐山。调用rotateUpTo允许我们这样做。简单在player上带着我们从terrain获取的normal调用方法,那么我们就做完了。只是简单的处理就为游戏增加了更多的真实性。

//获取terrain在我们当前位置的normal

//我们然后将它应用到player上面。

tb.getSurfaceNormal(

player.getLocalTranslation(),

normal

);

if(normal != null) {

player.rotateUpTo(normal);

}

现在,驾驶着box并看它是怎样跟着terrain的。太激动了!现在,让我们处理掉那个丑陋的box

7.4、加载模型(model

那么,现在我们想真正驾驶一辆cool的未来主义vehicle。很好,就像很多没艺术的人一样,我开始寻找免费的模型。3D Cafe 有一些未来主义的摩托车。它甚至在两边有漂亮的枪。

所以,我们需要做一些事去让这个进入我们的scene

1、 加载.3ds文件(它是3DS MAX的格式,相当标准)

2、 转换为.jmejME二进制格式)

3、 加载二进制.jme格式到scene graph

很明显,每次都将.3ds转为.jme不是必须的。可以优化,我们将在发布游戏之前先这么做,那么以后只需加载.jme。这在下节课的优化将进行更深入的讨论。

加载模型的第一个任务是使用jMEModel Conversion工具。有一些被支持的格式(md2md3milkshape3dsobjase),选择和你的格式对应的Converter。这个converter创建一个.jme二进制文件。转换需要2件事:文件的InputStream,用于保存.jmeOutputStream。所以,我们将创建一个MaxToJme converter和一个ByteArrayOutputStream对象。我们接着创建一个指向我们模型的URL(我们将使用这个URL去获取InputStream)

调用converterconvert方法将使用jme文件的数据填充我们的ByteArrayOutputStream

我们现在拥有能加载进scene graph.jme数据。使用BinaryImporter我们能调用它的load方法去创建一个Node。这个Node将被用于替换vehicle中的box

如果你继续工作你可能会注意到,model有点大,事实上,它太巨大了。手动使用你在网上找到的模型并没有精确为我们的游戏制作。所以,我们需要缩放它到适合我们的terrain。事实上,我们将缩放很多(0.0025%)。

我们现在已经加载了bike(buildPlayer中的box创建部分换成下面)

Node model = null;

try {

MaxToJme C1 = new MaxToJme();

ByteArrayOutputStream BO = new ByteArrayOutputStream();

URL maxFile = Lesson7.class.getClassLoader()

.getResource("res/bike.3ds");

C1.convert(

new BufferedInputStream(maxFile.openStream()),

BO

);

model = (Node)BinaryImporter.getInstance().load(

new ByteArrayInputStream(BO.toByteArray())

);

//缩放它,让它比原来更小

model.setLocalScale(.0025f);

model.setModelBound(new BoundingBox());

model.updateModelBound();

} catch (IOException e) {

e.printStackTrace();

}

最后,用它代替在vehiclebox位置:

player = new Vehicle("Player Node",model);

我们现在有一个coolbike用于驾驶。

另一个轻微的改变是我们将在增加model之后update实体scene。这是因为Model是由许多Node(或scene graph中的分支)组成的。由于这样,这个能影响到实体树BoundingVolumes的合并。所以,我们将调用updateGeometricState去重建scene

scene.updateGeometricState(0, true);

7.5、结论

我们已经确实地为我们的游戏添加了一些视觉上具有吸引力的东西。

terrain看起来更好,我们驾驶一辆真正的交通工具而且它精确紧跟着terrain。下一步,我们将再优化一点:保存一个.jme文件并加载,从而不必每次都转化,让bike在转弯时倾斜和车轮旋转。我们将介绍终点Flag,所以我们将有一个目标!继续观看!

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics