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

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

话不多说,下面为译文。

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

如果有什么错误可以留言或EMAIL[email protected]给我。

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并告诉它剔除三角形背后的面。够还不够简单?

你可能感兴趣的:(Camera)