简体   繁体   English

libGDX:如何处理Tiled对象层中的精灵碰撞?

[英]libGDX: How to handle collisions for sprites from the Tiled object layer?

I am using libGDX and Tiled to make an RPG. 我正在使用libGDXTiled制作一个RPG。 I already have a lot of stuff working: title screen, a testing screen, that has my map loaded on it. 我已经有很多工作了:标题屏幕,测试屏幕,上面已经加载了我的地图。 I can read the properties I put onto the map and on certain tiles. 我可以阅读放置在地图和某些图块上的属性。 I can also move around the map and everything, but what I am trying to figure out now is: 我还可以在地图和所有内容之间移动,但是现在我想弄清楚的是:

How do I render a map object from the object layer and handle collisions? 如何从对象层渲染地图对象并处理碰撞?

I am wanting to use Tiled's Object layer for my collision layer. 我想将Tiled的Object层用于碰撞层。 IE: put shapes around certain tiles/areas that I don't want the characters to be able to pass through. IE:在某些我不希望角色能够通过的图块/区域周围放置形状。

This is what I have so far: 这是我到目前为止的内容:

package rawct.awakening;

import java.lang.reflect.Method;
import java.util.Iterator;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.maps.MapObject;
import com.badlogic.gdx.maps.MapObjects;
import com.badlogic.gdx.maps.tiled.TiledMap;
import com.badlogic.gdx.maps.tiled.TiledMapTile;
import com.badlogic.gdx.maps.tiled.TiledMapTileLayer;
import com.badlogic.gdx.maps.tiled.TiledMapTileLayer.Cell;
import com.badlogic.gdx.maps.tiled.renderers.OrthogonalTiledMapRenderer;
import com.badlogic.gdx.maps.tiled.TiledMapTileSet;
import com.badlogic.gdx.maps.tiled.TmxMapLoader;
import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.utils.ObjectMap;

public class GameMap extends TiledMap {
    private String TAG = "GameMap";
    private TmxMapLoader loader = new TmxMapLoader();
    private OrthogonalTiledMapRenderer mRender;

    private TiledMap gamemap;
    private TiledMapTileLayer mapTiles;

    private ObjectMap<TiledMapTile, Boolean> Blocked;
    private ObjectMap<TiledMapTile, Boolean> Event;

    private MapObjects mObjects = new MapObjects();
    private MapObject mObj;

    public void draw(OrthographicCamera cam){
        mRender.setView(cam);
        mRender.render();
    // Should render my map object?
        mRender.renderObject(mObj);
    }

    public GameMap(String Map){
        Blocked = new ObjectMap<TiledMapTile, Boolean>();
        Event = new ObjectMap<TiledMapTile, Boolean>();

        gamemap = loader.load("maps/"+Map+".tmx");
        mRender = new OrthogonalTiledMapRenderer(gamemap);
        loadMap(gamemap);
    }

    private Cell getCellAt(float x, float y){
        return mapTiles.getCell((int)x, (int)y);
    }

    private TiledMapTile getTileAt(float x, float y){
        Cell cell = getCellAt(x, y);
        return cell != null ? cell.getTile() : null;
    }

    public boolean isTileBlocked(float x, float y){
        try {
            return Blocked.get(getTileAt(x, y));
        } catch (Exception e) {
            Gdx.app.log(TAG, e.toString());
            return false;
        }
    }

    private void loadMap(TiledMap map) {
        String sI = null;
        Blocked.clear();
        Event.clear();

        try{
            mapTiles = (TiledMapTileLayer)map.getLayers().get(0);
            mObjects = map.getLayers().get("Testing").getObjects();
        } catch (Exception e) {
            Gdx.app.log(TAG, e.toString());
        }


        Gdx.app.log(TAG, "Objects:"+mObjects.getCount());

        for(Iterator<MapObject> mObjs = mObjects.iterator(); mObjs.hasNext();){
        // I have set just about everything possible(I only have one object at the moment so mObj only gets set once.
            mObj = mObjs.next();
            Gdx.app.log(TAG, "Obj:"+mObj.getName());
            mObj.setColor(Color.GREEN);
            mObj.setOpacity(1f);
            mObj.setVisible(true);

//          try {
//              Method Test = getClass().getDeclaredMethod((String) mObj.getProperties().get("Func"));
//              Test.invoke(this);
//          } catch (Exception e) {
//              Gdx.app.log(TAG, e.toString());
//          }
        }

        Array<String> sTilesets = new Array<String>();
        TiledMapTile tile;

        try {
            for(Iterator<TiledMapTileSet> tilesets = map.getTileSets().iterator(); tilesets.hasNext();){
                    sI = tilesets.next().getName();
                    sTilesets.add(sI);
            }

            int tCount = sTilesets.size;
            for(int i = 0; i < tCount; i++){
                for(Iterator<TiledMapTile> tiles = map.getTileSets().getTileSet(sTilesets.get(i)).iterator(); tiles.hasNext();){
                tile = tiles.next();

                if(tile.getProperties().containsKey("blocked")){
                    //Gdx.app.log(TAG, "Tile:" + tile.getId() + " blocked!");
                }

                if(tile.getProperties().containsKey("name")){
                    //Gdx.app.log(TAG, "Name:" + tile.getProperties().get("name"));
                }

                boolean blocked = Boolean.parseBoolean(tile.getProperties().get("blocked", "false", String.class));
                boolean event = Boolean.parseBoolean(tile.getProperties().get("event", "false", String.class));

                Blocked.put(tile, blocked);
                Event.put(tile, event);
            }
        }
            Gdx.app.log(TAG, "Map Loaded!");
        } catch (Exception e) {
            Gdx.app.log(TAG, "Error:" + e.toString());
        }
    }

    public TiledMap getMap(){
        return gamemap;
    }
}

I answered a similar question over on GameDev recently, having been stymied by the same problem for a while. 我最近在GameDev上回答了一个类似的问题 ,因为一段时间以来一直被同一问题所困扰。 A thorough Googling eventually led me to a tutorial on the subject. 彻底的Google搜寻最终使我获得有关该主题的教程 And I do mean thorough. 我的意思是彻底的。 It seems to be the only solution to this problem on the entire Internet. 这似乎是整个Internet上解决此问题的唯一方法。 Weird. 奇怪的。

Anyway. 无论如何。 The answer I gave and the tutorial it was based on both use Box2d to handle collision detection. 我给出的答案以及基于它的教程都使用Box2d来处理碰撞检测。 I'd wholeheartedly suggest going that route if you're not too far into making your game: Box2d handles quite a bit for you if you use it, but it does mean rethinking a lot of your existing code. 全心全意地建议您在不做游戏的前提下走那条路:如果使用Box2d,Box2d会为您处理很多事情,但这确实意味着要重新考虑许多现有代码。 Everything to do with movement, basically. 基本上,一切都与运动有关。 If that's what you choose to do, the good old LibGDX wiki can get you started (that's a direct link to the relevant article). 如果您选择这样做,那么老式的LibGDX Wiki可以使您入门(这是指向相关文章的直接链接)。

If you don't want to go down that rabbit hole, then the links above still suggest a solution. 如果您不想掉进那个兔子洞,那么上面的链接仍然建议解决方案。 Where I think you're going wrong is in trying to just draw the MapObject s directly. 我认为您要出错的地方是尝试直接绘制MapObject Try this: 尝试这个:

MapObjects objects = map.getLayers().get("Obstacles").getObjects();

for(MapObject object : objects) {
    if (object instanceof RectangleMapObject) {
        Rectangle rect = ((RectangleMapObject) object).getRectangle();
        // do something with rect...
    }
    else if (object instanceof PolygonMapObject) {
        Polygon polygon = ((PolygonMapObject) object).getPolygon();
        // do something with polygon...
    }
    else if (object instanceof PolylineMapObject) {
        Polyline chain = ((PolylineMapObject) object).getPolyline();
        // do something with chain...
    }
    else if (object instanceof CircleMapObject) {
        Circle circle = ((CircleMapObject) object).getCircle();
        // do something with circle...
    }
}

From there you deal with the generated shapes (the Rectangle or what have you) rather than the MapObject s. 从那里,您处理生成的形状( Rectangle或您拥有的形状),而不是MapObject They have the methods useful for collision detection, like overlaps() , contains() and the like. 它们具有对冲突检测有用的方法,例如overlaps()contains()等。 If you're only dealing with one type of shape, then it makes life easier in a non-Box2d game. 如果您只处理一种形状,那么在非Box2d游戏中它会使生活变得更轻松。 Use only the if statement for PolygonMapObject , for instance, and drop the other three. 例如,仅对PolygonMapObject使用if语句,并删除其他三个语句。



I guess you need a kind of "mapping" for the blocked tiles. 我想您需要一种“映射”来阻止砖块。 So you cant always check if the tile is blocked or not. 因此,您无法始终检查瓷砖是否被阻塞。 This would take to long to get the tile. 这将花费很长时间才能获得拼贴。
To do so there are different ways but i used this. 这样做有不同的方法,但是我用了这个。
I wrote my own "Map". 我写了自己的“地图”。 This Map holds a Array at the dimension of my current TiledMap. 该地图在我当前的TiledMap的尺寸处包含一个数组。 When i load a Map i do set a value inside of that mapArray so i know, if i can move on that Tile or not. 当我加载地图时,我确实在该mapArray内设置了一个值,所以我知道是否可以在该Tile上移动。 This does look a bit awefull and take some time at bigger maps but it does work. 这看起来确实很糟糕,并且在较大的地图上需要花费一些时间,但确实可行。

private void setBlocked() {
    for (int i = 0; i < this.map.getLayers().getCount(); i++) {
        TiledMapTileLayer layer = (TiledMapTileLayer) this.map.getLayers()
                .get(i);
        for (int y = 0; y < layer.getHeight(); y++) {
            for (int x = 0; x < layer.getWidth(); x++) {
                if (layer.getCell(x, y) != null
                        && layer.getCell(x, y).getTile().getProperties()
                                .containsKey("blocked")) {
                    this.mapArray[x][y] = Config.CANTMOVEONPOSITION;
                }
            }
        }
    }
}

To debug i render squares and do fill them in the color of the "mapState" likes this: 要调试,我渲染正方形并以“ mapState”的颜色填充正方形,如下所示:

if (this.map != null) {
            for (int i = 0; i < this.map.width; i++) {
                for (int j = 0; j < this.map.height; j++) {
                    switch (this.map.mapArray[i][j]) {
                    case Config.CHARSTATE:
                        // green
                        debugger.setColor(0, 1f, 0, Config.DEBUG_ALPHA);
                        break;
                    case Config.CANTMOVEONPOSITION:
                        // black
                        debugger.setColor(1f, 1f, 1f, Config.DEBUG_ALPHA + 0.1f);
                        break;
                    case Config.MONSTERSTATE:
                        // red
                        debugger.setColor(1f, 0, 0, Config.DEBUG_ALPHA);
                        break;
                    case Config.MOVETONEXTMAP:
                        // yellow
                        debugger.setColor(1f, 1f, 0, Config.DEBUG_ALPHA);
                        break;
                    default:
                        debugger.setColor(0, 0, 0, 0);
                        break;
                    }
                    debugger.rect(i * Config.TILE_SIZE, j * Config.TILE_SIZE,
                            Config.TILE_SIZE, Config.TILE_SIZE);
                }
            }
            debugger.end();
        }

this isnt optimized! 这还没有优化! So if you have a big map and just show a small frame it still does draw every square. 因此,如果您有一张大地图并且只显示一个小框,它仍然会绘制每个正方形。 The height and width in this case are the tilecount in height and width. height and width在这种情况下是在高度和宽度的tilecount。
I hope this does answer your question or you can find your way with that help. 我希望这能回答您的问题,或者您可以在该帮助下找到自己的出路。
Regards 问候

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM