libgdx游戏引擎开发笔记(十二)SuperJumper游戏例子的讲解(篇六)---- .游戏主人公创建以及碰撞检测

简介:

 看了前面的几讲,相信大家都已经对这款游戏有了一定的了解,今天我们就来完成最后的工作:主人公的控制、碰撞检测, 主场景的移动。


1.主人公:

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

声明:

1
public  final  Bob bob;   //主角

实例化:

1
this .bob =  new  Bob( 5 1 );

   

   接下来,就是在刷新方法里增加刷新Bob的方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**刷新界面**/
     public  void  update(float deltaTime, float accelX) {
         updateBob(deltaTime, accelX);   //刷新主角
         updatePlatforms(deltaTime);   //刷新跳板
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      
     }
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  
     /**刷新Bob**/
     private  void  updateBob(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,跳板,松鼠,弹簧,城堡,金币)**/
     private  void  renderObjects() {
         batch.enableBlending();
         batch.begin();
         //绘制跳板
         renderPlatforms();
         //绘制主角
         renderBob();
         batch.end();
     }
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      
     /**渲染主角**/
     private  void  renderBob() {
         TextureRegion keyFrame;   //对应状态下的图片
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          
         switch  (world.bob.state) {
         case  Bob.BOB_STATE_FALL:
             keyFrame = Assets.bobFall.getKeyFrame(world.bob.stateTime, Animation.ANIMATION_LOOPING);
             break ;
         case  Bob.BOB_STATE_JUMP:
             keyFrame = Assets.bobJump.getKeyFrame(world.bob.stateTime, Animation.ANIMATION_LOOPING);
             break ;
         case  Bob.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
package  com.zhf.mylibgdx;
import  com.badlogic.gdx.graphics.g2d.TextureRegion;
/**
  * 动画类
  * @author ZHF
  *
  */
public  class  Animation {
     //动画的两种状态,循环和不循环(不循环则动画播放完毕后停留在最后一张图片上)
     public  static  final  int  ANIMATION_LOOPING =  0 ;
     public  static  final  int  ANIMATION_NONLOOPING =  1 ;
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               
     //存储图片的数组
     final  TextureRegion[] keyFrames;
     //动画每帧持续时间
     final  float frameDuriation;
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               
     //在Assets中调用,大家自行参看
     public  Animation(float frameDuration, TextureRegion...  keyFrames) {
         this .frameDuriation = frameDuration;
         this .keyFrames = keyFrames;
     }
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               
     /**
      *  截取动画图片
      * @param stateTime 存在此状态的时间
      * @param mode 动画模式    
      * @return
      */
     public  TextureRegion getKeyFrame(float stateTime,  int  mode) {
         //需要返回的图片在数组中的位置
         int  frameNumber = ( int ) (stateTime / frameDuriation);
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   
         //不循环,停留在最后一张图片上
         if (mode == ANIMATION_NONLOOPING) {
             frameNumber = Math.min(keyFrames.length -  1 , frameNumber);
         else {
             //循环模式
             frameNumber = frameNumber % keyFrames.length;
         }
         //返回keyFrames数组
         return  keyFrames[frameNumber];
     }
}

   和以前一样每当我们添加游戏物体后,都需要在Asset中加载资源到内存中以备后面的显示:

声明:

1
2
3
4
//主角
     public  static  Animation bobJump;   //跳跃的动画
     public  static  Animation bobFall;   //下落的动画
     public  static  TextureRegion bobHit;   //碰撞图片

实例化:

1
2
3
4
//主角
         bobJump =  new  Animation( 0 .2f,  new  TextureRegion(items,  0 128 32 32 ),  new  TextureRegion(items,  32 128 32 32 ));
         bobFall =  new  Animation( 0 .2f,  new  TextureRegion(items,  64 128 32 32 ),  new  TextureRegion(items,  96 128 32 32 ));
         bobHit =  new  TextureRegion(items,  128 128 32 32 );

    

ok!到这里!我们运行一下代码,看一下效果:

135443830.png


   但是,主角不能被控制,貌似很尴尬。。。 别着急,我们这就给它加上操控的方法,在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
/**游戏运行状态**/
     private  void  updateRunning ( float  deltaTime) {
         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  {
             float  accel =  0 ;
             if  (Gdx.input.isKeyPressed(Keys.DPAD_LEFT)) accel = 5f;
             if  (Gdx.input.isKeyPressed(Keys.DPAD_RIGHT)) accel = -5f;
             world.update(deltaTime, accel);
         }
                                                                                                                                                                                                                                                                                                                                                                                                                                                                    
     }


运行一下代码,按下左右键,我们的主角就可以移动了,效果图:

140202746.png


这里没有添加碰撞检测,所以会跑出边界,踩不上跳板的哦,这里可能有些人或许会有疑惑:主角如何自动跳起,还有降落那?


我们看一下以前的代码:

World类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**生成关卡中除了Bob外所有的物体**/
     private  void  generateLevel() {
         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 ) {
             int  type = 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 =  new  Platform(type, x, y);
             platforms.add(platform);
                                                                                                                                                                                                                                                                                                                                                                                                
             y += (maxJumpHeight -  0 .5f);
             y -= rand.nextFloat() * (maxJumpHeight /  3 );
         }
     }


   对于每次Y坐标要增加多少,这里提供了一个参考的值为maxJumpHeight ,这个值表示每次主角Bob能跳过高,也就是在y轴上往上增加多少个单位。而它就是根据物理公式计算的,如下:

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

 

其中Bob.BOB_JUMP_VELOCITY就是跳跃的初速度 为 11;gravity为重力,在World成员变量中已经定义,如下:

1
public  static  final  Vector2 gravity =  new  Vector2( 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
/**刷新界面**/
public  void  update( float  deltaTime,  float  accelX) {
     updateBob(deltaTime, accelX);   //刷新主角
     updatePlatforms(deltaTime);   //刷新跳板
                                                                                                                                                                                                                                                
     if  (bob.state != Bob.BOB_STATE_HIT) checkCollisions();
}
/**碰撞检测**/
private  void  checkCollisions() {
     // TODO Auto-generated method stub
     checkPlatformCollisions();   //跳板的碰撞
}
private  void  checkPlatformCollisions() {
     if  (bob.velocity.y >  0 return ;
     int  len = platforms.size();
     for  ( int  i =  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
package  com.zhf.mylibgdx;
import  com.badlogic.gdx.math.Rectangle;
import  com.badlogic.gdx.math.Vector2;
/**
  * 工具类:检测各种碰撞
  * @author ZHF
  *
  */
public  class  OverlapTester {
     /**检测输入的X,Y是否在输入的矩形框r内**/
     public  static  boolean pointInRectangle(Rectangle r, float x, float y) {
         return  r.x <= x && r.x + r.width >= x && r.y <= y
                 && r.y + r.height >= y;
     }
                                              
     /**两个矩形块的碰撞检测**/
     public  static  boolean 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)
             return  true ;
         else
             return  false ;
     }
                                              
     /**点是否在矩形内**/
     public  static  boolean pointInRectangle (Rectangle r, Vector2 p) {
         return  r.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
/**渲染**/
     public  void  render () {
                                                                                                                      
         //重点讲解
         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中游戏物体来绘制游戏画面,在这里作者把不同的物体分开来,让代码更为清晰。


再次运行一下哦!效果图:

144429136.png

主角跳到中间时,场景会一直处在中间,按下左右键,实现了不断地想上跳跃!


下一讲我们就会完成最后游戏中各个物体的创建及其碰撞检测,还有一些琐碎的事情,游戏中音效,分值排行榜,游戏结束通关判断,等等


源码下载:http://down.51cto.com/data/897107






     本文转自zhf651555765 51CTO博客,原文链接:http://blog.51cto.com/smallwoniu/1263923,如需转载请自行联系原作者




相关文章
|
2天前
|
图形学
【用unity实现100个游戏之17】从零开始制作一个类幸存者肉鸽(Roguelike)游戏5(附项目源码)(下)
【用unity实现100个游戏之17】从零开始制作一个类幸存者肉鸽(Roguelike)游戏5(附项目源码)
4 0
|
2天前
|
图形学
【用unity实现100个游戏之17】从零开始制作一个类幸存者肉鸽(Roguelike)游戏5(附项目源码)(上)
【用unity实现100个游戏之17】从零开始制作一个类幸存者肉鸽(Roguelike)游戏5(附项目源码)
4 0
|
存储 JavaScript C#
从零开始做一款Unity3D游戏<三>——编写游戏机制(一)
从零开始做一款Unity3D游戏<三>——编写游戏机制
从零开始做一款Unity3D游戏<三>——编写游戏机制(一)
|
存储 设计模式 JavaScript
从零开始做一款Unity3D游戏<三>——编写游戏机制(二)
从零开始做一款Unity3D游戏<三>——编写游戏机制
从零开始做一款Unity3D游戏<三>——编写游戏机制(二)
|
vr&ar 图形学
【Unity3D 灵巧小知识点】 ☀️ | Unity退出游戏代码
Unity 小科普 老规矩,先介绍一下 Unity 的科普小知识: Unity是 实时3D互动内容创作和运营平台 。 包括游戏开发、美术、建筑、汽车设计、影视在内的所有创作者,借助 Unity 将创意变成现实。 Unity 平台提供一整套完善的软件解决方案,可用于创作、运营和变现任何实时互动的2D和3D内容,支持平台包括手机、平板电脑、PC、游戏主机、增强现实和虚拟现实设备。 也可以简单把 Unity 理解为一个游戏引擎,可以用来专业制作游戏!
【Unity3D 灵巧小知识点】 ☀️ | Unity退出游戏代码
|
vr&ar 图形学 索引
【Unity3D 灵巧小知识点】 ☀️ | Unity中如何使用代码切换场景
Unity 小科普 老规矩,先介绍一下 Unity 的科普小知识: Unity是 实时3D互动内容创作和运营平台 。 包括游戏开发、美术、建筑、汽车设计、影视在内的所有创作者,借助 Unity 将创意变成现实。 Unity 平台提供一整套完善的软件解决方案,可用于创作、运营和变现任何实时互动的2D和3D内容,支持平台包括手机、平板电脑、PC、游戏主机、增强现实和虚拟现实设备。 也可以简单把 Unity 理解为一个游戏引擎,可以用来专业制作游戏!
【Unity3D 灵巧小知识点】 ☀️ | Unity中如何使用代码切换场景
|
编解码 定位技术 图形学
Unity 之 手把手教你实现自己Unity2D游戏寻路逻辑
还在看别人的寻路逻辑?保姆级教程,一步步教你实现网格寻路逻辑。
724 0
Unity 之 手把手教你实现自己Unity2D游戏寻路逻辑
|
图形学
带你读《Unity游戏开发(原书第3版)》之二:游戏对象
本书主要介绍Unity2018的使用和游戏开发流程中涉及的各种知识。每一章的结构特别清晰,先综述该章要介绍的内容,然后一步步深入讲解,中间穿插着很多动手做的实践操作,可以让读者加深对某个概念、方法的理解,每章的最后还有一个小测验和一个稍微大一点的实践练习,用于巩固该章的学习内容。
|
图形学
unity案例入门(拾取游戏)
案例简述这个案例实现一个非常简单的拾取宝物游戏,主角是一个小球,玩家通过键盘控制小球拾取全部宝物。 键盘控制物体移动 Rigidbody rd;public int force = 10; void Start () {rd = GetComponent ();//获得物体的刚体组件}void Update () {float h = Input.
1569 0