欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  数据库

jMonkeyEngine译文 FlagRush5(2)跟随的摄像机(ChaseCamera

程序员文章站 2022-05-30 23:45:52
...

5.4 、让我们增加玩家 对于这个向导,我们将只是使用一个占位符代替交通工具。我们将在之后载入模型,但那只是没价值的工作,我们想要先让游戏的核心能运作。一个 Box 是一个好的占位符,因为它是我们交通工具的基础模型。 所以,让我们先增加一个 buildPlay

5.4、让我们增加玩家

对于这个向导,我们将只是使用一个占位符代替交通工具。我们将在之后载入模型,但那只是没价值的工作,我们想要先让游戏的核心能运作。一个Box是一个好的占位符,因为它是我们交通工具的基础模型。

所以,让我们先增加一个buildPlayer的方法并在initGame中调用它。我们将接着创建一个box做为玩家的几何体并把这个Box attachnode。这个玩家Node将会是一个类变量,以便我们能在update期间访问它。我将创建一个中心为(0,0,0)和大小为(0.35,0.25,0.5),让它看起来长和宽。Node接着被移到坐标(100,0,100)。我还没设置它的高度,我将在之后才那么做。

privatevoid buildPlayer() {

//box 代替

Box b = new Box("box", new Vector3f(), 0.35f,0.25f,0.5f);

b.setModelBound(new BoundingBox());

b.updateModelBound();

player = new Node("Player Node");

player.setLocalTranslation(new Vector3f(100,0, 100));

scene.attachChild(player);

player.attachChild(b);

player.updateWorldBound();

}

如果你现在运行这个,实际上不会看到player,因为它深陷在terrain下面。我们在update里面设置height,这是因为我们将很快让交通工具在平面上移动,并需要让它保持在terrain上。所以,为了保持box在地面的顶部行驶,增加:

//确保当玩家离开平面时我们不会坠落。

//当我们增加冲突时,fence将做它自己的工作并保持玩家在里面。

float characterMinHeight =

tb.getHeight(player.getLocalTranslation()) +

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

if(

!Float.isInfinite(characterMinHeight) &&

!Float.isNaN(characterMinHeight)

)

player.getLocalTranslation().y = characterMinHeight;

首先,我们获取玩家当前位置对应的terrain的高度。接着加上BoundingBix的偏移,我们这么做是因为Box位置的点是Box的中心。如果我们没加上偏移,box将有一半沉入地下。我们使用包围对象的BoundingBox去获取对象的高度(yExtent)(但实际上如果你对模型了解得很好,你可以使用值代替)。最后,我们检查获取的高度去确认没有得到一些糟糕的值(非数字、无穷大等)。我们这么做是因为目前我们没做任何事去阻止玩家驾驶出terrain

我们现在已经在terrain上拥有了玩家!

jMonkeyEngine译文 FlagRush5(2)跟随的摄像机(ChaseCamera

(现在你可能还看不到这个画面,别急,后面会看到的)

5.5、跟随摄像机(ChaseCamera

好了,现在我们有了玩家,我们应该有能力移动它。这将是一个第三人称游戏,意味我们在玩游戏的时候能看到自己的玩家(而不是以player的眼睛去看)。所以我们想要摄像头一直指向玩家并跟随他。为了这么做,我们将使用ChaseCameraChaseCamera将通过定义它跟随距离的参数一直追踪一个给出的对象。ChaseCamera也定义一些值让它平滑跟随。那就是它不能突然转向玩家。这种突然性的效果当然也是可以定义的。

所以,当我们使用ChaseCamera,视图将一直对着玩家。鼠标将允许camera在玩家四周旋转并一直面向它。鼠标滚轮将允许camera缩放(尽管在这个例子中缩放值很小)。

因此,创建一个buildChaseCamera方法并从initGame中调用它。我们在这里设置ChaseCamera的参数并创建它。ChaseCamera对象将成为一个类变量以致我们能update它(所以把它加到类的顶部)。

我们相对ChaseCamera设置的参数有一些。首先,我们将设置Camera的目标偏移玩家。我们通常想让camera看起来在玩家上一点。所以我们设置偏移(offset)值为(0,玩家的Y*1.50.这将让camera指向指向一个在玩家原始高度上面多一半的一个点。下一步,我们将设置滚动(rollout)值。这些值决定了我们能拉近或推远摄像机多少。我这里不想给太多*,因此这个级别实际上很小。所以我们设置最大为6个单元,而最小为3个单元。下一步我们将设置camera能向上转动多高,在这个例子中为45度,注意是弧度。最后,我们将为camera设置开始起点的球形坐标,roll out5并升高30度。因为camera在一个“弹簧”系统中,如果交通工具行驶太快时,它能延迟落后一定距离。因此,我们将增加camera能落后的最小和最大值。82应该是可以的。

我们在一个hash map中设置这些参数。

privatevoid buildChaseCamera() {

Vector3f targetOffset = new Vector3f();

targetOffset.y =

((BoundingBox)player.getWorldBound()).yExtent*1.5f;

HashMap props = new HashMap();

props.put(ThirdPersonMouseLook.PROP_MAXROLLOUT, "6");

props.put(ThirdPersonMouseLook.PROP_MINROLLOUT, "3");

props.put(

ThirdPersonMouseLook.PROP_MAXASCENT,

""+45*FastMath.DEG_TO_RAD

);

props.put(

ChaseCamera.PROP_INITIALSPHERECOORDS,

new Vector3f(5,0,30*FastMath.DEG_TO_RAD)

);

props.put(ChaseCamera.PROP_TARGETOFFSET, targetOffset);

chaser = new ChaseCamera(cam, player, props);

chaser.setMaxDistance(8);

chaser.setMinDistance(2);

}

我们现在已经设置好了自己的ChaseCamera,但它在调用update方法之前不会产生任何作用。因此在update中加入:

chaser.update(interpolation);

现在,当应用程序运行时,你能看到camera在它初始化的位置并光滑地把镜头拉近直到最大的6个单元。你能接着移动鼠标去围绕box旋转camera,也能滚动鼠标滑轮去将camera拉近或推远一点。那就是所激动的,但没有什么东西可以追踪,因为box只是停在那里。让我们纠正那个。

这里补充一点是作者漏掉的,我们还需要在gameupdate方面里面加入下面代码以保证camera的位置一直在terrain上面:

//我们不想chase camera走到世界下面,因此让它一直在水平面上2个单元。

if(cam.getLocation().y tb.getHeight(cam.getLocation())+2)) {

cam.getLocation().y = tb.getHeight(cam.getLocation()) + 2;

cam.update();

}

5.6、我们自定义的输入处理

我们将创建自己的输入处理器(InputHandler)从而允许我们驾驶交通工具。这个handler的目标是允许我们行驶向前、向后和转向。我想这些控制的键被设置为:WASD。幸运的是,为了做到这个,我们将能使用在jME中构建的action。名字是,KeyNodeForwardActionKeyNodeBackwardActionKeyNodeRotateRightActionKeyNodeRotateLeftAction。这些action处理一个node的旋转和移动,这些都基于速度和传入的时间。

InputAction很直观。你简单将触发器(trigger)赋给action,并把键(key)赋给这些触发器。然后在每次update期间它将检查是否有任何键被按下,如果它们是trigger赋予的按键,那么则让trigger去调用相应的action

创建一个新的叫做FlagRushInputHandler的类,它继承自InputHandler。这个类将只有2个方法,setKeyBindingssetActionssetKeyBindings将创建KeyBindingManager并赋予W,A,S,D到相应的trigger名字,而setActions将为每个trigger创建InputAction

FlagRushInputHandler.java

import com.jme.input.InputHandler;

import com.jme.input.KeyBindingManager;

import com.jme.input.KeyInput;

import com.jme.input.action.KeyNodeBackwardAction;

import com.jme.input.action.KeyNodeForwardAction;

import com.jme.input.action.KeyNodeRotateLeftAction;

import com.jme.scene.Spatial;

/**

* 游戏的InputHnadler。这控制了一个给出的Spatial

* 允许我们去把它往前移、往后移和左右旋转。

* @author John

*

*/

publicclass FlagRushInputHandler extends InputHandler {

/**

* 提供用于控制的nodeapi将处理input的创建

* @param node 我们想移动的那个node

* @param api library将处理input的创建

*/

public FlagRushInputHandler(Spatial node, String api){

setKeyBindings(api);

setActions(node);

}

/**

* action类赋给trigger。这些action处理结点前移、后移和旋转

* @param node 用于控制的结点

*/

privatevoid setActions(Spatial node) {

KeyNodeForwardAction forward =

new KeyNodeForwardAction(node,30f);

addAction(forward,"forward",true);

KeyNodeBackwardAction backward =

new KeyNodeBackwardAction(node,15f);

addAction(backward,"backward",true);

KeyNodeRotateLeftAction rotateLeft =

new KeyNodeRotateLeftAction(node,5f);

rotateLeft.setLockAxis(

node.getLocalRotation().getRotationColumn(1)

);

addAction(rotateLeft,"turnLeft",true);

KeyNodeRotateRightAction rotateRight =

new KeyNodeRotateRightAction(node,5f);

rotateRight.setLockAxis(

node.getLocalRotation().getRotationColumn(1)

);

addAction(rotateRight,"turnRight",true);

}

/**

* 创建keyboard对象,当键被按下时允许我们获取键盘的值。

* 它接着设置action作为触发器的基础,如果确认了键被按下(WASD

* @param api

*/

privatevoid setKeyBindings(String api) {

KeyBindingManager keyboard =

KeyBindingManager.getKeyBindingManager();

keyboard.set("forward", KeyInput.KEY_W);

keyboard.set("backward", KeyInput.KEY_S);

keyboard.set("turnLeft", KeyInput.KEY_A);

keyboard.set("turnRight", KeyInput.KEY_D);

}

}

当这个类真的写完后,我们在自己的游戏中使用。创建一个buildInput方法,由initGame方法调用。这个方法将只有一行:

input = new FlagRushInputHandler(

player,

settings.getRenderer()

);

正如你所猜的,这里input也是类变量。为什么要在类里面呢?因为你将在游戏的update期间调用它的update

就是那样!不管相信与否,我们现在具有做游戏的条件。Box能被驾驶。现在试试看。注意ChaseCamera将会落后于box一点然后尝试赶上,带来更平滑和真实的感觉。

接下来,我们将改进box的移动以便它能加速和减速。我们也将让它和环境交互得更好。继续收看!