好得很程序员自学网

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

jMonkeyEngine译文FlagRush2从你的应用程序中移除SimpleGam

注:本系列教程全部翻译完之后可能会以 PDF 的形式发布。 如果有什么错误可以留言或 EMAIL : kakashi9bi@gmail.com 给我。 jME 版本 : jME_2.0.1_Stable 开发工具: MyEclipse8.5 操作系统: Window7/Vista 这个向导中,我们将为 Flag Rush 构建基

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

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

jME 版本 : jME_2.0.1_Stable

开发工具: MyEclipse8.5

操作系统: Window7/Vista

这个向导中,我们将为 Flag Rush 构建基础。我们将通过自己实现继承 BaseGame 。我们将使用 BaseGame 做为父类,但之后可能改为其它的游戏类型,因为 BaseGame 简单地尽可能快地进行 update 和 render 。我们或许不必或不想使用这种类型的循环。然而,现在 BaseGame 是一个循环无关的类。在以后,改变 BaseGame 将不是重点,因为只是传入 update 和 render 方法的值不同而已。

我们将开始创建一个继承自 BaseGame 的新类。你会注意到有 6 个需要实现的方法: update 、 render 、 initSystem 、 initGame 和 reinit 。现在,只需要为它们创建一个存根方法,我们将在后面将自己的逻辑填充进去。

import com.jme.app.BaseGame;

public class Lesson2 extends BaseGame{

public static void main(String[] args) {

}

protected void cleanup() {

}

protected void initGame() {

}

protected void initSystem() {

}

protected void reinit() {

}

protected void render( float arg0) {

}

protected void update( float arg0) {

}

}

2.1 、 Main

那么,让我们从最初开始。我们在这里将再次创建 main 方法。它很像前一个向导的 main 方法,除了一个关键的地方不同。这次我们将显示 FlagRush 的迷人的新 logo 。 AbstractGame 定义了一对 setConfigShowMode 方法,其中的一个接受一个 URL 类用于加载 Image 。因此,我们将加载 FlagRush.png (迷人的 logo )并把它传给这个方法。现在,当 PropertiesDialog 被显示时,它将显示新的 Logo 。

public static void main(String[] args) {

Lesson2 app = new Lesson2();

java.net.URL url =

app.getClass().getClassLoader()

.getResource( "jmetest/data/images/FlagRush.png" );

app.setConfigShowMode(ConfigShowMode. AlwaysShow ,url);

app.start();

}

现在,当 PropertiesDialog 出现时,它将像下面这个一样(你应该在项目中新建一个 package —— jmetest.data.images ,然后里面有一张叫 FlagRush.png 的图片):

2.2 、 InitSystem

现在,你能运行你的应用程序,但它仅仅是显示 PropertiesDialog ,除此之外不会做更多的工作。我们下一步将实现 initSystem 方法。这个方法在进入主循环之前由 BaseGame 调用。这正是我们设置 window 和 display 的地方。我们将保存 width , height , depth , frequency 和 fullscreen 标志。我们将在后面使用这些值,假如用户想改变分辨率的时候。所以,首先,让我们创建变量去保存这些值:

public class Lesson2 extends BaseGame{

private int width , height ;

private int freq , depth ;

private boolean fullscreen ;

……………………….

我们也需要在我们的程序中保存 Camera ,所以我们也应该为那创建一个变量。

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

private Camera cam ;

最后将初始化的一项是 Timer , Timer 将允许我们获取我们的帧率。所以,同样的,这将是一个实例变量。

protected Timer timer ;

现在我们已经准备好我们的实例变量,并且我们将在 initSystem 中初始化它们。

protected void initSystem() {

// 保存属性信息

width = settings .getWidth();

height = settings .getHeight();

depth = settings .getDepth();

freq = settings .getFrequency();

fullscreen = settings .isFullscreen();

try {

display = DisplaySystem. getDisplaySystem (

settings .getRenderer()

);

display .createWindow(

width , height , depth , freq , fullscreen

);

cam = display .getRenderer().createCamera( width , height );

} catch (JmeException e){

e.printStackTrace();

System. exit (-1);

}

// 设置背景为黑色

display .getRenderer().setBackgroundColor(ColorRGBA. black );

// 初始化摄像机

cam .setFrustumPerspective(

45.0f,

( float ) width /( float ) height ,

1f,

1000f

);

Vector3f loc = new Vector3f(0.0f,0.0f,25.0f);

Vector3f left = new Vector3f(-1.0f,0.0f,0.0f);

Vector3f up = new Vector3f(0.0f,1.0f,0.0f);

Vector3f dir = new Vector3f(0.0f,0.0f,-1.0f);

// 将摄像机移到正确位置和方向

cam .setFrame(loc, left, up, dir);

// 我们改变自己的摄像机位置和视锥的标志

cam .update();

// 获取一个高分辨率用于 FPS 更新

timer = Timer. getTimer ();

display .getRenderer().setCamera( cam );

KeyBindingManager. getKeyBindingManager ().set(

"exit" ,

KeyInput. KEY_ESCAPE

);

}

这是一个长的方法,所以,我们将一点一点讨论它。

// 保存属性信息

width = settings .getWidth();

height = settings .getHeight();

depth = settings .getDepth();

freq = settings .getFrequency();

fullscreen = settings .isFullscreen();

首先,我们保存从 properties 对象( properties 是由 AbstractGame 创建的)获取的值。通过保存这些值,当用户以后从系统菜单改变屏幕设置的时候,我们可以很容易地修改它们中的一个或全部值。

try {

display = DisplaySystem. getDisplaySystem (

settings .getRenderer()

);

display .createWindow(

width , height , depth , freq , fullscreen

);

cam = display .getRenderer().createCamera( width , height );

} catch (JmeException e){

e.printStackTrace();

System. exit (-1);

}

下一步,我们获取新的 DisplaySystem ,并通过先前获得的屏幕参数创建一个本地窗口。我们接着使用 DisplaySystem 去创建一个 Camera 对象。你将注意到那用一个 try/catch 块包围。如果我们尝试创建一个系统没能力绘制的窗口,异常将在这里出现。目前,它只会退出,但之后,我们将让这个显示得更友好,并通知用户。

// 设置背景为黑色

display .getRenderer().setBackgroundColor(ColorRGBA. black );

我们接着设置了窗口的背景颜色。当没有其它数据被渲染的时候,这是显示的默认颜色。我选择黑色,这是因为它和我们后面将使用的任何文本形成鲜明的对比。不管怎样,这都不是重点,因为当一切正常工作时,屏幕上通常覆盖其它的数据。

// 初始化摄像机

cam .setFrustumPerspective(

45.0f,

( float ) width /( float ) height ,

1f,

1000f

);

Vector3f loc = new Vector3f(0.0f,0.0f,25.0f);

Vector3f left = new Vector3f(-1.0f,0.0f,0.0f);

Vector3f up = new Vector3f(0.0f,1.0f,0.0f);

Vector3f dir = new Vector3f(0.0f,0.0f,-1.0f);

// 将摄像机移到正确位置和方向

cam .setFrame(loc, left, up, dir);

// 我们改变自己的摄像机位置和视锥的标志

cam .update();

display .getRenderer().setCamera( cam );

下一步,我设置了 camera 。我想要一个标准的 camera ,正常情况下是右手坐标系统(向上是正 Y ,向右是正 X 和向屏幕里面是 -Z )。我同时设置了透视图为 45 度视角。这个是大多数游戏里面的公认标准,而它将应用于 Flag Rush 。在 camera 数据设置之后,我们调用 update ,这将设置所有的 OpenGL 组件,例如视点(下文以 ViewPort 代替)和 Frustum 。

// 获取一个高分辨率用于 FPS 更新

timer = Timer. getTimer ();

这里只是初始化 Timer ,从本地 Timer 获取(例如 LWJGLTimer )。

KeyBindingManager. getKeyBindingManager ().set(

"exit" ,

KeyInput. KEY_ESCAPE

);

最后,我们创建一个新的 InputSystem ,将它绑定到我们的 KeyBindingManager 并设置一个输入行为( Input action )。在这个框架中我们只关心一个按键—— Escape 。在这个例子中,我们设置 action [ exit ]给 Escape 键。 KeyBindingManager 是一个单例类,它使用单一的 get 调用,关注了所有 InputSystem 组件的初始化。

现在,如果你运行系统你将真正获得一个屏幕显示。它将充满黑色(我们设置的背景颜色),没有任何东西。

2.3 、 InitGame

现在,我们拥有一个窗口和 OpenGL 上下文环境,我们将加载我们的游戏数据(如上面前个向导的 Sphere )

protected void initGame() {

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

// 创建我们的球体

Sphere s = new Sphere( "sphere" , 30, 30, 25);

s.setLocalTranslation( new Vector3f(0, 0, -40));

s.setModelBound( new BoundingBox());

s.updateModelBound();

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

ts . setEnabled ( true );

ts .setTexture(

TextureManager. loadTexture (

Lesson2. class .getClassLoader()

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

Texture.MinificationFilter. Trilinear ,

Texture.MagnificationFilter. Bilinear

)

);

s.setRenderState( ts );

scene .attachChild(s);

// 更新 scene 用于渲染

scene .updateGeometricState(0.0f, true );

scene .updateRenderState();

}

我们现在保存我们自己的 Scene Graph 结点,我已经选择把它命名为 scene ,但实际上怎样命名都是没关系。因为这是 scene 的根节点,它也是一个实例变量而它和其他实例变量一样被声明:

private Node scene ;

这个 Node 接着被实例化。接着我们创建了一个 Sphere 和 TextureState (就像上一个的向导)。 Sphere 接着被 attach 到 scene 。这个看起来将和我们上一个向导所做的很相似。然而,现在,我们还调用 updateGeometricState 和 updateRenderState 。这些方法为 SceneGraph updates 调用。 updateGeometricState 是必须的,不管场景图( Scene Graph )结构在何时改变(设置一个新的,改变另一个的参数,等等),在我们的例子中,我增加了一个 sphere 到到 scene 。不管 RenderState 在什么时候以何种方式发生改变, updateRenderState 都应该被调用(比如创建一个新的 RenderState 、改变它的参数等等),在我们的例子中,我们增加了 TextureState 。

我们现在拥有游戏中的数据,但它仍然没被渲染到屏幕。

2.4 、 Render 和 update

既然我们已经初始化了窗口并加载了数据,如果能看到它将更好。那就是 render 方法的到来。 BaseGame 调用 update 并根据它的能力尽可能快地 render 。 render 的调用需要处理所有绘画调用,而 update 应该处理任何的游戏逻辑。在我们的例子中,我们想要 update 做一点游戏逻辑,退出游戏。为了简单退出游戏,我们将设置 finished 布尔值为 true 。

/*

* 在 update 期间,我们只需寻找 Escape 按钮

* 并更新 timer 去获取帧率

*/

protected void update( float interpolation) {

// 更新 timer 去获取帧率

timer .update();

interpolation = timer .getTimePerFrame();

// 当 Escape 被按下时,我们退出游戏

if (KeyBindingManager. getKeyBindingManager ()

.isValidCommand( "exit" )

){

finished = true ;

}

}

你也将注意到 update 获取最新的 timer 读数并为此设置插值( interpolation )。 BaseGame 通常在调用 update 时发送 -1 ,所以我们将继续并重用这个值去保存每帧真正的时间。

接下来,我们将渲染。

/*

* 绘制场景图

*/

protected void render( float interpolation) {

// 清除屏幕

display .getRenderer().clearBuffers();

display .getRenderer().draw( scene );

}

这个直截了当。我们使用 clearBuffers 清除屏幕。我们接着画了 scene ,这是包含我们 Sphere 的树。

你现在能运行程序并看到:

正是和前一课的显示一样,只不过没了灯光。

2.5 、 reinit 和 cleanup

最后我们将覆盖的 2 个方法是 reinit 和 cleanup 。当窗口需要重建时, Reinit 应该被调用,就像参数发生了变化。而在关闭的时候调用 cleanup 。

/*

* 如果分辨率改变将被调用

*/

protected void reinit() {

display .recreateWindow( width , height , depth , freq , fullscreen );

}

我们在这里所做的就只是传递新的值给 DisplaySystem 处理。仅此而已。

/*

* 清除 texture

*/

protected void cleanup() {

ts .deleteAll();

}

这简单确保了 texture 被删除。这不是特别必须的,因为 OpenGL 在它退出时将处理这个。但[宁可事先谨慎有余,切莫事后追悔莫及]。

2.6 、总结

很好,就是那样。我们现在有一个很基本、可工作的框架。通过创建我们自己的应用程序类型,我们能完全保持对我们场景中一切的控制。随着向导的继续,我们将很明确地增强并构建基于这个类的程序。

2.7 、源码

import com.jme.app.BaseGame;

import com.jme.bounding.BoundingBox;

import com.jme.image.Texture;

import com.jme.input.KeyBindingManager;

import com.jme.input.KeyInput;

import com.jme.math.Vector3f;

import com.jme.renderer.Camera;

import com.jme.renderer.ColorRGBA;

import com.jme.scene.Node;

import com.jme.scene.shape.Sphere;

import com.jme.scene.state.TextureState;

import com.jme.system.DisplaySystem;

import com.jme.system.JmeException;

import com.jme.util.TextureManager;

import com.jme.util.Timer;

public class Lesson2 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 ;

public static void main(String[] args) {

Lesson2 app = new Lesson2();

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

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

// 创建我们的球体

Sphere s = new Sphere( "sphere" , 30, 30, 25);

s.setLocalTranslation( new Vector3f(0, 0, -40));

s.setModelBound( new BoundingBox());

s.updateModelBound();

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

ts .setEnabled( true );

ts .setTexture(

TextureManager. loadTexture (

Lesson2. class .getClassLoader().getResource( "res/logo.png" ),

Texture.MinificationFilter. Trilinear ,

Texture.MagnificationFilter. Bilinear

)

);

s.setRenderState( ts );

scene .attachChild(s);

// 更新 scene 用于渲染

scene .updateGeometricState(0.0f, true );

scene .updateRenderState();

}

protected void initSystem() {

// 保存属性信息

width = settings .getWidth();

height = settings .getHeight();

depth = settings .getDepth();

freq = settings .getFrequency();

fullscreen = settings .isFullscreen();

try {

display = DisplaySystem. getDisplaySystem (

settings .getRenderer()

);

display .createWindow(

width , height , depth , freq , fullscreen

);

cam = display .getRenderer().createCamera( width , height );

} catch (JmeException e){

e.printStackTrace();

System. exit (-1);

}

// 设置背景为黑色

display .getRenderer().setBackgroundColor(ColorRGBA. black );

// 初始化摄像机

cam .setFrustumPerspective(

45.0f,

( float ) width /( float ) height ,

1f,

1000f

);

Vector3f loc = new Vector3f(0.0f,0.0f,25.0f);

Vector3f left = new Vector3f(-1.0f,0.0f,0.0f);

Vector3f up = new Vector3f(0.0f,1.0f,0.0f);

Vector3f dir = new Vector3f(0.0f,0.0f,-1.0f);

// 将摄像机移到正确位置和方向

cam .setFrame(loc, left, up, dir);

// 我们改变自己的摄像机位置和视锥的标志

cam .update();

// 获取一个高分辨率用于 FPS 更新

timer = Timer. getTimer ();

display .getRenderer().setCamera( cam );

KeyBindingManager. getKeyBindingManager ().set(

"exit" ,

KeyInput. KEY_ESCAPE

);

}

/*

* 如果分辨率改变将被调用

*/

protected void reinit() {

display .recreateWindow( width , height , depth , freq , fullscreen );

}

/*

* 绘制场景图

*/

protected void render( float interpolation) {

// 清除屏幕

display .getRenderer().clearBuffers();

display .getRenderer().draw( scene );

}

/*

* 在 update 期间,我们只需寻找 Escape 按钮

* 并更新 timer 去获取帧率

*/

protected void update( float interpolation) {

// 更新 timer 去获取帧率

timer .update();

interpolation = timer .getTimePerFrame();

// 当 Escape 被按下时,我们退出游戏

if (KeyBindingManager. getKeyBindingManager ()

.isValidCommand( "exit" )

){

finished = true ;

}

}

}

查看更多关于jMonkeyEngine译文FlagRush2从你的应用程序中移除SimpleGam的详细内容...

  阅读:44次