注:本系列教程全部翻譯完之后可能會以 PDF 的形式發(fā)布。 如果有什么錯誤可以留言或 EMAIL : kakashi9bi@gmail.com 給我。 jME 版本 : jME_2.0.1_Stable 開發(fā)工具: MyEclipse8.5 操作系統(tǒng): Window7/Vista 這個向?qū)е?,我們將?Flag Rush 構(gòu)建基
注:本系列教程全部翻譯完之后可能會以PDF的形式發(fā)布。
如果有什么錯誤可以留言或EMAIL:kakashi9bi@gmail.com給我。
jME版本:jME_2.0.1_Stable
開發(fā)工具:MyEclipse8.5
操作系統(tǒng):Window7/Vista
這個向?qū)е?,我們將為Flag Rush構(gòu)建基礎(chǔ)。我們將通過自己實現(xiàn)繼承BaseGame。我們將使用BaseGame做為父類,但之后可能改為其它的游戲類型,因為BaseGame簡單地盡可能快地進行update和render。我們或許不必或不想使用這種類型的循環(huán)。然而,現(xiàn)在BaseGame是一個循環(huán)無關(guān)的類。在以后,改變BaseGame將不是重點,因為只是傳入update和render方法的值不同而已。
我們將開始創(chuàng)建一個繼承自BaseGame的新類。你會注意到有6個需要實現(xiàn)的方法:update、render、initSystem、initGame和reinit?,F(xiàn)在,只需要為它們創(chuàng)建一個存根方法,我們將在后面將自己的邏輯填充進去。
import com.jme.app.BaseGame;
publicclass Lesson2 extends BaseGame{
publicstaticvoid main(String[] args) {
}
protectedvoid cleanup() {
}
protectedvoid initGame() {
}
protectedvoid initSystem() {
}
protectedvoid reinit() {
}
protectedvoid render(float arg0) {
}
protectedvoid update(float arg0) {
}
}
那么,讓我們從最初開始。我們在這里將再次創(chuàng)建main方法。它很像前一個向?qū)У膍ain方法,除了一個關(guān)鍵的地方不同。這次我們將顯示FlagRush的迷人的新logo。AbstractGame定義了一對setConfigShowMode方法,其中的一個接受一個URL類用于加載Image。因此,我們將加載FlagRush.png(迷人的logo)并把它傳給這個方法?,F(xiàn)在,當PropertiesDialog被顯示時,它將顯示新的Logo。
publicstaticvoid 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();
}
現(xiàn)在,當PropertiesDialog出現(xiàn)時,它將像下面這個一樣(你應(yīng)該在項目中新建一個package——jmetest.data.images,然后里面有一張叫FlagRush.png的圖片):
現(xiàn)在,你能運行你的應(yīng)用程序,但它僅僅是顯示PropertiesDialog,除此之外不會做更多的工作。我們下一步將實現(xiàn)initSystem方法。這個方法在進入主循環(huán)之前由BaseGame調(diào)用。這正是我們設(shè)置window和display的地方。我們將保存width,height,depth,frequency和fullscreen標志。我們將在后面使用這些值,假如用戶想改變分辨率的時候。所以,首先,讓我們創(chuàng)建變量去保存這些值:
publicclass Lesson2 extends BaseGame{
privateintwidth,height;
privateintfreq,depth;
privatebooleanfullscreen;
……………………….
我們也需要在我們的程序中保存Camera,所以我們也應(yīng)該為那創(chuàng)建一個變量。
//我們的camera對象,用于觀看scene
private Camera cam;
最后將初始化的一項是Timer,Timer將允許我們獲取我們的幀率。所以,同樣的,這將是一個實例變量。
protected Timer timer;
現(xiàn)在我們已經(jīng)準備好我們的實例變量,并且我們將在initSystem中初始化它們。
protectedvoid 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);
}
//設(shè)置背景為黑色
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創(chuàng)建的)獲取的值。通過保存這些值,當用戶以后從系統(tǒng)菜單改變屏幕設(shè)置的時候,我們可以很容易地修改它們中的一個或全部值。
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,并通過先前獲得的屏幕參數(shù)創(chuàng)建一個本地窗口。我們接著使用DisplaySystem去創(chuàng)建一個Camera對象。你將注意到那用一個try/catch塊包圍。如果我們嘗試創(chuàng)建一個系統(tǒng)沒能力繪制的窗口,異常將在這里出現(xiàn)。目前,它只會退出,但之后,我們將讓這個顯示得更友好,并通知用戶。
//設(shè)置背景為黑色
display.getRenderer().setBackgroundColor(ColorRGBA.black);
我們接著設(shè)置了窗口的背景顏色。當沒有其它數(shù)據(jù)被渲染的時候,這是顯示的默認顏色。我選擇黑色,這是因為它和我們后面將使用的任何文本形成鮮明的對比。不管怎樣,這都不是重點,因為當一切正常工作時,屏幕上通常覆蓋其它的數(shù)據(jù)。
//初始化攝像機
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);
下一步,我設(shè)置了camera。我想要一個標準的camera,正常情況下是右手坐標系統(tǒng)(向上是正Y,向右是正X和向屏幕里面是-Z)。我同時設(shè)置了透視圖為45度視角。這個是大多數(shù)游戲里面的公認標準,而它將應(yīng)用于Flag Rush。在camera數(shù)據(jù)設(shè)置之后,我們調(diào)用update,這將設(shè)置所有的OpenGL組件,例如視點(下文以ViewPort代替)和Frustum。
//獲取一個高分辨率用于FPS更新
timer = Timer.getTimer();
這里只是初始化Timer,從本地Timer獲?。ɡ鏛WJGLTimer)。
KeyBindingManager.getKeyBindingManager().set(
"exit",
KeyInput.KEY_ESCAPE
);
最后,我們創(chuàng)建一個新的InputSystem,將它綁定到我們的KeyBindingManager并設(shè)置一個輸入行為(Input action)。在這個框架中我們只關(guān)心一個按鍵——Escape。在這個例子中,我們設(shè)置action“exit”給Escape鍵。KeyBindingManager是一個單例類,它使用單一的get調(diào)用,關(guān)注了所有InputSystem組件的初始化。
現(xiàn)在,如果你運行系統(tǒng)你將真正獲得一個屏幕顯示。它將充滿黑色(我們設(shè)置的背景顏色),沒有任何東西。
現(xiàn)在,我們擁有一個窗口和OpenGL上下文環(huán)境,我們將加載我們的游戲數(shù)據(jù)(如上面前個向?qū)У腟phere)
protectedvoid initGame() {
scene = new Node("Scene Graph Node");
//創(chuàng)建我們的球體
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();
}
我們現(xiàn)在保存我們自己的Scene Graph結(jié)點,我已經(jīng)選擇把它命名為scene,但實際上怎樣命名都是沒關(guān)系。因為這是scene的根節(jié)點,它也是一個實例變量而它和其他實例變量一樣被聲明:
private Node scene;
這個Node接著被實例化。接著我們創(chuàng)建了一個Sphere和TextureState(就像上一個的向?qū)В?。Sphere接著被attach到scene。這個看起來將和我們上一個向?qū)龅暮芟嗨?。然而,現(xiàn)在,我們還調(diào)用updateGeometricState和updateRenderState。這些方法為SceneGraph updates調(diào)用。updateGeometricState是必須的,不管場景圖(Scene Graph)結(jié)構(gòu)在何時改變(設(shè)置一個新的,改變另一個的參數(shù),等等),在我們的例子中,我增加了一個sphere到到scene。不管RenderState在什么時候以何種方式發(fā)生改變,updateRenderState都應(yīng)該被調(diào)用(比如創(chuàng)建一個新的RenderState、改變它的參數(shù)等等),在我們的例子中,我們增加了TextureState。
我們現(xiàn)在擁有游戲中的數(shù)據(jù),但它仍然沒被渲染到屏幕。
既然我們已經(jīng)初始化了窗口并加載了數(shù)據(jù),如果能看到它將更好。那就是render方法的到來。BaseGame調(diào)用update并根據(jù)它的能力盡可能快地render。render的調(diào)用需要處理所有繪畫調(diào)用,而update應(yīng)該處理任何的游戲邏輯。在我們的例子中,我們想要update做一點游戲邏輯,退出游戲。為了簡單退出游戲,我們將設(shè)置finished布爾值為true。
/*
* 在update期間,我們只需尋找Escape按鈕
* 并更新timer去獲取幀率
*/
protectedvoid update(float interpolation) {
//更新timer去獲取幀率
timer.update();
interpolation = timer.getTimePerFrame();
//當Escape被按下時,我們退出游戲
if(KeyBindingManager.getKeyBindingManager()
.isValidCommand("exit")
){
finished = true;
}
}
你也將注意到update獲取最新的timer讀數(shù)并為此設(shè)置插值(interpolation)。BaseGame通常在調(diào)用update時發(fā)送-1,所以我們將繼續(xù)并重用這個值去保存每幀真正的時間。
接下來,我們將渲染。
/*
* 繪制場景圖
*/
protectedvoid render(float interpolation) {
//清除屏幕
display.getRenderer().clearBuffers();
display.getRenderer().draw(scene);
}
這個直截了當。我們使用clearBuffers清除屏幕。我們接著畫了scene,這是包含我們Sphere的樹。
你現(xiàn)在能運行程序并看到:
正是和前一課的顯示一樣,只不過沒了燈光。
最后我們將覆蓋的2個方法是reinit和cleanup。當窗口需要重建時,Reinit應(yīng)該被調(diào)用,就像參數(shù)發(fā)生了變化。而在關(guān)閉的時候調(diào)用cleanup。
/*
* 如果分辨率改變將被調(diào)用
*/
protectedvoid reinit() {
display.recreateWindow(width, height, depth, freq, fullscreen);
}
我們在這里所做的就只是傳遞新的值給DisplaySystem處理。僅此而已。
/*
* 清除texture
*/
protectedvoid cleanup() {
ts.deleteAll();
}
這簡單確保了texture被刪除。這不是特別必須的,因為OpenGL在它退出時將處理這個。但“寧可事先謹慎有余,切莫事后追悔莫及”。
很好,就是那樣。我們現(xiàn)在有一個很基本、可工作的框架。通過創(chuàng)建我們自己的應(yīng)用程序類型,我們能完全保持對我們場景中一切的控制。隨著向?qū)У睦^續(xù),我們將很明確地增強并構(gòu)建基于這個類的程序。
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;
publicclass Lesson2 extends BaseGame{
privateintwidth,height;
privateintfreq,depth;
privatebooleanfullscreen;
//我們的camera對象,用于觀看scene
private Camera cam;
protected Timer timer;
private Node scene;
private TextureState ts;
publicstaticvoid 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
*/
protectedvoid cleanup() {
ts.deleteAll();
}
protectedvoid initGame() {
scene = new Node("Scene Graph Node");
//創(chuàng)建我們的球體
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();
}
protectedvoid 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);
}
//設(shè)置背景為黑色
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
);
}
/*
* 如果分辨率改變將被調(diào)用
*/
protectedvoid reinit() {
display.recreateWindow(width, height, depth, freq, fullscreen);
}
/*
* 繪制場景圖
*/
protectedvoid render(float interpolation) {
//清除屏幕
display.getRenderer().clearBuffers();
display.getRenderer().draw(scene);
}
/*
* 在update期間,我們只需尋找Escape按鈕
* 并更新timer去獲取幀率
*/
protectedvoid update(float interpolation) {
//更新timer去獲取幀率
timer.update();
interpolation = timer.getTimePerFrame();
//當Escape被按下時,我們退出游戲
if(KeyBindingManager.getKeyBindingManager()
.isValidCommand("exit")
){
finished = true;
}
}
}
聲明:本網(wǎng)頁內(nèi)容旨在傳播知識,若有侵權(quán)等問題請及時與本網(wǎng)聯(lián)系,我們將在第一時間刪除處理。TEL:177 7030 7066 E-MAIL:11247931@qq.com