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

5.4、让我们增加玩家

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

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

 

    private void 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上拥有了玩家!

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

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中设置这些参数。

 

    private void buildChaseCamera() {

       Vector3f targetOffset = new Vector3f();

       targetOffset.y =

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

       HashMap<String, Object> props = new HashMap<String, Object>();

       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

 *

 */

public class 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 用于控制的结点

     */

    private void 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

     */

    private void 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的移动以便它能加速和减速。我们也将让它和环境交互得更好。继续收看!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值