http://magicbird.iteye.com/blog/1578367 jpct-ae開發3D賽車游戲博客分類:
jpct-ae游戲引擎的資料比較少,本人是在官網中helloworld程序的基礎上進行編寫的。
- 首先,先說一下游戲的框架模塊。游戲包括渲染模塊(場景渲染、賽車渲染和效果渲染)、游戲邏輯模塊(碰撞檢測和重力感應計算)、音效模塊和數據模塊四部分。在JPCT-AE游戲引擎的基礎上,通過將3DMAX制作的賽道和賽車模型文件導入到游戲中,同時添加碰撞渲染效果,完成游戲的渲染模塊;通過保持賽車位置不動,而移動賽道完成賽車的行進;通過Android中內置加速度傳感器來計算手機的重力感應,從而控制賽車的左右移動;通過碰撞檢測線程完成賽車越界(賽道)、賽車碰撞障礙物及賽車到達終點線終止;通過其它線程完成游戲開始倒計時、速度儀表盤轉動等;數據模塊存儲程序的全局常量;音效模塊實現了碰撞音效和游戲背景音樂。
- 以下,直接摘自我的畢設論文了啊。。
4.1.2 JPCT-AE加載賽車和賽道模型及添加紋理
JPCT-AE加載賽車和賽道模型:首先通過Autodesk 3ds Max 軟件對賽車和賽道模型進行建模,生成三維文件的格式,然后通過JPCT-AE引擎導入Assert文件夾下的obj和3ds文件,來加載賽車和賽道Object3D模型到World中。最后,World中的所有對象會被繪制到FrameBuffer中,完成模型的加載。
加載賽車obj格式文件(carbody.obj):其中mergeAll()是合並數組中的所有對象成一個大的對象。loadOBJ()是加載OBJ格式的文件到一個對象數組中。
Object3D.mergeAll(Loader.loadOBJ(getResources().getAssets().open("carbody.obj"), null, scale));
加載賽道3ds格式文件(straightroad.3ds):與載入MD2格式不同的是,如果載入一個3ds文件它是沒有動畫效果的。其中load3DS()是將3DS格式的文件加載到一個對象數組中。3ds是一個支持很多轉換工具的3D-Studio格式。
Loader.load3DS(getAssets().open("straightroad.3ds"), scale);
同理,加載游戲場景中的其它模型。下面是繪制小車的代碼:
try {
carBody = Object3D.mergeAll(Loader.loadOBJ(getResources()
.getAssets().open("carbody.obj"), null, 0.4f));
} catch (IOException e) {
e.printStackTrace();
}
carBody.calcTextureWrapSpherical();
carBody.setTexture("texture");
carBody.rotateX((float) Math.PI);
carBody.build();
world.addObject(carBody);
carBody.strip();
JPCT-AE添加紋理:為了使賽車和賽道顯得更加逼真,需要增添光照、紋理,並放置在屏幕中合適的位置。
紋理定義了物體表面的結構,如花紋,圖案,皺紋等等。有了紋理,模型世界才會更加豐富多彩。紋理實際上是一個二維數組,其元素是一些顏色值,每一元素稱之為紋理像素。紋理對象是一個內部數據類型,存儲着紋理數據。
通常一個紋理映射的步驟是:首先創建紋理對象。就是獲得一個新的紋理句柄ID。其次指定紋理。就是將數據賦值給ID的紋理對象,在這一步,圖像數據正式加載到了ID的紋理對象中。之后綁定紋理對象。就是將ID的紋理作為下面操作的紋理。最后紋理映射。將已綁定紋理的數據繪制到屏幕上去,在這一步,就能看到貼圖的效果了。我們要將一個圖像的一部分繪制到屏幕上,稱之為紋理映射,就是將圖像根據上述坐標系計算出要繪制的部分的各個點的紋理坐標,然后一一對應到屏幕上的坐標中去。
TextureManager tm = TextureManager.getInstance();
Texture roadTexture = new Texture(BitmapHelper.rescale( BitmapHelper.convert(rs.getDrawable( R.drawable.road)), 64,64));
tm.addTexture("roadTexture",roadTexture);
straightRoad.setTexture("roadTexture");
4.1.4 JPCT-AE實現賽車行進及賽車轉彎
賽車行進:本系統采用的方法是保持車的相對位置不變,通過使賽道相對於賽車向后移動來實現賽車在賽道上向前行進的效果。
在JPCT-AE中,translate(float, float, float)-com.threed.jpct.Object3D類中的方法:在世界空間中通過修改平移矩陣平移(“移動”)對象。
straightRoad.translate(0, 0.1f * SPEED, -0.241421393f * SPEED);其中SPEED的值不同,賽車行進速度也不同。
2) 賽車轉彎:賽車轉彎一共需要兩步完成。第一步,保持車身位置不動,只是向左右轉頭,通過賽車繞Y軸旋轉w角度,達到車頭左右轉的效果,需要配合重力感應檢測來逆時針或順時針旋轉車頭。
在JPCT-AE中,rotateY(float)- com.threed.jpct.Object3D類中的方法:用給定的w角度繞y軸旋轉對象的旋轉矩陣。(弧度,順時針為正值)。
carBody.rotateY(touchTurn)
第二步,保持車的左轉右轉狀態,利用平移方法來向左向右平移車身。
在JPCT-AE中,translate(float, float, float)-com.threed.jpct.Object3D類中的方法:在世界空間中通過修改平移矩陣平移(“移動”)對象。
carBody.translate(vector)
4.1.5 JPCT-AE繪制障礙物
第一步,即通過Object3D o = Primitives.getCube(4f)實現障礙物的生成。Primitives - com.threed.jpct中的Primitives提供了一些基元(基本3D-對象)。包括返回盒子Box,圓錐體Cone,立方體Cube,圓柱Cylinder,雙錐DoubleCone,橢球Ellipsoid,平面Plane,棱錐Pyramide,球體Sphere等。
第二步,將障礙物是作為子對象加到賽道中,使障礙物可以作為賽道的一部分,隨賽道一起做平移運動。這一步是必需的。straightRoad.addChild(o)
Object3D o = Primitives.getCube(4f);
o.setAdditionalColor(new RGBColor(0, 255, 0));
for (int i = 0; i < obstacles.length; i++) {
obstacles[i] = o.cloneObject();
obstacles[i].setOrigin(OBSTACLES_VECTOR[i]);
obstacles[i].build();
straightRoad.addChild(obstacles[i]);
world.addObject(obstacles[i]);
obstacles[i].strip();
}
4.1.6 JPCT-AE實現碰撞檢測
通過添加線程來實現碰撞檢測,如果不添加線程來實現,則很可能會阻塞主線程,導致Android中ANR異常等。
通過判斷賽車的x軸方向的坐標是否超過賽道的邊界來判斷是否越界;通過計算障礙物的y坐標和障礙物與賽車x坐標的差值,來判斷賽車是否撞上障礙物。
- public class CheckObstacleThread extends Thread {
- public void run() {
- while (flag) {
- try {
- Thread.sleep(100);
- } catch (Exception e) {
- e.printStackTrace();
- }
- if (HelloWorld.obstacles[i].getTransformedCenter().y > 2) {
- if(Math.abs(HelloWorld.carVector.x-HelloWorld.obstacles[i].getTransformedCenter().x)<10){
- HelloWorld.obstacles[i].setAdditionalColor(new RGBColor(255, 0, 0));
- SCORE -= 20;
- }
- i++;
- }
- if(i==30){
- flag = false;
- }
- }
- }
- }
public class CheckObstacleThread extends Thread {
public void run() {
while (flag) {
try {
Thread.sleep(100);
} catch (Exception e) {
e.printStackTrace();
}
if (HelloWorld.obstacles[i].getTransformedCenter().y > 2) {
if(Math.abs(HelloWorld.carVector.x-HelloWorld.obstacles[i].getTransformedCenter().x)<10){
HelloWorld.obstacles[i].setAdditionalColor(new RGBColor(255, 0, 0));
SCORE -= 20;
}
i++;
}
if(i==30){
flag = false;
}
}
}
}
4.1.6 JPCT-AE實現碰撞效果(爆炸)
通過設置3D對象的廣告板模式,使對象總是面向攝像機,來實現碰撞效果。在JPCT-AE中setBillboarding(boolean)- com.threed.jpct.Object3D類中的方法,啟用/禁用此對象的billboarding模式。
Object3D collision = Primitives.getPlane(1, 10);
collision.setBillboarding(true);
collision.setTexture("collision");
collision.setTransparency(20);
4.1.7 JPCT-AE繪制起止線
通過建立公共DrawLine類,提供創建直線的通用方法createLine()實現繪制起止線。算法中通過JPCT-AE提供的addTriangle(SimpleVector, SimpleVector, SimpleVector, TextureInfo)- com.threed.jpct.Object3D類中的方法,添加一個三角形對象到此對象中,來添加8個三角形到直線對象中,從而繪制成直線。
- Object3D startLine = DrawLine.createLine(new SimpleVector(-30f,
- -10f, -170f), new SimpleVector(30f, -10f, -170f), 1f,
- "line");
- Object3D endLine = startLine.cloneObject();
- straightRoad.addChild(startLine);
- straightRoad.addChild(endLine);
Object3D startLine = DrawLine.createLine(new SimpleVector(-30f, -10f, -170f), new SimpleVector(30f, -10f, -170f), 1f, "line"); Object3D endLine = startLine.cloneObject(); straightRoad.addChild(startLine); straightRoad.addChild(endLine);
4.1.8 JPCT-AE繪制動態儀表盤及分數牌
繪制儀表盤:添加儀表盤紋理和指針紋理到平面,添加線程來實現指針的旋轉。每次旋轉pointer.rotateZ(-(float) Math.PI / 8f)個單位。最小速度為1,最大速度為11。
- public class DashBoardThread extends Thread {
- public void run() {
- while (flag) {
- if (CHANGED_SPEED != 0 && SPEED > 0 ) {
- if (CHANGED_SPEED == 2) {
- CHANGED_SPEED = 0;
- pointer.rotateZ(-(float) Math.PI / 8f);
- pointer.rotateZ(-(float) Math.PI / 8f);
- }
- }
- i--;
- if (!TravelingFlag && i < 0) {
- flag = false;
- pointer.strip();
- }
- try {
- Thread.sleep(1000);
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- }
public class DashBoardThread extends Thread {
public void run() {
while (flag) {
if (CHANGED_SPEED != 0 && SPEED > 0 ) {
if (CHANGED_SPEED == 2) {
CHANGED_SPEED = 0;
pointer.rotateZ(-(float) Math.PI / 8f);
pointer.rotateZ(-(float) Math.PI / 8f);
}
}
i--;
if (!TravelingFlag && i < 0) {
flag = false;
pointer.strip();
}
try {
Thread.sleep(1000);
} catch (Exception e) {
e.printStackTrace();
}
}
}
1) 繪制分數牌(動態顯示數字):在游戲中我們常常想要動態的顯示數字。在FrameBuffer中有一個blit方法,它的功能是將Texture中的一部分貼到屏幕上,有點類似於Overlay,不過這個方法更加靈活,可以只貼一部分。
通過JPCT-AE中blit(Texture, int, int, int, int, int, int, int, int, int, boolean, RGBColor)實現- com.threed.jpct.FrameBuffer 類中的方法,blit()的特殊版本,允許尺寸變換,也就是說,他不會一對一復制,但可以按比例放大或縮小。第一個參數是指下面的圖,Texuture對象第二、三個參數是你要在Texture對象上截下來的那個部分的左上角。第四、五個參數,是你想要放在屏幕上的位置,也是左上角。第六、七兩個參數是指截下來那部分的寬和高,貼在屏幕上也是這個寬和高。在顯示的時候將這個方法放在FrameBuffer的display之前,必須放在清屏(FrameBuffer的clear)和渲染與顯示世界之后。否則圖像會被清屏了,或者被世界中的物體擋住。
- public static void blitNumber(FrameBuffer fb,Texture src,int number, int x, int y) {
- if (src != null) {
- String sNum = Integer.toString(number);
- for (int i = 0; i < sNum.length(); i++) {
- char cNum = sNum.charAt(i);
- int iNum = cNum - 48;
- fb.blit(src, iNum * 5, 0, x, y, 5, 9,30,54, 0,false,null);
- x += 30;
- }
- }
- }
public static void blitNumber(FrameBuffer fb,Texture src,int number, int x, int y) {
if (src != null) {
String sNum = Integer.toString(number);
for (int i = 0; i < sNum.length(); i++) {
char cNum = sNum.charAt(i);
int iNum = cNum - 48;
fb.blit(src, iNum * 5, 0, x, y, 5, 9,30,54, 0,false,null);
x += 30;
}
}
}
4.1.9 JPCT-AE實現倒計時
JPCT-AE實現倒計時牌的繪制,即繪制紋理到平面中。添加線程,通過每一秒變換一次紋理來實現倒計時效果。實現倒計時功能,可以使玩家做好開始游戲的准備,倒計時結束后,游戲正式開始。
- public class CountdownThread extends Thread {
- public void run() {
- while (flag) {
- if (drawFlag == 3) {
- countdown.setTexture("countdown_3");
- } else if (drawFlag == 2) {
- countdown.setTexture("countdown_2");
- } else if (drawFlag == 1) {
- countdown.setTexture("countdown_1");
- } else if (drawFlag == 0) {
- countdown.setTexture("countdown_go");
- }
- if (drawFlag < 0) {
- countdown.translate(100f, 0f, 0f);
- countdown.strip();
- flag = false;
- TravelingFlag = true;
- }
- try {
- Thread.sleep(1000);
- } catch (Exception e) {
- e.printStackTrace();
- }
- drawFlag--;
- }
- }
