看了前面的几讲,相信大家都已经对这款游戏有了一定的了解,今天我们就来完成最后的工作:主人公的控制、碰撞检测, 主场景的移动。
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 && bob.position.y <= </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 < </code><code>0</code> <code>? -</code><code>1</code> <code>: </code><code>1</code><code>;</code>
<code> </code><code>if</code> <code>(side < </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 < WORLD_HEIGHT - WORLD_WIDTH / </code><code>2</code><code>) {</code>
<code> </code><code>int</code> <code>type = rand.nextFloat() > </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 > </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 < len; i++) {</code>
<code> </code><code>Platform platform = platforms.get(i);</code>
<code> </code><code>if</code> <code>(bob.position.y > 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() > </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 <= x && r.x + r.width >= x && r.y <= y</code>
<code> </code><code>&& r.y + r.height >= 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 < r2.x + r2.width && r1.x + r1.width > r2.x && r1.y < r2.y + r2.height && r1.y + r1.height > 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 <= p.x && r.x + r.width >= p.x && r.y <= p.y && r.y + r.height >= 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 > 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>