jMonkeyEngine译文 FlagRush8(1)——增加随机的Flag

FlagRush系列教程快完了~~今天正在看第9篇,现在先发第八篇吧。

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

如果有什么错误可以到http://blog.csdn.net/kakashi8841留言或EMAIL[email protected]给我。

jME版本 jME_2.0.1_Stable

开发工具:MyEclipse8.5

操作系统:Window7/Vista

8.1、介绍

在这个向导中,我们将在平面上增加flag。这将给我们自己的“目标”,我们想捕获的对象。为了这么做,我们将讨论Cloth System(织物系统),它是jME中用于render看起来真实的flag。我们也将做一些优化和一些对bike动画的改进,让它看起来更好。

8.2、优化

这节课我们将有几个优化要做。首先,加载一个3DS 模型文件,将它转换为jme格式然后把它加载进scene,这没有多大的意义。我们已经只是加载一个.jme文件。所以,首先,我把转化的文件保存为bike.jme然后改变buildPlayer方法加载这个文件。这将提高一点速度。为了保存为.jme,我简单使用先前的向导并将转化的buffer写入文件。

Node model = null;

URL maxFile = Lesson8.class.getClassLoader()

.getResource("res/bike.jme");

try {

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

maxFile.openStream()

);

model.setModelBound(new BoundingBox());

model.updateModelBound();

model.setLocalScale(0.0025f);

} catch (IOException e1) {

e1.printStackTrace();

}

当驾驶时,我注意到一点问题,那是在我漂移到停止(尤其在低的FrameRate)的时候。这个bike将停止得看起来像往后震动。那是由于drift的算法引起的。我们不应该认为如果frame rate足够高就行,bikedrift超过0。因此,它应该drift到停,然后往后退一点,再往前,等等。所以,我增加检查,如果它超过0,把它设置为0

public void drift(float time) {

if(velocity < -FastMath.FLT_EPSILON) {

velocity += ((weight/5) * time);

//我们将漂移到停止,所以不能超过0

velocity = velocity > 0 ? 0 : velocity;

} else if(velocity > FastMath.FLT_EPSILON){

velocity -= ((weight/5) * time);

//我们将漂移到停止,所以不能低于0

velocity = velocity < 0 ? 0 : velocity;

}

}

我对于chase camera一直跟在bike上面很不满意,所以,我移除从buildChaseCamera方法中移除target offset property。这将让camera低一点,并让它更容易看到平面上的东西。

以上就是这次的优化。下面我们将改进游戏。

8.3、向转弯的方向倾斜

我对现在bike当我们做一个转弯时它的僵硬看起来很不爽。为了让bike看起来更流畅和更像一个真正的bike,我想增加倾斜以便bike能在转弯的时候倾斜。这里需要注意的一个概念是我将倾斜Vehicle包含的模型的node,而不是Vehicle它自己。这是因为Vehicle的责任是保存biketerrain上的方向,这个倾斜只是纯粹的视觉效果。

我们要做的第一件事是为vehicle增加一个方式以便我们告诉它我们是否需要倾斜。在VehicleRotateAction中,我们在performAction方法下面增加以下这一行。

vehicle.setRotateOn(modifier);

这将告诉Vehicle我们将转左或右。而当然,我们需要增加一个方法给Vehicle

public void setRotateOn(int modifier) {

lean = modifier;

}

这将为modifier设置一个新的类变量。Lean包含在一个给出的帧下bike倾斜的方向。

既然我们知道bike是怎样倾斜的,我们需要为bike的倾斜调整方向。如果lean0,我们则不必倾斜。在这个例子中,我们看bikeleanAngle是否是其它的而不是0,如果是,我们将缓慢让bike从右到直。这给出一种效果,做一个转弯,然后把bike带到正确的位置上。如果lean不是0,那么我们根据lean的值调整leanAngle。我们将把它锁定为-11因此bike不会倒下(我们驾驶我们的驾驶员知道怎么骑一个bike)。我们然后根据leanAxisVector3f(0,0,1))设置bike的角度为一个新的leanAngle。将lean设置回0很重要,这样使我们能正确驾驶bike,如果玩家停止转弯。

/**

* processlean将基于一个lean因素调整bike模型的角度。

* 我们调整bike而不是Vehicle

* 因为Vehicle关心的是biketerrain上的位置

* @param time the time between frames

*/

private void processLean(float time) {

//查查我们是否需要倾斜

if(lean != 0) {

if(lean == -1 && leanAngle < 0) {

leanAngle += -lean * 4 * time;

} else if(lean == 1 && leanAngle > 0) {

leanAngle += -lean * 4 * time;

} else {

leanAngle += -lean * 2 * time;

}

//最大倾斜为-11

if(leanAngle > 1) {

leanAngle = 1;

} else if(leanAngle < -1) {

leanAngle = -1;

}

} else { //我们不需要倾斜,让它直立

if(leanAngle < LEAN_BUFFER && leanAngle > -LEAN_BUFFER) {

leanAngle = 0;

}

else if(leanAngle < -FastMath.FLT_EPSILON) {

leanAngle += time * 4;

} else if(leanAngle > FastMath.FLT_EPSILON) {

leanAngle -= time * 4;

} else {

leanAngle = 0;

}

}

q.fromAngleAxis(leanAngle, leanAxis);

model.setLocalRotation(q);

lean = 0;

}

现在,我们需要在每帧调用这个方法去在需要的时候更新bike。所以,update方法被修改为包含一个processLean的调用。

processLean(time);

就像那样,现在我们的bike当转弯的时候倾斜!真正解决了bike的视觉!

jMonkeyEngine译文 FlagRush8(1)——增加随机的Flag

8.4、旋转轮胎

我们想增加的另一个移动的可视提示是轮胎的旋转。这个是微妙的增加,因为车轮没有很多变化去表示它们正在旋转,但是它对于那些细心观察的人是可以被注意到的。

首先,我们需要获取模型部件的参考才能知道哪里是车轮。这个简单要求你对模型结构有一点了解。在bike.jme这个例子中,结构像下面:

jMonkeyEngine译文 FlagRush8(1)——增加随机的Flag

所以,我们需要获取Geometry的后轮(backwheel)和前轮(frontwheel)。我偶然知道这些是索引为15的孩子。实际上我通过名字获取。

//获取前轮和后轮的引用

backwheel = ((Node)model).getChild("backwheel");

frontwheel = ((Node)model).getChild("frontwheel");

下一步,在Vehicle中,我们将增加一个方法处理这些轮子的旋转。首先,我们通过调用一个vehicleIsMoving决定Vehicle是否正在移动。如果是,我们通过1/2速度修改轮子的旋转角度(由时间缩放)。

private void rotateWheels(float time){

//vehicle移动的时候旋转轮子

if(vehicleIsMoving()){

//前进

if(velocity > FastMath.FLT_EPSILON){

angle = angle - ((time) * velocity * 0.5f);

if (angle < -360) {

angle += 360;

}

}else{

angle = angle + ((time) * velocity * .5f);

if(angle > 360){

angle -= 360;

}

}

rotQuat.fromAngleAxis(angle, wheelAxis);

frontwheel.getLocalRotation().multLocal(rotQuat);

backwheel.setLocalRotation(frontwheel.getLocalRotation());

}

}

正如上面提到的,vehicleIsMoving只是决定了velocity是否足够接近0到应该停止。

/**

* 用于判断vehicle是否移动的便利方法。

* 当速度velocity接近0时为真。

* @return true vehicle正在移动,否则为false

*/

private boolean vehicleIsMoving() {

return velocity > FastMath.FLT_EPSILON ||

velocity < -FastMath.FLT_EPSILON;

}

我们然后简单在在update中调用vehicle的新方法rotateWheels(time);

这时候你运行程序,会发现vehicle行驶的时候“颠簸”得很厉害,这可能是作者的疏忽。后面经过本人对比,发现作者对程序Lesson8.java做了以下修改。

1、 增加了类变量private float agl;

2、 private void buildPlayer() 中增加:

agl = ((BoundingBox)(player.getWorldBound())).yExtent;

3、将

protected void update(float interpolation)中的:

float characterMinHeight =

tb.getHeight(player.getLocalTranslation()) +

((BoundingBox)player.getWorldBound()).yExtent;

改为:

float characterMinHeight =

tb.getHeight(player.getLocalTranslation()) + agl;

OK了,现在平滑地运行吧。在享受高质量游戏的同时想想为什么?

8.5、增加一个Flag对象

现在,我们将增加一面旗。如果你记起这个游戏的目的是快速收集旗帜。很好,这讲的将是那旗帜。flag的工作是处理它自己的timer和在terrain上随机的位置。

所以,我们将创建一个新的Flag类从Node继承。

import com.jme.math.FastMath;

import com.jme.math.Quaternion;

import com.jme.math.Vector3f;

import com.jme.renderer.Renderer;

import com.jme.scene.Node;

import com.jme.scene.shape.Cylinder;

import com.jmex.terrain.TerrainBlock;

/**

* Flag保存了游戏中的“goal”。驾驶员将为了分数尝试获取Flag

* 这个类的主要工作是创建flag geometry

* 还有在一段时间后在平面内随机移动自己的位置

* @author Mark Powell

*

*/

public class Flag extends Node {

//生命10

private static final int LIFT_TIME = 10;

//从满生命开始

private float countdown = LIFT_TIME;

//指向用于放置的地形

private TerrainBlock tb;

public Flag(TerrainBlock tb){

super("flag");

this.tb = tb;

//创建一个旗杆

Cylinder c = new Cylinder(

"pole", 10, 10,

0.5f, 50

);

this.attachChild(c);

Quaternion q = new Quaternion();

//选择旗杆为垂直

q.fromAngleAxis(FastMath.PI/2, new Vector3f(1,0,0));

c.setLocalRotation(q);

c.setLocalTranslation(new Vector3f(-12.5f,-12.5f,0));

this.setRenderQueueMode(Renderer.QUEUE_OPAQUE);

this.setLocalScale(0.25f);

}

/**

* update期间,我们减少time

* 当它到了0,我们重设flag

* @param time 间隔2帧的时间

*/

public void update(float time){

countdown-=time;

if(countdown<=0){

reset();

}

}

/**

* 设置生命时间为10

* 然后随机在terrain上放置flag

*/

private void reset() {

countdown = LIFT_TIME;

placeFlag();

}

/**

* terrain上选择一个随机点并在那放置flag

* 设置值在(45175)之间,这在force平面里面

*/

public void placeFlag() {

float x = 45 + FastMath.nextRandomFloat()*130;

float z = 45 + FastMath.nextRandomFloat()*130;

float y = tb.getHeight(x, z)+7.5f;

localTranslation.x = x;

localTranslation.y = y;

localTranslation.z = z;

}

}

这相当直观。它获取了我们TerrainBlock类的引用,以便旗能放在平面上。它包含一个update方法去处理减少时间,计算它什么时候需要重设自己的位置。而那是通过placeFlag方法来完成的。你将注意平面上的flag的随机值将是0130+4545-175。这个数字是我决定将flag放在fence里面并使用大部分的区域。

我们使用一个简单的cylinder作为旗杆。

现在,为了真正使用这个Flag,我们修改Lesson8去创建Flagupdate它。首先增加一个新的方法buildFlag

/**

* 我们创建一个新的Flag类,

* 因此我们将使用这个把Flag加到世界中。

* 这个flag是我们相要获取的那个。

*/

private void buildFlag() {

flag = new Flag(tb);

scene.attachChild(flag);

flag.placeFlag();

}

这将创建新的Flag,将它attachscene并随机设置位置。

接下来我们将在initGame中加入对buildFlag的调用,在buildTerrain下调用。我们在buildTerrain后调用是因为我们使用terrain去放置Flag

我们也需要update flag去更新countdown。因此在Lesson8update方法中增加flagupdate方法:

flag.update(interpolation);

通过那样,我们现在将有一个旗杆随机放置在terrain上,然后每10秒移动一次。然而,看着旗杆实在没趣,所以让我们增加旗帜。

你可能感兴趣的:(Engine)