现在的位置: 首页 > 综合 > 正文

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

2014年09月05日 ⁄ 综合 ⁄ 共 11972字 ⁄ 字号 评论关闭

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

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

 

jME版本 jME_2.0.1_Stable

开发工具:MyEclipse8.5

操作系统:Window7/Vista

 

 

 

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

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

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.1Main

那么,让我们从最初开始。我们在这里将再次创建main方法。它很像前一个向导的main方法,除了一个关键的地方不同。这次我们将显示FlagRush的迷人的新logoAbstractGame定义了一对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.2InitSystem

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

 

public class Lesson2 extends BaseGame{

    private int width,height;

    private int freq,depth;

private boolean fullscreen;

……………………….

 

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

 

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

    private Camera cam;

 

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

 

    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。在这个例子中,我们设置actionexit”给Escape键。KeyBindingManager是一个单例类,它使用单一的get调用,关注了所有InputSystem组件的初始化。

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

 

2.3InitGame

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

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

 

2.4Renderupdate

既然我们已经初始化了窗口并加载了数据,如果能看到它将更好。那就是render方法的到来。BaseGame调用update并根据它的能力尽可能快地renderrender的调用需要处理所有绘画调用,而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.5reinitcleanup

最后我们将覆盖的2个方法是reinitcleanup。当窗口需要重建时,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;

       }

    }

 

}

抱歉!评论已关闭.