libgdx遊戲引擎開發筆記(十二)SuperJumper遊戲例子的講解(篇六)---- .遊戲主人公創建以及碰撞檢測

看了前面的幾講,相信大家都已經對這款遊戲有了一定的瞭解,今天我們就來完成最後的工作:主人公的控制、碰撞檢測, 主場景的移動。


1.主人公:

和添加platform一樣,在World中添加Bob並初始化:

聲明:

1
publicfinalBob bob;  //主角

實例化:

1
this.bob = newBob(5, 1);


  接下來,就是在刷新方法裏增加刷新Bob的方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**刷新界面**/
publicvoidupdate(float deltaTime, float accelX) {
updateBob(deltaTime, accelX);  //刷新主角
updatePlatforms(deltaTime);  //刷新跳板
}
/**刷新Bob**/
privatevoidupdateBob(float deltaTime, float accelX) {
//碰撞跳板
if(bob.state != Bob.BOB_STATE_HIT && bob.position.y <= 0.5f) bob.hitPlatform();
//主角x軸方向移動的速度
if(bob.state != Bob.BOB_STATE_HIT) bob.velocity.x = -accelX / 10* Bob.BOB_MOVE_VELOCITY;
bob.update(deltaTime);
//豎直最大高度
heightSoFar = Math.max(bob.position.y, heightSoFar);
}


這樣還是不能將主角顯示出來的!我們還需要在WorldRenderer類中renderObjects()方法中添加渲染Bob的方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
/**渲染遊戲中各種物體(Bob,跳板,松鼠,彈簧,城堡,金幣)**/
privatevoidrenderObjects() {
batch.enableBlending();
batch.begin();
//繪製跳板
renderPlatforms();
//繪製主角
renderBob();
batch.end();
}
/**渲染主角**/
privatevoidrenderBob() {
TextureRegion keyFrame;  //對應狀態下的圖片
switch(world.bob.state) {
caseBob.BOB_STATE_FALL:
keyFrame = Assets.bobFall.getKeyFrame(world.bob.stateTime, Animation.ANIMATION_LOOPING);
break;
caseBob.BOB_STATE_JUMP:
keyFrame = Assets.bobJump.getKeyFrame(world.bob.stateTime, Animation.ANIMATION_LOOPING);
break;
caseBob.BOB_STATE_HIT:
default:
keyFrame = Assets.bobHit;
}
float side = world.bob.velocity.x < 0? -1: 1;
if(side < 0)
batch.draw(keyFrame, world.bob.position.x + 0.5f, world.bob.position.y - 0.5f, side * 1, 1);
else
batch.draw(keyFrame, world.bob.position.x - 0.5f, world.bob.position.y - 0.5f, side * 1, 1);
}


因爲遊戲中主角的移動,還有松鼠,金幣,破碎的跳臺,這些都是需要動畫的支持,爲此我們單獨寫了一個動畫類,在Asset資源加載時調用!


Animation動畫類:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
packagecom.zhf.mylibgdx;
importcom.badlogic.gdx.graphics.g2d.TextureRegion;
/**
* 動畫類
* @author ZHF
*
*/
publicclassAnimation {
//動畫的兩種狀態,循環和不循環(不循環則動畫播放完畢後停留在最後一張圖片上)
publicstaticfinalintANIMATION_LOOPING = 0;
publicstaticfinalintANIMATION_NONLOOPING = 1;
//存儲圖片的數組
finalTextureRegion[] keyFrames;
//動畫每幀持續時間
finalfloat frameDuriation;
//在Assets中調用,大家自行參看
publicAnimation(float frameDuration, TextureRegion...  keyFrames) {
this.frameDuriation = frameDuration;
this.keyFrames = keyFrames;
}
/**
*  截取動畫圖片
* @param stateTime 存在此狀態的時間
* @param mode 動畫模式    
* @return
*/
publicTextureRegion getKeyFrame(float stateTime, intmode) {
//需要返回的圖片在數組中的位置
intframeNumber = (int) (stateTime / frameDuriation);
//不循環,停留在最後一張圖片上
if(mode == ANIMATION_NONLOOPING) {
frameNumber = Math.min(keyFrames.length - 1, frameNumber);
} else{
//循環模式
frameNumber = frameNumber % keyFrames.length;
}
//返回keyFrames數組
returnkeyFrames[frameNumber];
}
}

和以前一樣每當我們添加遊戲物體後,都需要在Asset中加載資源到內存中以備後面的顯示:

聲明:

1
2
3
4
//主角
publicstaticAnimation bobJump;  //跳躍的動畫
publicstaticAnimation bobFall;  //下落的動畫
publicstaticTextureRegion bobHit;  //碰撞圖片

實例化:

1
2
3
4
//主角
bobJump = newAnimation(0.2f, newTextureRegion(items, 0, 128, 32, 32), newTextureRegion(items, 32, 128, 32, 32));
bobFall = newAnimation(0.2f, newTextureRegion(items, 64, 128, 32, 32), newTextureRegion(items, 96, 128, 32, 32));
bobHit = newTextureRegion(items, 128, 128, 32, 32);


ok!到這裏!我們運行一下代碼,看一下效果:


  但是,主角不能被控制,貌似很尷尬。。。 彆着急,我們這就給它加上操控的方法,在GameScreen主遊戲界面中updateRunning(float deltaTime)中添加:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/**遊戲運行狀態**/
privatevoidupdateRunning (floatdeltaTime) {
if(Gdx.input.justTouched()) {
guiCam.unproject(touchPoint.set(Gdx.input.getX(), Gdx.input.getY(), 0));
//點擊暫停
if(OverlapTester.pointInRectangle(pauseBounds, touchPoint.x, touchPoint.y)) {
Assets.playSound(Assets.clickSound);
state = GAME_PAUSED;
return;
}
}
ApplicationType appType = Gdx.app.getType();
// should work also with Gdx.input.isPeripheralAvailable(Peripheral.Accelerometer)
if(appType == ApplicationType.Android || appType == ApplicationType.iOS) {
world.update(deltaTime, Gdx.input.getAccelerometerX());
} else{
floataccel = 0;
if(Gdx.input.isKeyPressed(Keys.DPAD_LEFT)) accel = 5f;
if(Gdx.input.isKeyPressed(Keys.DPAD_RIGHT)) accel = -5f;
world.update(deltaTime, accel);
}
}


運行一下代碼,按下左右鍵,我們的主角就可以移動了,效果圖:


這裏沒有添加碰撞檢測,所以會跑出邊界,踩不上跳板的哦,這裏可能有些人或許會有疑惑:主角如何自動跳起,還有降落那?


我們看一下以前的代碼:

World類:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**生成關卡中除了Bob外所有的物體**/
privatevoidgenerateLevel() {
float y = Platform.PLATFORM_HEIGHT / 2;
float maxJumpHeight = Bob.BOB_JUMP_VELOCITY * Bob.BOB_JUMP_VELOCITY / (2* -gravity.y);
while(y < WORLD_HEIGHT - WORLD_WIDTH / 2) {
inttype = rand.nextFloat() > 0.8f ? Platform.PLATFORM_TYPE_MOVING : Platform.PLATFORM_TYPE_STATIC;
float x = rand.nextFloat() * (WORLD_WIDTH - Platform.PLATFORM_WIDTH) + Platform.PLATFORM_WIDTH / 2;
Platform platform = newPlatform(type, x, y);
platforms.add(platform);
y += (maxJumpHeight - 0.5f);
y -= rand.nextFloat() * (maxJumpHeight / 3);
}
}


  對於每次Y座標要增加多少,這裏提供了一個參考的值爲maxJumpHeight ,這個值表示每次主角Bob能跳過高,也就是在y軸上往上增加多少個單位。而它就是根據物理公式計算的,如下:

1
floatmaxJumpHeight = Bob.BOB_JUMP_VELOCITY * Bob.BOB_JUMP_VELOCITY  / (2* -gravity.y);


其中Bob.BOB_JUMP_VELOCITY就是跳躍的初速度 爲 11;gravity爲重力,在World成員變量中已經定義,如下:

1
publicstaticfinalVector2 gravity = newVector2(0, -12);


因爲我們現在是要模擬在有重力的情況下的跳躍過程,那麼真實的跳躍過程就是,一開始跳躍時,會有一個初速度,也就是Bob.BOB_JUMP_VELOCITY,但是受到重力gravity的影響gravity,主角Bob的速度會慢慢的減至爲0,然後開始往下掉下來。所以Y方向重力被設置爲-12,那麼根據物理公式,在知道初速度,末速度,還有重力三個條件下,就可以計算出位移,在這裏就是主角Bob能跳多高了。

那麼把這個值作爲每次Y座標增加的基準,再用隨機數rand做一些處理,讓每次生Y座標的增加量都有所不同。

這樣一個World(關卡)就生成了,接下來就可以交給WorldRender開始渲染。


 相信大家這下明白了吧,我們接着來進行碰撞檢測的實現。

還是在World類中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
/**刷新界面**/
publicvoidupdate(floatdeltaTime, floataccelX) {
updateBob(deltaTime, accelX);  //刷新主角
updatePlatforms(deltaTime);  //刷新跳板
if(bob.state != Bob.BOB_STATE_HIT) checkCollisions();
}
/**碰撞檢測**/
privatevoidcheckCollisions() {
// TODO Auto-generated method stub
checkPlatformCollisions();  //跳板的碰撞
}
privatevoidcheckPlatformCollisions() {
if(bob.velocity.y > 0) return;
intlen = platforms.size();
for(inti = 0; i < len; i++) {
Platform platform = platforms.get(i);
if(bob.position.y > platform.position.y) {
//調用工具類中矩形塊碰撞檢測
if(OverlapTester.overlapRectangles(bob.bounds, platform.bounds)) {
bob.hitPlatform();
listener.jump();
if(rand.nextFloat() > 0.5f) {
platform.pulverize();
}
break;
}
}
}
}


OverlapTester類:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
packagecom.zhf.mylibgdx;
importcom.badlogic.gdx.math.Rectangle;
importcom.badlogic.gdx.math.Vector2;
/**
* 工具類:檢測各種碰撞
* @author ZHF
*
*/
publicclassOverlapTester {
/**檢測輸入的X,Y是否在輸入的矩形框r內**/
publicstaticboolean pointInRectangle(Rectangle r, float x, float y) {
returnr.x <= x && r.x + r.width >= x && r.y <= y
&& r.y + r.height >= y;
}
/**兩個矩形塊的碰撞檢測**/
publicstaticboolean overlapRectangles (Rectangle r1, Rectangle r2) {
if(r1.x < r2.x + r2.width && r1.x + r1.width > r2.x && r1.y < r2.y + r2.height && r1.y + r1.height > r2.y)
returntrue;
else
returnfalse;
}
/**點是否在矩形內**/
publicstaticboolean pointInRectangle (Rectangle r, Vector2 p) {
returnr.x <= p.x && r.x + r.width >= p.x && r.y <= p.y && r.y + r.height >= p.y;
}
}



在刷新update方法裏添加碰撞檢測方法checkCollisions(),這裏目前只添加了跳板的碰撞檢測,(碰撞檢測的具體細節:遊戲中的物體我們定義時就將其固定爲矩形,通過兩個矩形塊的相交來完成判斷)運行一下,我們發現是能踩在跳板上了,但是一直往上跳都出屏幕了,這是怎麼回事那?我們好多遊戲在主角運動的時候,其實都是後面的場景在動,給玩家的感受就是主角在前進了。


先解決一下,場景移動的問題,在WorldRenderer類中render ()方法中,添加一行:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**渲染**/
publicvoidrender () {
//重點講解
if(world.bob.position.y > cam.position.y) cam.position.y = world.bob.position.y;
cam.update();
//它的作用都是通過把映射矩陣綁定給SpritBatch,告訴SpritBatch怎麼去繪製圖形
batch.setProjectionMatrix(cam.combined);
//渲染背景
renderBackground();
//渲染遊戲中各種元素(Bob,跳板,松鼠,彈簧。。)下一講中會具體講到
renderObjects();
}


  代碼很簡單,原理我需要在這裏講解一下:

1.之前已經說過,WorldRender 是把World裏面的遊戲物體拿出來,把他們渲染到屏幕上,那麼顯然它需要在構造的時候傳入兩個東西,一個SpriteBatch,另一個則是World。再者,WorldRnder需要定義不同於GameScreen的OrthographicCamera,然後將OrthographicCamera的投影矩陣綁定給SpriteBatch。最後就是根據World中物體的屬性,把它們繪製到屏幕上。


  2.當bob的y座標值,大於WorldRender中OrthographicCamera的position的y值時,就把bob的y值賦OrthographicCamera。正是這句話,實現了遊戲場景的移動。接着就是調用update 和 setProjectionMatrix把投影矩陣綁定給SpriteBatch告訴它要怎麼繪圖。

  3.renderBackground ()方法是繪製背景圖片。而renderObjects()就是真正根據World中游戲物體來繪製遊戲畫面,在這裏作者把不同的物體分開來,讓代碼更爲清晰。


再次運行一下哦!效果圖:


主角跳到中間時,場景會一直處在中間,按下左右鍵,實現了不斷地想上跳躍!


下一講我們就會完成最後遊戲中各個物體的創建及其碰撞檢測,還有一些瑣碎的事情,遊戲中音效,分值排行榜,遊戲結束通關判斷,等等


×××:http://down.51cto.com/data/897107

本文出自 “狂奔的蝸牛” 博客,請務必保留此出處http://smallwoniu.blog.51cto.com/3911954/1263923


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章