天天看點

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

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

1.主人公:

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

聲明:

1

<code>public</code> <code>final</code> <code>Bob bob;  </code><code>//主角</code>

執行個體化:

<code>this</code><code>.bob = </code><code>new</code> <code>Bob(</code><code>5</code><code>, </code><code>1</code><code>);</code>

   接下來,就是在重新整理方法裡增加重新整理Bob的方法:

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

<code>/**重新整理界面**/</code>

<code>    </code><code>public</code> <code>void</code> <code>update(float deltaTime, float accelX) {</code>

<code>        </code><code>updateBob(deltaTime, accelX);  </code><code>//重新整理主角</code>

<code>        </code><code>updatePlatforms(deltaTime);  </code><code>//重新整理跳闆</code>

<code>                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     </code> 

<code>    </code><code>}</code>

<code>                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 </code> 

<code>    </code><code>/**重新整理Bob**/</code>

<code>    </code><code>private</code> <code>void</code> <code>updateBob(float deltaTime, float accelX) {</code>

<code>        </code><code>//碰撞跳闆</code>

<code>        </code><code>if</code> <code>(bob.state != Bob.BOB_STATE_HIT &amp;&amp; bob.position.y &lt;= </code><code>0</code><code>.5f) bob.hitPlatform();</code>

<code>        </code><code>//主角x軸方向移動的速度</code>

<code>        </code><code>if</code> <code>(bob.state != Bob.BOB_STATE_HIT) bob.velocity.x = -accelX / </code><code>10</code> <code>* Bob.BOB_MOVE_VELOCITY;</code>

<code>        </code><code>bob.update(deltaTime);</code>

<code>        </code><code>//豎直最大高度</code>

<code>        </code><code>heightSoFar = Math.max(bob.position.y, heightSoFar);</code>

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

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

<code>/**渲染遊戲中各種物體(Bob,跳闆,松鼠,彈簧,城堡,金币)**/</code>

<code>    </code><code>private</code> <code>void</code> <code>renderObjects() {</code>

<code>        </code><code>batch.enableBlending();</code>

<code>        </code><code>batch.begin();</code>

<code>        </code><code>//繪制跳闆</code>

<code>        </code><code>renderPlatforms();</code>

<code>        </code><code>//繪制主角</code>

<code>        </code><code>renderBob();</code>

<code>        </code><code>batch.end();</code>

<code>                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     </code> 

<code>    </code><code>/**渲染主角**/</code>

<code>    </code><code>private</code> <code>void</code> <code>renderBob() {</code>

<code>        </code><code>TextureRegion keyFrame;  </code><code>//對應狀态下的圖檔</code>

<code>                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         </code> 

<code>        </code><code>switch</code> <code>(world.bob.state) {</code>

<code>        </code><code>case</code> <code>Bob.BOB_STATE_FALL:</code>

<code>            </code><code>keyFrame = Assets.bobFall.getKeyFrame(world.bob.stateTime, Animation.ANIMATION_LOOPING);</code>

<code>            </code><code>break</code><code>;</code>

<code>        </code><code>case</code> <code>Bob.BOB_STATE_JUMP:</code>

<code>            </code><code>keyFrame = Assets.bobJump.getKeyFrame(world.bob.stateTime, Animation.ANIMATION_LOOPING);</code>

<code>        </code><code>case</code> <code>Bob.BOB_STATE_HIT:</code>

<code>        </code><code>default</code><code>:</code>

<code>            </code><code>keyFrame = Assets.bobHit;</code>

<code>        </code><code>}</code>

<code>        </code><code>float side = world.bob.velocity.x &lt; </code><code>0</code> <code>? -</code><code>1</code> <code>: </code><code>1</code><code>;</code>

<code>        </code><code>if</code> <code>(side &lt; </code><code>0</code><code>)</code>

<code>            </code><code>batch.draw(keyFrame, world.bob.position.x + </code><code>0</code><code>.5f, world.bob.position.y - </code><code>0</code><code>.5f, side * </code><code>1</code><code>, </code><code>1</code><code>);</code>

<code>        </code><code>else</code>

<code>            </code><code>batch.draw(keyFrame, world.bob.position.x - </code><code>0</code><code>.5f, world.bob.position.y - </code><code>0</code><code>.5f, side * </code><code>1</code><code>, </code><code>1</code><code>);</code>

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

Animation動畫類:

33

34

35

36

37

38

39

40

41

42

43

44

<code>package</code> <code>com.zhf.mylibgdx;</code>

<code>import</code> <code>com.badlogic.gdx.graphics.g2d.TextureRegion;</code>

<code>/**</code>

<code> </code><code>* 動畫類</code>

<code> </code><code>* @author ZHF</code>

<code> </code><code>*</code>

<code> </code><code>*/</code>

<code>public</code> <code>class</code> <code>Animation {</code>

<code>    </code><code>//動畫的兩種狀态,循環和不循環(不循環則動畫播放完畢後停留在最後一張圖檔上)</code>

<code>    </code><code>public</code> <code>static</code> <code>final</code> <code>int</code> <code>ANIMATION_LOOPING = </code><code>0</code><code>;</code>

<code>    </code><code>public</code> <code>static</code> <code>final</code> <code>int</code> <code>ANIMATION_NONLOOPING = </code><code>1</code><code>;</code>

<code>                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              </code> 

<code>    </code><code>//存儲圖檔的數組</code>

<code>    </code><code>final</code> <code>TextureRegion[] keyFrames;</code>

<code>    </code><code>//動畫每幀持續時間</code>

<code>    </code><code>final</code> <code>float frameDuriation;</code>

<code>    </code><code>//在Assets中調用,大家自行參看</code>

<code>    </code><code>public</code> <code>Animation(float frameDuration, TextureRegion...  keyFrames) {</code>

<code>        </code><code>this</code><code>.frameDuriation = frameDuration;</code>

<code>        </code><code>this</code><code>.keyFrames = keyFrames;</code>

<code>    </code><code>/**</code>

<code>     </code><code>*  截取動畫圖檔</code>

<code>     </code><code>* @param stateTime 存在此狀态的時間</code>

<code>     </code><code>* @param mode 動畫模式    </code>

<code>     </code><code>* @return</code>

<code>     </code><code>*/</code>

<code>    </code><code>public</code> <code>TextureRegion getKeyFrame(float stateTime, </code><code>int</code> <code>mode) {</code>

<code>        </code><code>//需要傳回的圖檔在數組中的位置</code>

<code>        </code><code>int</code> <code>frameNumber = (</code><code>int</code><code>) (stateTime / frameDuriation);</code>

<code>                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  </code> 

<code>        </code><code>//不循環,停留在最後一張圖檔上</code>

<code>        </code><code>if</code><code>(mode == ANIMATION_NONLOOPING) {</code>

<code>            </code><code>frameNumber = Math.min(keyFrames.length - </code><code>1</code><code>, frameNumber);</code>

<code>        </code><code>} </code><code>else</code><code>{</code>

<code>            </code><code>//循環模式</code>

<code>            </code><code>frameNumber = frameNumber % keyFrames.length;</code>

<code>        </code><code>//傳回keyFrames數組</code>

<code>        </code><code>return</code> <code>keyFrames[frameNumber];</code>

<code>}</code>

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

<code>//主角</code>

<code>    </code><code>public</code> <code>static</code> <code>Animation bobJump;  </code><code>//跳躍的動畫</code>

<code>    </code><code>public</code> <code>static</code> <code>Animation bobFall;  </code><code>//下落的動畫</code>

<code>    </code><code>public</code> <code>static</code> <code>TextureRegion bobHit;  </code><code>//碰撞圖檔</code>

<code>        </code><code>bobJump = </code><code>new</code> <code>Animation(</code><code>0</code><code>.2f, </code><code>new</code> <code>TextureRegion(items, </code><code>0</code><code>, </code><code>128</code><code>, </code><code>32</code><code>, </code><code>32</code><code>), </code><code>new</code> <code>TextureRegion(items, </code><code>32</code><code>, </code><code>128</code><code>, </code><code>32</code><code>, </code><code>32</code><code>));</code>

<code>        </code><code>bobFall = </code><code>new</code> <code>Animation(</code><code>0</code><code>.2f, </code><code>new</code> <code>TextureRegion(items, </code><code>64</code><code>, </code><code>128</code><code>, </code><code>32</code><code>, </code><code>32</code><code>), </code><code>new</code> <code>TextureRegion(items, </code><code>96</code><code>, </code><code>128</code><code>, </code><code>32</code><code>, </code><code>32</code><code>));</code>

<code>        </code><code>bobHit = </code><code>new</code> <code>TextureRegion(items, </code><code>128</code><code>, </code><code>128</code><code>, </code><code>32</code><code>, </code><code>32</code><code>);</code>

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

<a target="_blank" href="http://blog.51cto.com/attachment/201308/135443830.png"></a>

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

<code>/**遊戲運作狀态**/</code>

<code>    </code><code>private</code> <code>void</code> <code>updateRunning (</code><code>float</code> <code>deltaTime) {</code>

<code>        </code><code>if</code> <code>(Gdx.input.justTouched()) {</code>

<code>            </code><code>guiCam.unproject(touchPoint.set(Gdx.input.getX(), Gdx.input.getY(), </code><code>0</code><code>));</code>

<code>            </code><code>//點選暫停</code>

<code>            </code><code>if</code> <code>(OverlapTester.pointInRectangle(pauseBounds, touchPoint.x, touchPoint.y)) {</code>

<code>                </code><code>Assets.playSound(Assets.clickSound);</code>

<code>                </code><code>state = GAME_PAUSED;</code>

<code>                </code><code>return</code><code>;</code>

<code>            </code><code>}</code>

<code>                                                                                                                                                                                                                                                                                                                                                                                                                                                                   </code> 

<code>        </code><code>ApplicationType appType = Gdx.app.getType();</code>

<code>        </code><code>// should work also with Gdx.input.isPeripheralAvailable(Peripheral.Accelerometer)</code>

<code>        </code><code>if</code> <code>(appType == ApplicationType.Android || appType == ApplicationType.iOS) {</code>

<code>            </code><code>world.update(deltaTime, Gdx.input.getAccelerometerX());</code>

<code>        </code><code>} </code><code>else</code> <code>{</code>

<code>            </code><code>float</code> <code>accel = </code><code>0</code><code>;</code>

<code>            </code><code>if</code> <code>(Gdx.input.isKeyPressed(Keys.DPAD_LEFT)) accel = 5f;</code>

<code>            </code><code>if</code> <code>(Gdx.input.isKeyPressed(Keys.DPAD_RIGHT)) accel = -5f;</code>

<code>            </code><code>world.update(deltaTime, accel);</code>

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

<a target="_blank" href="http://blog.51cto.com/attachment/201308/140202746.png"></a>

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

我們看一下以前的代碼:

World類:

<code>/**生成關卡中除了Bob外所有的物體**/</code>

<code>    </code><code>private</code> <code>void</code> <code>generateLevel() {</code>

<code>        </code><code>float y = Platform.PLATFORM_HEIGHT / </code><code>2</code><code>;</code>

<code>        </code><code>float maxJumpHeight = Bob.BOB_JUMP_VELOCITY * Bob.BOB_JUMP_VELOCITY / (</code><code>2</code> <code>* -gravity.y);</code>

<code>                                                                                                                                                                                                                                                                                                                                                                                           </code> 

<code>        </code><code>while</code> <code>(y &lt; WORLD_HEIGHT - WORLD_WIDTH / </code><code>2</code><code>) {</code>

<code>            </code><code>int</code> <code>type = rand.nextFloat() &gt; </code><code>0</code><code>.8f ? Platform.PLATFORM_TYPE_MOVING : Platform.PLATFORM_TYPE_STATIC;</code>

<code>            </code><code>float x = rand.nextFloat() * (WORLD_WIDTH - Platform.PLATFORM_WIDTH) + Platform.PLATFORM_WIDTH / </code><code>2</code><code>;</code>

<code>            </code><code>Platform platform = </code><code>new</code> <code>Platform(type, x, y);</code>

<code>            </code><code>platforms.add(platform);</code>

<code>                                                                                                                                                                                                                                                                                                                                                                                               </code> 

<code>            </code><code>y += (maxJumpHeight - </code><code>0</code><code>.5f);</code>

<code>            </code><code>y -= rand.nextFloat() * (maxJumpHeight / </code><code>3</code><code>);</code>

   對于每次Y坐标要增加多少,這裡提供了一個參考的值為maxJumpHeight ,這個值表示每次主角Bob能跳過高,也就是在y軸上往上增加多少個機關。而它就是根據實體公式計算的,如下:

<code>float</code> <code>maxJumpHeight = Bob.BOB_JUMP_VELOCITY * Bob.BOB_JUMP_VELOCITY  / (</code><code>2</code> <code>* -gravity.y);</code>

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

<code>public</code> <code>static</code> <code>final</code> <code>Vector2 gravity = </code><code>new</code> <code>Vector2(</code><code>0</code><code>, -</code><code>12</code><code>);</code>

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

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

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

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

還是在World類中:

<code>public</code> <code>void</code> <code>update(</code><code>float</code> <code>deltaTime, </code><code>float</code> <code>accelX) {</code>

<code>    </code><code>updateBob(deltaTime, accelX);  </code><code>//重新整理主角</code>

<code>    </code><code>updatePlatforms(deltaTime);  </code><code>//重新整理跳闆</code>

<code>                                                                                                                                                                                                                                               </code> 

<code>    </code><code>if</code> <code>(bob.state != Bob.BOB_STATE_HIT) checkCollisions();</code>

<code>/**碰撞檢測**/</code>

<code>private</code> <code>void</code> <code>checkCollisions() {</code>

<code>    </code><code>// TODO Auto-generated method stub</code>

<code>    </code><code>checkPlatformCollisions();  </code><code>//跳闆的碰撞</code>

<code>private</code> <code>void</code> <code>checkPlatformCollisions() {</code>

<code>    </code><code>if</code> <code>(bob.velocity.y &gt; </code><code>0</code><code>) </code><code>return</code><code>;</code>

<code>    </code><code>int</code> <code>len = platforms.size();</code>

<code>    </code><code>for</code> <code>(</code><code>int</code> <code>i = </code><code>0</code><code>; i &lt; len; i++) {</code>

<code>        </code><code>Platform platform = platforms.get(i);</code>

<code>        </code><code>if</code> <code>(bob.position.y &gt; platform.position.y) {</code>

<code>            </code><code>//調用工具類中矩形塊碰撞檢測</code>

<code>            </code><code>if</code> <code>(OverlapTester.overlapRectangles(bob.bounds, platform.bounds)) {</code>

<code>                </code><code>bob.hitPlatform();</code>

<code>                </code><code>listener.jump();</code>

<code>                </code><code>if</code> <code>(rand.nextFloat() &gt; </code><code>0</code><code>.5f) {</code>

<code>                    </code><code>platform.pulverize();</code>

<code>                </code><code>}</code>

<code>                </code><code>break</code><code>;</code>

OverlapTester類:

<code>import</code> <code>com.badlogic.gdx.math.Rectangle;</code>

<code>import</code> <code>com.badlogic.gdx.math.Vector2;</code>

<code> </code><code>* 工具類:檢測各種碰撞</code>

<code>public</code> <code>class</code> <code>OverlapTester {</code>

<code>    </code><code>/**檢測輸入的X,Y是否在輸入的矩形框r内**/</code>

<code>    </code><code>public</code> <code>static</code> <code>boolean pointInRectangle(Rectangle r, float x, float y) {</code>

<code>        </code><code>return</code> <code>r.x &lt;= x &amp;&amp; r.x + r.width &gt;= x &amp;&amp; r.y &lt;= y</code>

<code>                </code><code>&amp;&amp; r.y + r.height &gt;= y;</code>

<code>                                             </code> 

<code>    </code><code>/**兩個矩形塊的碰撞檢測**/</code>

<code>    </code><code>public</code> <code>static</code> <code>boolean overlapRectangles (Rectangle r1, Rectangle r2) {</code>

<code>        </code><code>if</code> <code>(r1.x &lt; r2.x + r2.width &amp;&amp; r1.x + r1.width &gt; r2.x &amp;&amp; r1.y &lt; r2.y + r2.height &amp;&amp; r1.y + r1.height &gt; r2.y)</code>

<code>            </code><code>return</code> <code>true</code><code>;</code>

<code>            </code><code>return</code> <code>false</code><code>;</code>

<code>    </code><code>/**點是否在矩形内**/</code>

<code>    </code><code>public</code> <code>static</code> <code>boolean pointInRectangle (Rectangle r, Vector2 p) {</code>

<code>        </code><code>return</code> <code>r.x &lt;= p.x &amp;&amp; r.x + r.width &gt;= p.x &amp;&amp; r.y &lt;= p.y &amp;&amp; r.y + r.height &gt;= p.y;</code>

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

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

<code>/**渲染**/</code>

<code>    </code><code>public</code> <code>void</code> <code>render () {</code>

<code>                                                                                                                     </code> 

<code>        </code><code>//重點講解</code>

<code>        </code><code>if</code> <code>(world.bob.position.y &gt; cam.position.y) cam.position.y = world.bob.position.y;</code>

<code>        </code><code>cam.update();</code>

<code>        </code><code>//它的作用都是通過把映射矩陣綁定給SpritBatch,告訴SpritBatch怎麼去繪制圖形</code>

<code>        </code><code>batch.setProjectionMatrix(cam.combined);</code>

<code>        </code><code>//渲染背景</code>

<code>        </code><code>renderBackground();</code>

<code>        </code><code>//渲染遊戲中各種元素(Bob,跳闆,松鼠,彈簧。。)下一講中會具體講到</code>

<code>        </code><code>renderObjects();</code>

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

   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中遊戲物體來繪制遊戲畫面,在這裡作者把不同的物體分開來,讓代碼更為清晰。

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

<a target="_blank" href="http://blog.51cto.com/attachment/201308/144429136.png"></a>

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

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

<b>     本文轉自zhf651555765 51CTO部落格,原文連結:http://blog.51cto.com/smallwoniu/1263923</b><b>,如需轉載請自行聯系原作者</b>

<b></b>

繼續閱讀