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

花了一个小时研究了CullState,发现以前没学线性代数真是个错误。唉!不过这也和学校教育制度有关吧,如果学校能以2个方向培养人才:先理论后应用,先应用后理论,学生选择适合自己的方向这样多好!

话不多说,下面为译文。

 

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

如果有什么错误可以留言或EMAILkakashi9bi@gmail.com给我。

 

jME版本 jME_2.0.1_Stable

开发工具:MyEclipse8.5

操作系统:Window7/Vista

 

5、增加一个跟随摄像机(Chase Camera)

在这个向导中我们准备增加一个玩家和一些控制。我们将集中于交通工具的基本移动和跟随摄像机(下文以ChaseCamera代替)。所以,我们将为交通工具增加一个Box占位符,增加一个ChaseCamera,然后学习关于构建我们的自定义InputHandle(输入处理)。主要,我们将构建一个ThirdPersonHandler(第三人称控制器)。然而,jME包含一个预建的ThirdPersonHandle,它具有更多特性并值得研究。为了达到最好的教育效果,我们将创建自己的控制器。

5.1、清理和优化

同样地,我们这一节课将在前一节的基础上创建代码。每次迭代我们将清理现有的代码,为了达到展示怎样把事情做得更好的效果。

         Lesson5首先最主要的改变是我们创建了一个ForceFieldFence的类。通过将所有force field的数据移到它自己的类里面,这将允许我们可以在之后心血来潮的时候改善它。首先,我创建了一个新的类继承自Node。我们继承Node所以我们能附加fence到这个类本身并和平常一样把fence增加到scene。我接着创建了buildFence方法,从我们游戏的buildEnvironment方法中移除代码(除了位置和缩放调用,它们是游戏相关的,和围栏没有关系)到这个新的方法。现在,我们也想要继续力场的动画,所以我们也将移动Texture t到这个类并创建update方法。游戏中的Texture代码将移动到这个方法。现在,在游戏代码中,buildEnvironment方法我们加入:

 

       fence = new ForceFieldFence("forceFieldFence");

 

与此同时为游戏类增加一个类变量

 

    private ForceFieldFence fence;

 

         最后,我们需要告诉fence类在游戏update期间也update。在update方法中增加:

      

       fence.update(interpolation);

 

         将这么做。

         现在,游戏将和之前运行的一样,但我们有个fence让一切更容易。

5.2ForceFieldFence.java

package lesson5;

 

import com.jme.bounding.BoundingBox;

import com.jme.image.Texture;

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.SharedMesh;

import com.jme.scene.shape.Box;

import com.jme.scene.shape.Cylinder;

import com.jme.scene.state.BlendState;

import com.jme.scene.state.TextureState;

import com.jme.system.DisplaySystem;

import com.jme.util.TextureManager;

 

public class ForceFieldFence extends Node {

   

    private Texture t;

   

    public ForceFieldFence(String name){

       super(name);

       buildFence();

    }

   

    public void buildFence(){

       //这个圆柱体将扮演每个角落那4个主要的柱子

       Cylinder postGeometry = new Cylinder("Cylinder", 10, 10, 1, 10);

       Quaternion q = new Quaternion();

       //将圆柱体转为垂直

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

       postGeometry.setLocalRotation(q);

       postGeometry.setModelBound(new BoundingBox());

       postGeometry.updateModelBound();

      

       //我们将共享柱子4次(每个柱子一次)

       //增加原始的圆柱体不是一种好的方法

       //因为sharedmesh将修改它的本地值

       //我们然后将调整柱子到它的位置

       //使用神奇的数字是不好的,但帮助我们声明它的位置^_^

       SharedMesh post1 = new SharedMesh("post1",postGeometry);

       post1.setLocalTranslation(new Vector3f(0,0.5f,0));

       SharedMesh post2 = new SharedMesh("post2",postGeometry);

       post2.setLocalTranslation(new Vector3f(32,0.5f,0));

       SharedMesh post3 = new SharedMesh("post3",postGeometry);

       post3.setLocalTranslation(new Vector3f(0,0.5f,32));

       SharedMesh post4 = new SharedMesh("post4",postGeometry);

       post4.setLocalTranslation(new Vector3f(32,0.5f,32));

      

       //将所有的柱子放入一个tower Node

       Node towerNode = new Node("tower");

       towerNode.attachChild(post1);

       towerNode.attachChild(post2);

       towerNode.attachChild(post3);

       towerNode.attachChild(post4);

      

       //towerNode放入不透明队列(Opaque queue),我们不必看穿它

       //而我们想穿过forcefield看到它

       towerNode.setRenderQueueMode(Renderer.QUEUE_OPAQUE);

      

       //towerNode加载纹理

       TextureState ts2 = DisplaySystem.getDisplaySystem().getRenderer().createTextureState();

       Texture t2 = TextureManager.loadTexture(

              getClass().getClassLoader()

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

              Texture.MinificationFilter.Trilinear,

              Texture.MagnificationFilter.Bilinear

       );

       ts2.setTexture(t2);

       towerNode.setRenderState(ts2);

      

       //这个圆柱体将是水平的建筑

       //它将field限制在一个地方

       Cylinder strutsGeometry = new Cylinder("struts",10,10,0.125f,32);

       strutsGeometry.setModelBound(new BoundingBox());

       strutsGeometry.updateModelBound();

      

       //同样的,我们将共享mesh

       SharedMesh strut1 = new SharedMesh("strut1",strutsGeometry);

       Quaternion rotate90 = new Quaternion();

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

       strut1.setLocalRotation(rotate90);

       strut1.setLocalTranslation(new Vector3f(16,3f,0));

       SharedMesh strut2 = new SharedMesh("strut2",strutsGeometry);

       strut2.setLocalTranslation(new Vector3f(0,3f,16));

       SharedMesh strut3 = new SharedMesh("strut3",strutsGeometry);

       strut3.setLocalTranslation(new Vector3f(32,3f,16));

       SharedMesh strut4 = new SharedMesh("strut4",strutsGeometry);

       strut4.setLocalRotation(rotate90);

       strut4.setLocalTranslation(new Vector3f(16,3f,32));

      

       //将所有建筑放入一个结点

       Node strutNode = new Node("strutNode");

       strutNode.attachChild(strut1);

       strutNode.attachChild(strut2);

       strutNode.attachChild(strut3);

       strutNode.attachChild(strut4);

      

       //为建筑加载纹理

       TextureState ts3 = DisplaySystem.getDisplaySystem().getRenderer().createTextureState();

       Texture t3 = TextureManager.loadTexture(

              getClass().getClassLoader()

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

              Texture.MinificationFilter.Trilinear,

              Texture.MagnificationFilter.Bilinear

       );

       ts3.setTexture(t3);

       strutNode.setRenderState(ts3);

      

       //创建真实的forcefield

       //第一个box控制着X轴,而第二个控制着z

       //作为示例,我们没有旋转box,而是展示box能被不同地创建

       Box forceFieldX = new Box(

              "forceFieldX",

              new Vector3f(-16, -3f, -0.1f),

              new Vector3f(16, 3f, 0.1f)

       );

       forceFieldX.setModelBound(new BoundingBox());

       forceFieldX.updateModelBound();

       //我们也将共享这些box

       SharedMesh forceFieldX1 = new SharedMesh("forceFieldX1", forceFieldX);

       forceFieldX1.setLocalTranslation(new Vector3f(16,0,0));

       SharedMesh forceFieldX2 = new SharedMesh("forceFieldX2", forceFieldX);

       forceFieldX2.setLocalTranslation(new Vector3f(16,0,32));

      

       //另一个box,控制z轴的那个

       Box forceFieldZ = new Box(

              "forceFieldY",

              new Vector3f(-0.1f, -3f, -16f),

              new Vector3f(0.1f, 3f, 16f)

       );

        forceFieldZ.setModelBound(new BoundingBox());

       forceFieldZ.updateModelBound();

       //我们也将共享这些box

       SharedMesh forceFieldZ1 = new SharedMesh("forceFieldZ1", forceFieldZ);

       forceFieldZ1.setLocalTranslation(new Vector3f(0,0,16));

       SharedMesh forceFieldZ2 = new SharedMesh("forceFieldZ2", forceFieldZ);

       forceFieldZ2.setLocalTranslation(new Vector3f(32,0,16));

      

       //增加所有的forceField到一个单一的Node

       Node forceFieldNode = new Node("forceFieldNode");

       forceFieldNode.attachChild(forceFieldX1);

       forceFieldNode.attachChild(forceFieldX2);

       forceFieldNode.attachChild(forceFieldZ1);

       forceFieldNode.attachChild(forceFieldZ2);

 

       //force field元素增加texture

       TextureState ts = DisplaySystem.getDisplaySystem().getRenderer().createTextureState();

       t = TextureManager.loadTexture(

              getClass().getClassLoader()

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

              Texture.MinificationFilter.Trilinear,

              Texture.MagnificationFilter.Bilinear

       );

      

       t.setWrap(Texture.WrapMode.Repeat);

       t.setTranslation(new Vector3f());

       ts.setTexture(t);

      

       //transparent结点增加Alpha

       BlendState as1 = DisplaySystem.getDisplaySystem().getRenderer().createBlendState();

       as1.setSourceFunction(

              BlendState.SourceFunction.SourceAlpha

       );

       as1.setDestinationFunction(

              BlendState.DestinationFunction.One

       );

       as1.setTestFunction(BlendState.TestFunction.GreaterThan);

       as1.setBlendEnabled(true);

       as1.setTestEnabled(true);

       as1.setEnabled(true);

       forceFieldNode.setRenderState(as1);

       forceFieldNode.setRenderState(ts);

      

       forceFieldNode.setRenderQueueMode(Renderer.QUEUE_TRANSPARENT);

       towerNode.setRenderQueueMode(Renderer.QUEUE_OPAQUE);

       strutNode.setRenderQueueMode(Renderer.QUEUE_OPAQUE);

      

       Node forceFieldFence = new Node("forceFieldFenceNode");

       forceFieldFence.attachChild(towerNode);

       forceFieldFence.attachChild(strutNode);

       forceFieldFence.attachChild(forceFieldNode);

      

       attachChild(forceFieldFence);

    }

   

    public void update(float interpolation){

       //我们将使用插值(interpolation)去保持forcefield

       //texture运动速度和计算机保持一致

       //我们更新texture矩阵的Y值去让

       //force-field看起来像是在移动

       t.getTranslation().y += 0.3f*interpolation;

       //如果translation超过1,它被换行,因此回到开始

       //并检查这个(防止VectorY值变得太大)

       t.getTranslation().y = t.getTranslation().y > 1 ? 0 : t.getTranslation().y;

    }

}

5.3、剔除/挑选状态(CullState)

         最后的一点优化是在game中添加了CullState。这将是我们的首次提速。CullState处理三角形的剔除,这和视锥挑选(Frustum Culling)是不同的,因为它只和单个三角形的winding有关。顶点的winding定义了三角形的法向并接着告诉了我们三角形是否面对我们。jME使用和OpenGL一样的右手坐标系。这意味着,如果一个三角形的winding是逆时针(CCW)的,那么它面向屏幕。

         我们没理由看到三角形的背后,因此,也没理由去绘制它们。所以,我们将为整个scene应用CullState去移除所有三角形后面的面。这很直接,你只需要在initGame方法中加入:

      

       CullState cs = display.getRenderer().createCullState();

       cs.setCullFace(CullState.Face.Back);

       scene.setRenderState(cs);

 

         耶,那很容易。我简单创建了一个新的state并告诉它剔除三角形背后的面。够还不够简单?

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值