`
peigen
  • 浏览: 75988 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

Flag Rush Tutorial-2.用你的程序替代SimpleGame

    博客分类:
  • JME2
阅读更多

Moving away from SimpleGame with your own App


用你的程序替代SimpleGame

 

请浏览:http://peigen.info/2010/1/29/Flag-Rush-Tutorial-2/

 

 

This tutorial, we are going to build the foundation for Flag Rush. We are going to extend BaseGame with our own implementation. We are going to use BaseGame as the super class, but may change to another game type later on as BaseGame simply renders and updates as fast as possible. We probably won't need or want this type of loop. However, for now, BaseGame is a no-fuss loop. In the future, switching will be trivial, the only difference being the value passed in the update and render methods.

We will begin by creating a new class that extends BaseGame . You will notice there are six methods that require implementing: update, render, initSystem, initGame and reinit. For now, just create a stub method for them and we will fill in the logic as we go on.

 

在这份指南里,我们继续创建基础的Flag Rush.我们将要扩展BaseGame用我们的实现方式.我们继BaseGame,当你想要改变其他游戏类型直接改变BaseGame简单渲染和更新是 很快的.我们也许不需要这个类型循环.然而,现在,我先创建一个类继承自BaseGame.你需要注意的是这里有6哥方法必须实现update(), render(), initSystem(), initGame() and reinit().现在先为它们创建子方法然后我们 把逻辑填充进去.

 

public class Lesson2 extends BaseGame {
	
	public static void main(String[] args) {
	}
 
	protected void update(float interpolation) {
	}
 
	protected void render(float interpolation) {
	}
 
	protected void initSystem() {
	}
 
	protected void initGame() {
	}
 
	protected void reinit() {
	}
 
	protected void cleanup() {
	}
}
 

Main

So, let's start at the beginning. We are again going to create the main method here. It will be just like the previous tutorial's main, except for one key difference. This time, we will be displaying Flag Rush's amazing new logo. AbstractGame defines a couple setDialogBehaviour methods, one of which takes a URL class for loading an image. Therefore, we will load the FlagRush.png file (the amazing logo) and pass it to this method. Now, when the PropertiesDialog is shown, it will display the new logo.

 

因此,让我们先开始。我们再次要建立main()方法,就跟上一个指南的main()方法一样,除了一个字不一样.这一次,我们将要显示Flag Rush令人惊奇的新logo.AbstractGame定义了一对setDialogBehaviour()方法(译者注:我在 AbstractGame.java里面并没有找到 setDialogBehaviour()方法,也许版本不对吧,文档没有跟上.....) ,一个是需要读取图像的URL 类作为参数的.因此,我们读取FlagRush.png图片文件(就是那个logo)使用这个方法.现在当PropertiesDialog显示的时候,新的logo也显示出来了.

 

/**
* Main entry point of the application
*/
public static void main(String[] args) {
 Lesson2 app = new Lesson2();
 //We will load our own "fantastic" Flag Rush logo. Yes, I'm an artist.
 app.setConfigShowMode(ConfigShowMode.AlwaysShow, 
 Lesson2.class.getResource("/res/FlagRush.png"));
 app.start();
}
 

Now, when the PropertiesDialog appears it should appear similar to the following:

 

现在,当PropertiesDialog显示的时候出现了一个类似下面的样子的东东:

 

Init System

初始化系统

 

Now you can run your application, but it won't do much after displaying the dialog. We will want to implement the initSystem method next. This method is called before entering the main loop by BaseGame . This is where we want to set up the window and the display. We are going to want to store the width, height, depth, frequency, and fullscreen flag. We will use this values in the future, in case the user wants to change the resolution. So, first, let's create the variables to hold these values:

 

现在你可以运行你的程序了,但是在显示以上对话框后没有其他的行为.下面我们实现initSystem()方法.这个方法在BaseGame的游戏 主循环之前.这就是我们要建立的窗口和display.我们将要存储的宽度,高度,深度,频率,和全屏标帜.我们将会用到上面的值,在本例中将要改变分辨 率.首先,创建变亮以保留这些值:

 

public class Lesson2 extends BaseGame {
 //display attributes for the window. We will keep these values
 //to allow the user to change them
 private int width, height, depth, freq;
 private boolean fullscreen;
 //...
 

We are also going to maintain our own Camera in our application, so we should create a variable for that as well.

 

我们将要使用Camera在我们的程序里,所以我们为Camera创建一个对象.

 

//Our camera object for viewing the scene
private Camera cam;
 

The last items that will be initialized is the Timer . The timer will allow us to obtain our frame rate. So, again, this will be an instance variable.

最后一个组建就是初始化Timer类,用timer类我们可以获取我们的帧率,创建它:

protected Timer timer;
 

Now, we have our instance variables ready, and we will initialize them in the initSystem method.

 

现在,我们已经定义好我们的变量了,接下来我们在initSystem()方法里初始化它们

 

       /**
	* initializes the display and camera.
	* 
	* @see com.jme.app.SimpleGame#initSystem()
	*/
	protected void initSystem() {
	//store the properties information
	width = this.settings.getWidth();
	height = this.settings.getHeight();
	depth = this.settings.getDepth();
	freq = this.settings.getFrequency();
	fullscreen = this.settings.isFullscreen();
 
	try {
	display = DisplaySystem.getDisplaySystem(this.getNewSettings().getRenderer());
	display.createWindow(width, height, depth, freq, fullscreen);
 
	cam = display.getRenderer().createCamera(width, height);
	} catch (JmeException e) {
	e.printStackTrace();
	System.exit(1);
	}
 
	//set the background to black
	display.getRenderer().setBackgroundColor(ColorRGBA.black);
 
	//initialize the camera
	cam.setFrustumPerspective(45.0f, (float)width / (float)height, 1, 1000);
	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, 0f, -1.0f);
	// Move our camera to a correct place and orientation.
	cam.setFrame(loc, left, up, dir);
	/** Signal that we've changed our camera's location/frustum. */
	cam.update();
 
	/** Get a high resolution timer for FPS updates. */
 timer = Timer.getTimer();
 
	display.getRenderer().setCamera(cam);
 
	KeyBindingManager.getKeyBindingManager().set("exit",
	KeyInput.KEY_ESCAPE);
	}
 

This is a large method, so, I'll go through it bit by bit.

这是个大方法,我们一点点来看.

 

//store the properties information
width = this.settings.getWidth();
height = this.settings.getHeight();
depth = this.settings.getDepth();
freq = this.settings.getFrequency();
fullscreen = this.settings.isFullscreen();
 

First, we store the values obtained from the settings object (settings is built by AbstractGame ). By storing these values we will be able to easily alter one or all of them in the future when the user switches screen settings from a menu system.

 

首先,我们从settings对象(settings在AbstractGame被创建)里获取这些变量.这样做的好处是,当用户从菜单里改变屏幕设置的时候,我们可以很方便的改变它们的值.

 

try {
 display = DisplaySystem.getDisplaySystem(this.settings.getRenderer());
 display.createWindow(width, height, depth, freq, fullscreen);
 
 cam = display.getRenderer().createCamera(width, height);
} catch (JmeException e) {
 e.printStackTrace();
 System.exit(1);
}
 

Next, we obtain a new DisplaySystem and create a native window with our screen parameters we obtained earlier. We then use the DisplaySystem to create a Camera object. You'll notice that it is wrapped in a try/catch block. If we try to create a window that the system is not capable of displaying, it will be shown here. Currently, it will just exit, but later on we'll enhance this to play nice, and notify the user.

 

接下来,我们很容易获得DisplaySystem实例,又用我们的上面的参数创建了一个本地窗口.然后用DisplaySystem创建一个 Camera对象.注意:用try/catch块包裹一下哦.如果我们尝试建立一个窗口,该系统不能够显示,将在这里显示异常。目前,它只会退出,但后来 我们会加强这方面,并做出用户提示.

 

//set the background to black 设置背景为黑色
display.getRenderer().setBackgroundColor(ColorRGBA.black);
 

We then set the background color of the window. This is the default color that is displayed when no other data is rendered. I chose black because it tends to show a nice contrast for any text we might be displaying in the future. However, this is a moot point, because when things are working properly, there will always be some data covering the screen.

 

我们设置窗口的背景色.在没有提供其他选择的时候这是默认的渲染颜色.这里之所以用黑色是因为黑色是很容易跟其他的颜色作对比的颜色.然而,这是个待商榷的点,如果一切正常的话,它会被其他什么东西遮盖住的.

 

//initialize the camera
cam.setFrustumPerspective(45.0f, (float)width / (float)height, 1, 1000);
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, 0f, -1.0f);
// Move our camera to a correct place and orientation.
cam.setFrame(loc, left, up, dir);
/** Signal that we've changed our camera's location/frustum. */
cam.update();
display.getRenderer().setCamera(cam);
 

Next, I set up the camera. I want a standard camera, with the typical right handed coordinate system (Up is +Y, Right is +X, and into the screen is -Z). I also set up the perspective to be a 45 degree viewing angle. This is fairly standard in most games, and will work for Flag Rush. After the camera's data is set, we call update this will set up all the OpenGL components, such as the ViewPort and Frustum .

 

接下来,我设置camera.我需要一个标准camera,等同于典型右手习惯(上是+Y轴,右是+X轴,-z是深度).也可以设置45°视角,这 跟很多游戏一样,为Flag Rush做这样的设置.之后视频数据被设置好了,我们调用update就会设置OpenGl组件,就像ViewPort和Frustum.

 

/** Get a high resolution timer for FPS updates. */
timer = Timer.getTimer();
 

Here the timer is just initialized, obtaining the native timer (for example LWJGLTimer ).

这里timer被初始化了,获得一个本地时间(例如:LWJGL Timer).

KeyBindingManager.getKeyBindingManager().set("exit", KeyInput.KEY_ESCAPE);
 

Lastly, we create a new InputSystem, bind it to our KeyBindingManager and set up an input action. We only care about one key for this framework, and that is Escape. In this case, we set the action “exit” to the escape key. KeyBindingManager is a singleton class that will take care of initializing all the InputSystem components with the single get call.

Now, if you run the system you will actually get a window displayed. It will be filled with black (the background color we set) and nothing else.

 

最后,我们创建InputSystem,绑定到我们的KeyBindingManager上,然后设置一个input动作.这个例子里我们只关心一 个键,既Esc键.我们设置一个退出动作到Esc键上.KeyBindingManager是一个单例模式类,它初始化所以InputSystem组件.

现在,如果你运行你的程序,你会看到一个窗口显示出来.它会被黑色填充(因为我们设置的背景色是黑色),就没有其他东西了.

Init Game

Now that we have a window and an OpenGL context, we will load our game data (a Sphere just like last tutorial).

We need to add this attibute to the class:

 

现在,我们有一个窗口和一个OpenGL context(上下文),我们将要读取我们的游戏数据(一个球,就像上一个指南里的一样).

 

        // the root node of the scene graph
	private Node scene;
	// TextureState to show the monkey on the sphere.
	private TextureState ts;
 
/**
* initializes the scene
*/
protected void initGame() {
 scene = new Node("Scene graph node");
 
 //Create our Sphere
 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/Monkey.jpg"),
 Texture.MinificationFilter.BilinearNearestMipMap, 
 Texture.MagnificationFilter.Bilinear));
 
 s.setRenderState(ts);
 
 scene.attachChild(s);
 
 //update the scene graph for rendering
 scene.updateGeometricState(0.0f, true);
 scene.updateRenderState();
}
 

We now maintain our own Scene Graph root, I've chosen to name it scene but it really doesn't matter. Because this is the root node of the scene, it is also an instance variable and was declared along with the other instance variables:

 

现在我们维持我们自己的Scene Graph根节点,我定义了节点的名字但是这并不是必要的.因为这是场景上的根节点,这是一个实例化变量的用法.

 

private Node scene;
 

This Node is then instantiated. Which we then create a Sphere and TextureState (just like last tutorial). The Sphere is then attached to scene. This should look very familiar as we did the same thing last tutorial. However, now, we also call updateGeometricState and updateRenderState. These methods are called for Scene Graph Updates . updateGeometricState is required whenever the Scene Graph structure has changed (add a node, remove a node, changes to a nodes translation, etc) in our case we added a Sphere to the scene. While updateRenderState should be called whenever a RenderState has been changed in some way (setting a new one, changing parameters on another, etc), in our case, we added the TextureState .

We now have data in our application, but it's still not being rendered to the screen.

 

这个Node之后再说.下来我们创建哪个?Sphere或TextureState(就像上一个指南那样).把Sphere附给场景.这看起来很熟 悉就像我们之前做的那样.然而,现在,我们调用updateGeometricState 和 updateRenderState.这俩方法被Scene Graph Updates调用.当Scene Graph的结果改变的时候(添加一个节点,删除一个节点,改变节点等等)updateGeometricState必须被调用,在我们的例子里我们给场 景增加了一个Sphere.每当RenderState被改变的时候(设置一个新的变量或者改变参数的时候,等等)updateRenderState应 该被调用.在我们的例子里,我们添加了TextureState.

现在 我们的程序有了数据,但是并没有渲染给到屏幕上.

Render and Update 渲染和更新

Now that we have a window initialized and data loaded, it would be nice to be able to see it. That's where the render method comes in. BaseGame calls update and then render as fast as it is capable. A call to render should handle all drawing calls, while update should do any game logic. In our case, we want update to do one bit of game logic, exit the game. In order to cleanly exit the game we will set the finished boolean to true.

现在我们的窗口已经初始化好啦数据也读取好啦,看起来也不错.这就是渲染方法的使用.就我们而言,我们希望做一个更新的游戏逻辑位,退出了比赛。为了干净地退出比赛,我们将设置完成布尔为true。

 

/**
* During an update we only look for the escape button and update the timer
* to get the framerate.
*/
protected void update(float interpolation) {
 //update the time to get the framerate
 timer.update();
 interpolation = timer.getTimePerFrame();
 //if escape was pressed, we exit
 if (KeyBindingManager.getKeyBindingManager().isValidCommand("exit")) {
 finished = true;
 }
}
 

You'll also notice that update gets the latest timer reading and sets the interpolation value to this. BaseGame always sends -1 on the update call, so we will go ahead and reuse this value to hold the real time per frame.

Next, we will render.

 

你应该已经注意到了update方法获得最后的timer来改变timer的值.BaseGame值调用update方法的时候总是发送 -1 ,所以利用此值来保存每帧的实时性。

下来,我们开始渲染.

 


/**
* draws the scene graph
*/
protected void render(float interpolation) {
 //Clear the screen
 display.getRenderer().clearBuffers();
 
 display.getRenderer().draw(scene);
}
 

This is straight forward. We clear the screen with clearBuffers. 1) We then draw the scene which is the tree containing our Sphere .

You can now run the application and you should see:

看起来很简单.用clearBuffers方法来清屏.1)这时画出场景图就包含Sphere了.

现在运行程序将会看到如下图所示:

 

 

 

Which is the exact same display as the previous lesson without the lighting and statistics displayed.

和上一篇一样的显示,只是没有了灯光和统计数据.

reinit and cleanup  再初始化和清理

The last two methods we are going to override are reinit and cleanup. reinit should be called when the window needs rebuilding, such as the parameters changing. While cleanup is called during shutdown.

最后我们将要覆盖reinit()和cleanup()俩方法.当窗口需要重建的时候reinit()被调用,比如像参数变更的时候.当cleanup被调用的时候就shutdown.

/**
* will be called if the resolution changes
* 
*/
protected void reinit() {
 display.recreateWindow(width, height, depth, freq, fullscreen);
}
 

All we do here is pass the new values to the DisplaySystem for handling. Nothing more.

我们把所以的东西都扔给DisplaySystem处理,仅此而已.

/**
* clean up the textures.
* 
*/
protected void cleanup() {
 ts.deleteAll();
}
 

This simple insures that the textures are deleted. This is not particularly needed, as OpenGL will take care of it while it is going down. “Better safe than sorry”.

这是个简单的确保贴图被删掉的方法.这也不是特别需要,像OpenGL会很小心的对待的."安全点比说sorry强"

Summary汇总

Well, that's it. We now have a very basic framework from which to work off of. By creating our own application type we can maintain complete control of everything in our scene. We will definately be enhancing and building upon this class as the tutorials run forward.

1) Not calling this would cause the next frame to be rendered on top of the current frame. This can sometimes be a desired effect.

好啦,告一段落.我们现在有一个很基本的框架,以开展工作.通过建立我们自己的应用程序的类型,我们可以保持在场景图中完全控制一切。随着本教程的推进,会加强和建设这个类。
1)这并不会导致下一个帧是在当前帧的顶部渲染。这有时是一个预期的效果。

 

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics