好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

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

8.6 、为旗杆增加布 我想要让 Flag 看起来像 很好,像 Flag 。为了这么做,我们需要模拟一个布的 Flag , attach 到旗杆。有什么更好的方式完成这件事,还是使用 jME 的 ClothPatch 功能。这将允许我们去创建一个弹簧( spring )点的 matrix ,它们由不同方

8.6 、为旗杆增加布

我想要让 Flag 看起来像 … 很好,像 Flag 。为了这么做,我们需要模拟一个布的 Flag , attach 到旗杆。有什么更好的方式完成这件事,还是使用 jME 的 ClothPatch 功能。这将允许我们去创建一个弹簧( spring )点的 matrix ,它们由不同方向的外力( force )调整(引力和风力)。我已经为这个向导创建了我自己的风力,而我们将在下面讨论。

首先,增加对象到 Flag 类。

// 用于制作 Flag 的 Cloth

private ClothPatch cloth ;

// 风的参数

private float windStrength = 15f;

private Vector3f windDirection = new Vector3f(0.8f, 0, 0.2f);

private SpringPointForce gravity , wind ;

在 Flag 的构造参数中,我们将创建一个 ClothPatch 。这个 Cloth 将是 25*25 的 matrix ,给它一个相当详细的 cloth 。 cloth 上的点越多,它运行得越慢,所以你应该在 flag 的视觉外观和它对游戏的影响之间选个平衡点。 25*25 给我一个可接受的性能和外观的比例。在创建 cloth 之后,我们将增加我们的 force 。我们增加的第一个 force 是我们为这个向导创建的自定义的 force ,叫做 RandomFlagWindForce 。我们也将增加一个默认的重力它是由 ClothUtils 创建的,这将在风减小的时候把 Flag 拉下来。

// 创建一个 cloth patch 将处理我们 flag

cloth = new ClothPatch( "cloth" , 25, 25, 1f, 10);

// 将我们自定义的风力增加到 cloth

wind = new RandomFlagWindForce( windStrength , windDirection );

cloth .addForce( wind );

// 增加一个简单的重力

gravity = ClothUtils. createBasicGravity ();

cloth .addForce( gravity );

由于 cloth 是标准的 jME Spatial ,我们和平常一样应用 RenderState 。我们将为 Flag 增加 texture ,使用 jME monkey 的 logo 。这个是一个让 monkey 的头在你脑海留下烙印的企图。

// 创建一个将 flag 显示的 texture ,一起推进 jME 发展!

TextureState ts = DisplaySystem. getDisplaySystem ()

.getRenderer().createTextureState();

ts.setTexture(

TextureManager. loadTexture (

Flag. class .getClassLoader()

.getResource( "res/logo.png" ),

Texture.MinificationFilter. Trilinear ,

Texture.MagnificationFilter. Bilinear

)

);

cloth .setRenderState(ts);

当我开始增加一个 flag 到场景时,我对 light 非常不满意,因为 flag 大部分时间都是阴影。因此,我决定增加一个 light ,它只影响 flag 。这个 light 应该跟着 Flag 移动,并且根据 Flag 定位自己。因此,使用一个 LightNode 是最好的解决方案。 LightNode 允许你对待一个 light 就像 scene 中的其它元素一样,通过移动 light 的父亲 node 移动它。

// 我们将使用一个 LightNode 去给 Flag 增加 light ,使用 Node

// 是因为它允许 light 随着 FLag 移动

// 首先创建 light

PointLight dr = new PointLight();

dr.setEnabled( true );

dr.setDiffuse( new ColorRGBA(1.0f, 1.0f, 1.0f, 1.0f));

dr.setAmbient( new ColorRGBA(0.5f, 0.5f, 0.5f, 1.0f));

dr.setLocation( new Vector3f(0.5f, -0.5f, 0));

// 接下来是 state

LightState lightState = DisplaySystem. getDisplaySystem ()

.getRenderer(). createLightState ();

lightState.setEnabled( true );

lightState.setTwoSidedLighting( true );

lightState.attach(dr);

setRenderState(lightState);

// 最后是结点

LightNode lightNode = new LightNode( "light" );

lightNode.setLight(dr);

lightNode.setLocalTranslation( new Vector3f(15,0,0));

attachChild(lightNode);

cloth .setRenderState(lightState);

Flag 有一点和前面不同,那就是我们想看到它所有面的三角形。前面,我们设置了 scene 的 CullState 去剔除所有背后的三角形。因此,我们将增加另一个 CUllState 给 cloth ,让它不要剔除任何三角形。它将看到所有的三角形。

// 我们想看 flag 所有的面,所以我们将关闭 Cull

CullState cs = DisplaySystem. getDisplaySystem ()

.getRenderer().createCullState();

cs.setCullFace(CullState.Face. None );

cloth .setRenderState(cs);

this .attachChild( cloth );

下一步,我们需要为 cloth 创建一些点去固定它。如果我们不这么做,整个 cloth 将在 force 应用于它身上时被[吹走]。我们需要在旗杆上 attach 一些点去保住 cloth 。我们通过设置点的质量为无穷大来这么做。因此,没有任何 force 能移走它。为了模拟这个方法,一个 flag 被 attach 到一个旗杆, flag 上边缘的一些和旗杆接触的点将被 attach ,下面的也是。这些点被设置在一个基础的二维 matrix 中,所以我们将 attach 点( 0 , 1 , 2 , 3 , 4 )到旗杆,点( 500 , 525 , 550 , 575 , 600 )也一样。我们也调整这些点在 y 的位置让它们偏移,从而让 cloth 形成一点褶皱。

// 我们需要 attach 一些点到旗杆,这些点不应该被移动。

// 因此,我们将 attach 顶部和底部的 5 个点去让它们足够高而

// 且没有任何 forece 能移动它。我也稍微移动这些点的位置去

// 形成褶皱让它更真实。

for ( int i=0; i

cloth .getSystem().getNode(i*25). position . y *= .8f;

cloth .getSystem().getNode(i*25)

.setMass(Float. POSITIVE_INFINITY );

}

for ( int i=24; i>19; i--){

cloth .getSystem().getNode(i*25). position . y *= .8f;

cloth .getSystem().getNode(i*25)

.setMass(Float. POSITIVE_INFINITY );

}

最后,我创建自定义风力。为了模仿旗杆上 flag 的效果,我们也随机化风向。风向将基于它之前的位置

/**

* 在每次 update cloth 时调用。将轻微调整风向和强度、

*/

public void apply( float dt, SpringPoint node) {

windDirection . x += dt * (FastMath. nextRandomFloat () - .5f);

windDirection . z += dt * (FastMath. nextRandomFloat () - .5f);

windDirection .normalize();

float tStr = FastMath. nextRandomFloat () * strength ;

node. acceleration .addLocal(

windDirection . x * tStr,

windDirection . y * tStr,

windDirection . z * tStr

);

}

通过那样,我们现在有一个看起来真实的 flag ,而且我们以自己的方式去为我们游戏中添加元素。下一课,我们只需能获取 flag 。

8.7 、源码

import java.io.IOException;

import java.net.URL;

import java.util.HashMap;

import javax.swing.ImageIcon;

import com.jme.app.BaseGame;

import com.jme.bounding.BoundingBox;

import com.jme.image.Texture;

import com.jme.input.ChaseCamera;

import com.jme.input.InputHandler;

import com.jme.input.KeyBindingManager;

import com.jme.input.KeyInput;

import com.jme.input.thirdperson.ThirdPersonMouseLook;

import com.jme.light.DirectionalLight;

import com.jme.math.FastMath;

import com.jme.math.Vector3f;

import com.jme.renderer.Camera;

import com.jme.renderer.ColorRGBA;

import com.jme.renderer.Renderer;

import com.jme.scene.Node;

import com.jme.scene.Skybox;

import com.jme.scene.shape.Box ;

import com.jme.scene.state.CullState;

import com.jme.scene.state.LightState;

import com.jme.scene.state.TextureState;

import com.jme.scene.state.ZBufferState;

import com.jme.system.DisplaySystem;

import com.jme.system.JmeException;

import com.jme.util.TextureManager;

import com.jme.util.Timer;

import com.jme.util.export.binary.BinaryImporter;

import com.jmex.model.converters.MaxToJme ;

import com.jmex.terrain.TerrainBlock;

import com.jmex.terrain.util.MidPointHeightMap;

import com.jmex.terrain.util.ProceduralTextureGenerator;

public class Lesson8 extends BaseGame{

private int width , height ;

private int freq , depth ;

private boolean fullscreen ;

// 我们的 camera 对象,用于观看 scene

private Camera cam ;

protected Timer timer ;

private Node scene ;

private TextureState ts ;

private TerrainBlock tb ;

private ForceFieldFence fence ;

private Skybox skybox ;

private Vehicle player ;

private ChaseCamera chaser ;

private InputHandler input ;

// 保存 terrain 的任何一个给出点的法向

private Vector3f normal = new Vector3f();

private float agl ;

private Flag flag ;

public static void main(String[] args) {

Lesson8 app = new Lesson8();

java.net.URL url = app.getClass().getClassLoader().getResource( "res/logo.png" );

app.setConfigShowMode(ConfigShowMode. AlwaysShow ,url);

app.start();

}

/*

* 清除 texture

*/

protected void cleanup() {

ts .deleteAll();

}

protected void initGame() {

display .setTitle( "Flag Rush" );

scene = new Node( "Scene Graph Node" );

ZBufferState buf = display .getRenderer().createZBufferState();

buf.setEnabled( true );

buf.setFunction(ZBufferState.TestFunction. LessThanOrEqualTo );

scene .setRenderState(buf);

buildTerrain();

buildFlag();

buildLighting();

buildEnvironment();

createSkybox();

buildPlayer();

buildChaseCamera();

buildInput();

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

cs.setCullFace(CullState.Face. Back );

scene .setRenderState(cs);

// 更新 scene 用于渲染

scene .updateGeometricState(0.0f, true );

scene .updateRenderState();

}

private void buildFlag() {

flag = new Flag( tb );

scene .attachChild( flag );

flag .placeFlag();

}

private void buildInput() {

input = new FlagRushInputHandler(

player ,

settings .getRenderer()

);

}

private void buildChaseCamera() {

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 )

);

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

chaser .setMaxDistance(8);

chaser .setMinDistance(2);

}

private void buildPlayer() {

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();

}

// 设置 Vehicle 的属性(这些数字能被认为是单元 / 秒 Unit/S )

player = new Vehicle( "Player Node" ,model);

player .setAcceleration(15);

player .setBraking(25);

player .setTurnSpeed(2.5f);

player .setWeight(25);

player .setMaxSpeed(25);

player .setMinSpeed(15);

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

player .updateWorldBound();

player .setRenderQueueMode(Renderer. QUEUE_OPAQUE );

scene .attachChild( player );

scene .updateGeometricState(0, true );

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

}

private void createSkybox() {

skybox = new Skybox( "skybox" ,10,10,10);

Texture north = TextureManager. loadTexture (

Lesson8. class .getClassLoader()

.getResource( "res/texture/north.jpg" ),

Texture.MinificationFilter. BilinearNearestMipMap ,

Texture.MagnificationFilter. Bilinear

);

Texture south = TextureManager. loadTexture (

Lesson8. class .getClassLoader()

.getResource( "res/texture/south.jpg" ),

Texture.MinificationFilter. BilinearNearestMipMap ,

Texture.MagnificationFilter. Bilinear

);

Texture east = TextureManager. loadTexture (

Lesson8. class .getClassLoader()

.getResource( "res/texture/east.jpg" ),

Texture.MinificationFilter. BilinearNearestMipMap ,

Texture.MagnificationFilter. Bilinear

);

Texture west = TextureManager. loadTexture (

Lesson8. class .getClassLoader()

.getResource( "res/texture/west.jpg" ),

Texture.MinificationFilter. BilinearNearestMipMap ,

Texture.MagnificationFilter. Bilinear

);

Texture up = TextureManager. loadTexture (

Lesson8. class .getClassLoader()

.getResource( "res/texture/top.jpg" ),

Texture.MinificationFilter. BilinearNearestMipMap ,

Texture.MagnificationFilter. Bilinear

);

Texture down = TextureManager. loadTexture (

Lesson8. class .getClassLoader()

.getResource( "res/texture/bottom.jpg" ),

Texture.MinificationFilter. BilinearNearestMipMap ,

Texture.MagnificationFilter. Bilinear

);

skybox .setTexture(Skybox.Face. North , north);

skybox .setTexture(Skybox.Face. West , west);

skybox .setTexture(Skybox.Face. South , south);

skybox .setTexture(Skybox.Face. East , east);

skybox .setTexture(Skybox.Face. Up , up);

skybox .setTexture(Skybox.Face. Down , down);

skybox .preloadTextures();

scene .attachChild( skybox );

}

private void buildEnvironment() {

fence = new ForceFieldFence( "forceFieldFence" );

// 我们将手工做一些调整去让它更好适应 terrain

// 首先我们将实体 [ 模型 ] 放大

fence .setLocalScale(5);

// 现在,让我们移动 fence 到 terrain 的高度并有一点陷入它里面

fence .setLocalTranslation(

new Vector3f(25, tb .getHeight(25,25)+10,25)

);

scene .attachChild( fence );

}

private void buildLighting() {

/* 设置一个基础、默认灯光 */

DirectionalLight light = new DirectionalLight();

light.setDiffuse( new ColorRGBA(1.0f, 1.0f, 1.0f, 1.0f));

light.setAmbient( new ColorRGBA(0.5f, 0.5f, 0.5f, 1.0f));

light.setDirection( new Vector3f(1, -1, 0));

light.setEnabled( true );

LightState lightState = display .getRenderer().createLightState();

lightState.setEnabled( true );

lightState.attach(light);

scene .setRenderState(lightState);

}

/**

* 创建 heightmap 和 terrainBlock

*/

private void buildTerrain() {

// 生成随机地形数据

MidPointHeightMap heightMap = new MidPointHeightMap(64,1f);

// 缩放数据

Vector3f terrainScale = new Vector3f(4, .0575f, 4);

// 创建一个 terrain block

tb = new TerrainBlock(

"terrain" ,

heightMap.getSize(),

terrainScale,

heightMap.getHeightMap(),

new Vector3f(0, 0, 0)

);

tb .setModelBound( new BoundingBox());

tb .updateModelBound();

// 通过三个纹理生成地形纹理

ProceduralTextureGenerator pt =

new ProceduralTextureGenerator(heightMap);

pt.addTexture(

new ImageIcon(

getClass().getClassLoader()

.getResource( "res/grassb.png" )

),

-128, 0, 128

);

pt.addTexture(

new ImageIcon(

getClass().getClassLoader()

.getResource( "res/dirt.jpg" )

),

0, 128, 256

);

pt.addTexture(

new ImageIcon(

getClass().getClassLoader()

.getResource( "res/highest.jpg" )

),

128, 256, 384

);

pt.createTexture(32);

// 将纹理赋予地形

ts = display .getRenderer().createTextureState();

Texture t1 = TextureManager. loadTexture (

pt.getImageIcon().getImage(),

Texture.MinificationFilter. Trilinear ,

Texture.MagnificationFilter. Bilinear ,

true

);

ts .setTexture(t1, 0);

// 加载细节纹理并为 2 个 terrain 的 texture 设置组合模型

Texture t2 = TextureManager. loadTexture (

Lesson8. class .getClassLoader()

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

Texture.MinificationFilter. Trilinear ,

Texture.MagnificationFilter. Bilinear

);

ts .setTexture(t2, 1);

t2.setWrap(Texture.WrapMode. Repeat );

t1.setApply(Texture.ApplyMode. Combine );

t1.setCombineFuncRGB(Texture.CombinerFunctionRGB. Modulate );

t1.setCombineSrc0RGB(Texture.CombinerSource. CurrentTexture );

t1.setCombineOp0RGB(Texture.CombinerOperandRGB. SourceColor );

t1.setCombineSrc1RGB(Texture.CombinerSource. PrimaryColor );

t1.setCombineOp1RGB(Texture.CombinerOperandRGB. SourceColor );

t2.setApply(Texture.ApplyMode. Combine );

t2.setCombineFuncRGB(Texture.CombinerFunctionRGB. AddSigned );

t2.setCombineSrc0RGB(Texture.CombinerSource. CurrentTexture );

t2.setCombineOp0RGB(Texture.CombinerOperandRGB. SourceColor );

t2.setCombineSrc1RGB(Texture.CombinerSource. Previous );

t2.setCombineOp1RGB(Texture.CombinerOperandRGB. SourceColor );

tb .setRenderState( ts );

tb .setDetailTexture(1, 16);

tb .setRenderQueueMode(Renderer. QUEUE_OPAQUE );

scene .attachChild( tb 查看更多关于jMonkeyEngine译文FlagRush8(2)增加随机的Flag的详细内容...

  阅读:45次