博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
libgdx游戏引擎开发笔记(十二)SuperJumper游戏例子的讲解(篇六)---- .游戏主人公创建以及碰撞检测...
阅读量:6091 次
发布时间:2019-06-20

本文共 8594 字,大约阅读时间需要 28 分钟。

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


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!到这里!我们运行一下代码,看一下效果:

   但是,主角不能被控制,貌似很尴尬。。。 别着急,我们这就给它加上操控的方法,在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);
        
}
                                                                                                                                                                                                                                                                                                                                                                                                                                                                    
    
}

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

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


我们看一下以前的代码:

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中游戏物体来绘制游戏画面,在这里作者把不同的物体分开来,让代码更为清晰。


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

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


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


源码下载:

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

你可能感兴趣的文章
我的友情链接
查看>>
用yum安装mariadb
查看>>
一点IT"边缘化"的人的思考
查看>>
WPF 降低.net framework到4.0
查看>>
搭建一个通用的脚手架
查看>>
开年巨制!千人千面回放技术让你“看到”Flutter用户侧问题
查看>>
开源磁盘加密软件VeraCrypt教程
查看>>
本地vs云:大数据厮杀的最终幸存者会是谁?
查看>>
阿里云公共镜像、自定义镜像、共享镜像和镜像市场的区别 ...
查看>>
shadowtunnel v1.7 发布:新增上级负载均衡支持独立密码
查看>>
Java线程:什么是线程
查看>>
mysql5.7 创建一个超级管理员
查看>>
【框架整合】Maven-SpringMVC3.X+Spring3.X+MyBatis3-日志、JSON解析、表关联查询等均已配置好...
查看>>
要想成为高级Java程序员需要具备哪些知识呢?
查看>>
带着问题去学习--Nginx配置解析(一)
查看>>
onix-文件系统
查看>>
java.io.Serializable浅析
查看>>
我的友情链接
查看>>
多线程之线程池任务管理通用模板
查看>>
CSS3让长单词与URL地址自动换行——word-wrap属性
查看>>