加载中…
个人资料
  • 博客等级:
  • 博客积分:
  • 博客访问:
  • 关注人气:
  • 获赠金笔:0支
  • 赠出金笔:0支
  • 荣誉徽章:
正文 字体大小:

14.碰撞检测(一)

(2013-04-30 17:58:54)
标签:

android开发

分类: libGDX开发教程
本文使用的libgdx是0.98版本,可能和最新版有一些不同地方。全文内容仅供参考。

一个冒险类的游戏,有地图和背景是必须的,但是地图难道仅仅只是作为装饰用吗?答案当然是否定的,一个完美的游戏,地图不仅仅只有装饰作用。例如我们常见的地图处理就有很多,比如地面碰撞、传送门、定点爆炸等,这些都是和地图仅仅联系起来的。作为libgdx御用的地图编辑器,libgdx对TileMap也进行了详细的封装,为了使我们更加方便的使用TileMap来进行碰撞检测。今天土豆这里就简单的介绍下libgdx中碰撞检测是如何实现的。

Libgdx游戏引擎(1群):187378034
Libgdx游戏引擎(2群):148848483
Libgdx游戏引擎(3群): 79168470

                       视频教程

1.二维数组 Tiles

碰撞检测首先要提到的就是这个二维数组,为什么他特别重要呢?这个数组在前面的博文中,也是给大家介绍了,它是一个 Tilelayer类中我为我们封装的,来方便我们查找每一个单元格的二维数组。

(1)Tiles 数组分析

Tiles数组实际上是将指定像素的地图,分割成行列数不同的矩形方块,同时将每一个方块都进行编号,例如:起点的单元格编号就是tile[0][0]。这样确定一个单元格的位置,如果他是第M行、第N列的单元格,那么它在libgdx中的去数组编号就是-----tile[m-1][n-1].

如图:



(2)图块单元格

在 TileMap中我们使用图层的方式来实现地图的编辑,图块是地图的基本组成单位。在libgdx的tiles数组中,如果一个tile内部有图块的话,那么它的返回值就是1,如果没有,那么他的返回值就是0。例如:起点tile[0][0]内部没有图块,那么我们得到 tile[0][0] = 0,法相反如果有图块的话那么tile[0][0] = 1。

如图:

2.碰撞检测原理

根绝上面的知识我们知道了libgdx已经给我们封装好了一个数组,而且这个数组也有编号,只是这个编号的的x、y、都需要我们进行减一换算才能确定目标。同样的,一般我的游戏主角在移动过程中都有自己的坐标,那么我们可不可以利用这个坐标来确定人物在那个图层呢?这是当然可以的了。

(1)检测点

什么是检测点呢?所谓的检测点,其实就是用于确定游戏主角所在单元格位置的坐标。大家都知道,我们的游戏主角一般的都是可以放在一个矩形的内部,可以实现包裹。主角像右移动就检测矩形右侧边的2个点(建议不要设置端点),向上移动就检测就检测矩形上方边的2个点(建议不要设置端点),以此类推。

PS:不推荐设置端点的原因,是因为可能出现端点和单元格端点重合这种情况,这样很难判断是相邻的2个方向向哪侧检测。大家可以将坐标点减去0.03 这样就不是端点了。不过原理还是取端点,只是坐标移动了一下,这样比较方便。

影响检测点个数的因素主要有2个:

(1)包裹游戏主角的矩形大小。
(2)地图中单元格面积的大小。

如果我们包裹游戏主角的矩形面积小于单元格(tile)的面积,那么我们只需要检测4个点。但是并不是移动的时候4个点全部检测,例如向右移动仅仅检测右侧2点、向下移动仅仅检测下面2点,以此类推。相对来说主角矩形面积小于单元格还是比较简单的。

如图:



如果我们包裹主角的矩形面积大于单元格的时候,那么我们就要检测更多的点了,比如我们主角的矩形是1.5倍的单元格,那么我们要检测9个点,原理同上。包裹矩形越大,检测点就越多,这样是为了精确,防止主角头部脚部过不去,但是身子却可以通过地图的情况,就是为了防止这种情况,才需要检测中间的点。推荐大家使用1.5倍的包裹矩形。

如图:



(2)检测原理

通过上面的分析,我们了解了检测点的情况。利用检测点,我们可以了解到,游戏主角在哪一个单元个的内部。同样也分为二种情况:

(1)主角的包裹矩形大小小于单元格的面积,移动中判断移动方向对应的边上2点所在单元格处,是否有图块,通过布尔类型的返回值来实现,如果有图块就不移动,如果没有就移动。

(1)主角的包裹矩形大小大于单元格的面积,移动中判断移动方向对应的边上3点所在单元格处,是否有图块,通过布尔类型的返回值来实现,如果有图块就不移动,如果没有就移动。

详细实现,见下文实例。


2.实例演示

为了方便大家理解,这次我们同样也使用之前博客的超级玛丽的例子。之前我们的超级玛丽移动,并没有实现和地图的检测,只是简单的的设置了坐标,通过坐标来解决人物移动障碍的问题,但是在实际游戏处理中,这样设置坐标是不实际的,你不可能对每一个地图的不同图款做坐标处理,这样做也好似非常愚蠢的。通过上面的分析,相信我们已经知道该如何实现超级玛丽的碰撞检测了,下面土豆就带大家实现依稀超级玛丽的碰撞检测的具体实现。

(1)游戏素材

为了方便我们实现碰撞检测,这次我们选取单元格为 48像素,新建一个地图。同时我们重新处理一下Mario的素材图片,将人物的位置调换一下,将超级玛丽的包裹矩形设置为 64(约为单元格的1.5倍).

素材如图:

(1)新地图                         (2)超级玛丽素材


(2)新建一个地图和对象层,同时设置对象属性为Mario。

如图:



(3)新建一个Collision(碰撞)类,同时创建一个静态语句,实习map的实例化和tile数组的实例。

如图:


(4)选取9个点作为碰撞检测的点,代码中我使用的是 M 和 N,为了大家方便理解,这个点实际传入的是主角的坐标,我在途中用 x 和 y 来表示,方便大家理解。

如图:



(5)创建静态方法 leftEnable 和 downEnable 来实现检测单元格是否有图块,如果有那么就不能通过,返回false。如果没有图块,那么就可以通过,返回true。

即:  能通过----返回 true 。
      不能通过 ---- 返回 false  


如图:

(a)

(b)


(6)方法讲解

(1)首先,我们讲一下这个 “int m1 = MathUtils.ceilPositive((x + 12/ map.tileWidth);”这句话,MathUtils.ceilPositive(delta)是对delta这一参数进行加 1 取整运算,这样第一个点所在单元格的x坐标就计算出来了。同理计算y的,这样计算出来的 tile[m-1][y -1] 就是单元格的坐标。如果这个值d等于0 就是没有图块,等于1就是有图块。  

(2)int m1 = MathUtils.ceilPositive((x + 12/ map.tileWidth);”这里的 12 就是微调,我并没有选取端点而是加了 12 将点移动到对应边内部,这样不会出现端点重合的情况,推荐大家这样设置。

(3)只有3个点有一个不是0的情况下,马里奥都是不可以运动过去的。反之,可以运动。

如图:


(7)模拟重力,重写Mario类中的update()方法,实现一个y坐标一直自加的状态。当然,当检测到下面有图来块的情况下,就不自加,就停止,这样就形成了地面可以站立的效果。
     
    加入向左移动的碰撞检测,当人物的状态是向左的时候同时检测左侧没有图块,这样才可以移动,都是通过调用Collision类中的静态方法来实现的。

   向右移动,这里为了区分碰撞检测效果,土豆这里没有做碰撞检测,只是当有向右移动的状态的时候,实现向右的坐标自加。

如图:



(8)重写act()方法,调用update()方法,同时 实现 statetime 和系统时间delta 递增。delta是stage中的间隔时间。


如图:

这样一个简单的碰撞检测就实现了,这里向右的碰撞检测,希望同学们学习了以后,可以自己完成,基本的理论就是这样,希望能帮助大家理解碰撞检测。



3.完整代码

(1)TiledMapTest 类中的代码。

package com.potato;

03 import com.badlogic.gdx.ApplicationListener;
04 import com.badlogic.gdx.Gdx;
05 import com.badlogic.gdx.graphics.GL10;
06 import com.badlogic.gdx.graphics.OrthographicCamera;
07 import com.badlogic.gdx.graphics.g2d.tiled.TileAtlas;
08 import com.badlogic.gdx.graphics.g2d.tiled.TileMapRenderer;
09 import com.badlogic.gdx.graphics.g2d.tiled.TiledLoader;
10 import com.badlogic.gdx.graphics.g2d.tiled.TiledMap;
11 import com.badlogic.gdx.graphics.g2d.tiled.TiledObject;
12 import com.badlogic.gdx.graphics.g2d.tiled.TiledObjectGroup;
13 import com.badlogic.gdx.scenes.scene2d.Stage;
14 
15 public class TiledMapTest implements ApplicationListener {
16 
17     public static TiledMap map;
18     TileAtlas atlas;
19     TileMapRenderer render;
20     OrthographicCamera cam;
21     Stage stage;
22     Mario mario;
23 
24     @Override
25     public void create() {
26 
27         map TiledLoader.createMap(Gdx.files.internal("data/1.tmx"));
28 
29         atlas new TileAtlas(map, Gdx.files.internal("data/"));
30 
31         render new TileMapRenderer(map, atlas, 1010);
32 
33         cam new OrthographicCamera();
34 
35         cam.setToOrtho(false480336);
36 
37         stage new Stage(480320false);
38 
39         mario new Mario(00);
40 
41         setMario();
42 
43         stage.addActor(mario);
44 
45         stage.addActor(mario.buttonL);
46 
47         stage.addActor(mario.buttonR);
48 
49         Gdx.input.setInputProcessor(stage);
50     }
51 
52     @Override
53     public void dispose() {
54 
55     }
56 
57     public void setMario() {
58         for (TiledObjectGroup group map.objectGroups{
59             for (TiledObject object group.objects{
60                 if ("mario".equals(object.name)) {
61                     mario.x object.x;
62                     mario.y render.getMapHeightUnits() object.y;
63                 }
64             }
65         }
66     }
67 
68     @Override
69     public void render() {
70         Gdx.gl.glClearColor(0000);
71         Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
72 
73         render.render(cam);
74         stage.act();
75         stage.draw();
76 
77     }
78 
79     @Override
80     public void resize(int width, int height) {
81     }
82 
83     @Override
84     public void pause() {
85     }
86 
87     @Override
88     public void resume() {
89     }
90 }

(2)Mario 类中的代码。

package com.potato;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.GL10;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.Animation;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.scenes.scene2d.Actor;
import com.badlogic.gdx.scenes.scene2d.Group;
import com.badlogic.gdx.scenes.scene2d.InputEvent;

import com.badlogic.gdx.scenes.scene2d.InputListener;
import com.badlogic.gdx.scenes.scene2d.ui.ImageButton;
import com.badlogic.gdx.scenes.scene2d.utils.ClickListener;


import com.badlogic.gdx.scenes.scene2d.utils.TextureRegionDrawable;

public class Mario extends Actor{
   public static float ;
   public static float ;
   public float statetime;
   Texture texture;
   TextureRegion currentFrame;
   
   ImageButton buttonL;
   ImageButton buttonR;
   
   Animation aniRight;
   Animation aniLeft;
   Animation aniIdle;
   
   STATE state;

   enum STATE {
       Left,Right,Idel
   };
   
   public Mario(float x,float y) {

       this.x x;
       this.y y;
       this.statetime 0;
       this.show();
       state STATE.Idel;
       
   }
   
   @Override
   public void act(float delta) {
       
       statetime += delta;    
       this.update();
       this.check();
       super.act(delta);
   }
   
   @Override
   public void draw(SpriteBatch batch, float parentAlpha) {

       batch.draw(currentFrame, x, y);
   }

   public void update() {
       //能否像下走。
       if(Collision.downEnable(this.xthis.y)) {
           this.y -=6.5*statetime;
       }
       //能否像左走
       if(state == STATE.Left&&Collision.leftEnable(x, y, 6464)) {
           this.x -=1.5f;
       }
       
       if(state == STATE.Right{
           this.x +=1.5f;
       }

   }
   
   public void check() {
       if(state == STATE.Left{
           currentFrame aniLeft.getKeyFrame(statetime, true);
       }else if (state == STATE.Right{
           currentFrame aniRight.getKeyFrame(statetime, true);
       }else if (state == STATE.Idel{
           currentFrame aniIdle.getKeyFrame(statetime, true);
       }
       
   }
   
   public void show() {
       texture new Texture(Gdx.files.internal("data/Mario.png"));
       
       TextureRegion[][] spilt TextureRegion.split(texture, 6464);
       TextureRegion[][] miror TextureRegion.split(texture, 6464);
       
       for(TextureRegion[] region1 miror){
           for(TextureRegion region2 region1){
               region2.flip(truefalse);
           }
       }
       //右
       TextureRegion[] regionR new TextureRegion[3];
           regionR[0spilt[0][1];
           regionR[1spilt[0][2];
           regionR[2spilt[0][0];
       aniRight new Animation(0.1fregionR);
       //左
       TextureRegion[] regionL new TextureRegion[3];
       regionL[0miror[0][1];
       regionL[1miror[0][2];
       regionL[2miror[0][0];
       aniLeft new Animation(0.1fregionL);
       //空闲
       TextureRegion[] regionI new TextureRegion[1];
       regionI[0spilt[0][0];

       aniIdle new Animation(0.1fregionI);
   
       buttonL new ImageButton(new TextureRegionDrawable(spilt[1][0]), new TextureRegionDrawable(spilt[1][1]));
       buttonR new ImageButton(new TextureRegionDrawable(miror[1][0]), new TextureRegionDrawable(miror[1][1]));
       
       buttonL.setPosition(2020);
       buttonR.setPosition(10020);
       
       buttonL.addListener(new InputListener(){

           @Override
           public void touchUp(InputEvent event, float x, float y,
                   int pointer, int button) {
               // TODO Auto-generated method stub
               state STATE.Idel;
               super.touchUp(event, x, y, pointer, button);
           }

           @Override
           public boolean touchDown(InputEvent event, float x, float y,
                   int pointer, int button) {
               // TODO Auto-generated method stub
               state STATE.Left;
               return true;
           }
           
       });
       buttonR.addListener(new InputListener(){
           @Override
           public void touchUp(InputEvent event, float x, float y,
                   int pointer, int button) {
               // TODO Auto-generated method stub
               state STATE.Idel;
               super.touchUp(event, x, y, pointer, button);
           }
           @Override
           public boolean touchDown(InputEvent event, float x, float y,
                   int pointer, int button) {
               // TODO Auto-generated method stub
               state STATE.Right;
               return true;
           }
           
       });
       
   
   }
   
   
}

3)Collision 类中的代码。

package com.potato;

import com.badlogic.gdx.graphics.g2d.tiled.TiledMap;
import com.badlogic.gdx.math.MathUtils;

public class Collision {
   
   public static int tile[][];
   public static TiledMap map;

   static{
       map TiledMapTest.map;
       tile map.layers.get(0).tiles;
   }
   
   
   public static boolean leftEnable (float x,float y,float width,float height) {
       int m1 MathUtils.ceilPositive((x) map.tileWidth); //
       int n1 tile.length MathUtils.ceilPositive((y) map.tileHeight);

       int m7 MathUtils.ceilPositive((x) map.tileWidth); 
       int n7 tile.length
               MathUtils.ceilPositive((y height 2map.tileHeight);

       int m8 MathUtils.ceilPositive((x) map.tileWidth); 
       int n8 tile.length
               MathUtils.ceilPositive((y height) map.tileHeight);
       
       
       if (tile[n1][m1 1!= 0 || tile[n7][m7 1!= 0
               || tile[n8][m8 1!= 0{
           return false;
       else
           return true;
   }


   public static boolean downEnable(float x, float y)              

       int m1 MathUtils.ceilPositive((x 12map.tileWidth); 
       int n1 tile.length MathUtils.ceilPositive((y 1map.tileHeight);

       int m2 MathUtils.ceilPositive((x 24map.tileWidth);
       int n2 tile.length MathUtils.ceilPositive((y 1map.tileHeight);

       int m3 MathUtils.ceilPositive((x 40map.tileWidth); 
       int n3 tile.length MathUtils.ceilPositive((y 1map.tileHeight);

       if (tile[n3][m3 1!= 0 || tile[n2][m2 1!= 0
               || tile[n1][m1 1!= 0{
           return false;
       else {
           return true;
       }
   }
}


效果图:(同样土豆这里用视频,方便大家看效果)


源码下载:http://pan.baidu.com/share/link?shareid=427941&uk=3456175979

写在最后,关于libgdx中地图的碰撞检测就给大家介绍到这里,希望对大家有所帮助。下一篇是是关于libgdx框架中《碰撞检测(二)》的博文,我会介绍下0.99版本碰撞是如何实现的。如果大家有什么问题可以加我QQ:316344618,一起讨论下。PS:五一长假,祝大家节日快乐,好好休息哦~

                             ------------奋斗小土豆丶
                                                                                                          2013 年 4 月 30 日

0

阅读 收藏 喜欢 打印举报/Report
  

新浪BLOG意见反馈留言板 欢迎批评指正

新浪简介 | About Sina | 广告服务 | 联系我们 | 招聘信息 | 网站律师 | SINA English | 产品答疑

新浪公司 版权所有