0
\$\begingroup\$

I'm fairly new to Libgdx and have some good basic knowledge on Java. Right now I am stuck on how to add collision to my TiledMap, I am using the Mapeditor to create the tilemap. I read so many different ways how to approach this problem, but it is very difficult to understand how exactly this works.

Then I read about using Box2d for collision detection. Transforming the tiles I want into static bodys and creating a dynamic body for my player.

Right now I have created a MapBodyBuilder, wich should transform a tile layer into a specific box2d object. My problem is now, I don't know where I load this mapBuilder, how I can make my already set up Player to a dynamic body, where to create the player and how to combine the static tile bodies and the player body.

Hope someone can help me out!

As you can see I already tried myself didn't work tho .... I didn't post the whole player class, because most of it is for animation and userInput.

EDIT :.... I don't know what to do anymore.

public class MapBodyBuilder {

    static Array <Body> bodies = new Array<Body>();

    private static float ppt = 0;       // pixels per unit

    public static Array<Body> buildShapes(Map map, float pixels, World world) {
        ppt = pixels;

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

        for (MapObject object : objects) {

            if (object instanceof TextureMapObject) {

                continue;

            }

            Shape shape;

            if (object instanceof RectangleMapObject) {
                shape = getRectangle((RectangleMapObject) object);
            }

    //      else if (object instanceof PolygonMapObject) {
                //      shape = getPolygon((PolygonMapObject) objects);

            //}

        /*  else if (object instanceof CircleMapObject) {
                shape = getCircle((CircleMapObject) objects);
            }  */

    //      else if (object instanceof PolylineMapObject) {
    //          shape = getPolyline((PolylineMapObject) objects);
        //  }

            else {
                continue;

            }

            BodyDef bd = new BodyDef();
            bd.type = BodyType.StaticBody;
            Body body = (Body) world.createBody(bd);
            ((com.badlogic.gdx.physics.box2d.Body) body).createFixture(shape, 1);

            bodies.add(body);

            shape.dispose();

        }

        return bodies;

    }

    private static PolygonShape getRectangle(RectangleMapObject rectangleObject) {
        Rectangle rectangle = rectangleObject.getRectangle();
        PolygonShape polygon = new PolygonShape();
        Vector2 size = new Vector2((rectangle.x + rectangle.width * 0.5f) / ppt, (rectangle.y + rectangle.height * 0.5f) / ppt);

        polygon.setAsBox(rectangle.width * 0.5f / ppt, rectangle.height * 0.5f / ppt);

        return polygon;
    }

    /*private static CircleShape getCircle(CircleMapObject circleObject) {
        Circle circle = new circleObject.getCircle();
        CircleShape circleShape = new CircleShape();
        circleShape.setRadius(circle.radius / ppt);
        circleShape.setPosition(new Vector2(circle.x / ppt, circle.y / ppt));

        return circleShape;

    } */

    private static PolygonShape getPolygon(PolygonMapObject polygonObject) {
        PolygonShape polygon = new PolygonShape();
        float[] vertices = polygonObject.getPolygon().getTransformedVertices();

        float[] worldVertices = new float[vertices.length];

        for (int i = 0; i < vertices.length / 2; i++) {

            System.out.println(vertices[i]);
            worldVertices[i] = vertices[i] / ppt;

        }

        polygon.set(worldVertices);
        return polygon;

    }

    private  static ChainShape getPolyline(PolylineMapObject polylineObject) {
        float[] vertices = polylineObject.getPolyline().getTransformedVertices();
        Vector2[] worldVertices = new Vector2[vertices.length / 2];


        for (int i = 0; i < vertices.length / 2; i++) {
            worldVertices[i] = new Vector2();
            worldVertices[i].x = vertices[i * 2 ] / ppt;
            worldVertices[i].y = vertices[i * 2 + 1] / ppt;
    }


        ChainShape chain = new ChainShape();
        chain.createChain(worldVertices);
        return chain;

    }
}

public class WorldRenderer {

    // rectangle 

    TiledMapTileLayer tilelayer;

    private ShapeRenderer sRDebugRectangel = new ShapeRenderer();


    private static final float UNIT_SCALE = 6 / 16f;
    public SpriteBatch batch;
    private OrthogonalTiledMapRenderer renderer;
    private OrthographicCamera camera;

    private World world;


    private MapBodyBuilder bodyBuilder;
    private Player player;
    private Level level;

    public WorldRenderer() {

        level = new Level("Leveltry.tmx");
        renderer = new OrthogonalTiledMapRenderer(level.getMap(), UNIT_SCALE);


//      int columns = tilelayer.getWidth();
    //  int row = tilelayer.getHeight();

        //Cell cell  =  tilelayer.getCell(columns, row);


         world  = new World(null, false);


        bodyBuilder = new MapBodyBuilder();

        player = new Player();

        /*BodyDef bodyDef = new BodyDef();
        bodyDef.type = BodyType.DynamicBody;

         Body body = world.createBody(bodyDef);

        CircleShape circle = new CircleShape();
        circle.setRadius(8); */

        //player.world = new World(null, false);


        camera = new OrthographicCamera();
        camera.setToOrtho(false, 200, 100);

        camera.update();

        batch = new SpriteBatch();

    //  Rectangle playerRect = new Rectangle(player.position.x, player.position.y, 32, 32);


    }

    public void render(float delta) {


       // bodyBuilder.buildShapes(level.getMap(), 32, world);
        //world.getBodies(null);

        camera.position.set(player.getX(), player.getY(), 0);
        batch.setProjectionMatrix(camera.combined);


        renderer.setView(camera);

        camera.update();


        renderer.render();

        player.render(batch, delta);




        //System.out.println(player.position.x + " " + player.position.y);
    }

    public void dispose() {
        batch.dispose();
    }

}

public Player() {


       // createBody();

        position = new Vector2(200, 100);

        createPlayer();
    }

    public void createPlayer() {



        tex = new Texture("angel.png");
        frames = TextureRegion.split(tex, 48, 48);

        walkingHorizontalLeft = new Animation(0.2f, frames[1]);
        walkingHorizontalRight = new Animation(0.2f, frames[2]);
        walkingVeritcalDown = new Animation(0.2f, frames[0]);
        walkingVerticalUp = new Animation(0.2f, frames[3]);

        animations = new Animation[4];

        animations[0] = walkingHorizontalLeft;
        animations[1] = walkingHorizontalRight;
        animations[2] = walkingVeritcalDown;
        animations[3] = walkingVerticalUp;

    }


public void render(SpriteBatch batch, float delta) {

        handleInput();

        frameTime += delta;

        position.x += velocity.x * delta; //distance travelled is equal to the velocity * the time taken
        position.y += velocity.y * delta;

        velocity.x = (float) (velocity.x * 0.8f);
        velocity.y = (float) (velocity.y * 0.8f);

        boolean idle;
        //Lets check current state
        if ((Math.abs(velocity.x) + Math.abs(velocity.y)) < 4f) { //Some small value)
            //Its idle
            idle = true;
        } else {
            //Its moving
            idle = false;
        }

        if (idle) {
            currentFrame = animations[facing].getKeyFrame(frameTime, false);//idleAnimation.getFRame(...), or maybe just a non moving region, whatever you want
        } else {

            currentFrame = animations[facing].getKeyFrame(frameTime, true);
        }
        //So Ive created the "facing" short variable, we can encode the facing direction to this variable
        //Value of 0 left
        //Value of 1 right
        //value of 2 up
        //value of 3 down


        // System.out.println(velocity.x + velocity.y);
        batch.begin();
        batch.draw(currentFrame, position.x, position.y, 15, 15);
        batch.end();

        //System.out.println("this is x   " + position.x + "this is y   "  + position.y);

    }


/*  private void createBody() {
             BodyDef bodyDef = new BodyDef();
             bodyDef.type = BodyType.DynamicBody;

             Body body = world.createBody(bodyDef);

             CircleShape circle = new CircleShape();
             circle.setRadius(8);

    }  */
\$\endgroup\$
1
\$\begingroup\$

I realise this is five months old, but still relevant. The way I do it is by creating a Physics utility class for my unit calculation and conversion:

public final class Physics {

    public static final float PIXELS_PER_UNIT = 32f;

    private Physics() {}

    public static float toUnits(float pixels) {
        return pixels / PIXELS_PER_UNIT;
    }

    public static Vector toUnits(Vector2 pixels) {
        return new Vector2(toUnits(pixels.x), toUnits(pixels.y));
    }

    public static float toPixels(float units) {
        return units * PIXELS_PER_UNIT;
    }

    public static Vector2 toPixels(Vector2 units) {
        return new Vector2(toPixels(units.x), toPixels(units.y));
    }
}

I then create a ShapeFactory class which converts MapObjects into shapes of the same type:

public final class ShapeFactory {

    private ShapeFactory() {}

    public static PolygonShape getRectangle(RectangleMapObject rectangleObject) {
        return getRectangle(rectangleObject.getRectangle());
    }

    public static PolygonShape getRectangle(Rectangle rectangle) {
        PolygonShape polygon = new PolygonShape();

        Vector2 size = new Vector2(
                Physics.toUnits(rectangle.x + rectangle.width * 0.5f),
                Physics.toUnits(rectangle.y + rectangle.height * 0.5f)
        );

        polygon.setAsBox(
                Physics.toUnits(rectangle.width * 0.5f),
                Physics.toUnits(rectangle.height * 0.5f),
                size,
                0.0f
        );

        return polygon;
    }

    public static CircleShape getCircle(CircleMapObject circleObject) {
        return getCircle(circleObject.getCircle());
    }

    public static CircleShape getCircle(Circle circle) {
        CircleShape circleShape = new CircleShape();
        circleShape.setRadius(Physics.toUnits(circle.radius));
        circleShape.setPosition(new Vector2(Physics.toUnits(circle.x), Physics.toUnits(circle.y)));

        return circleShape;
    }

    public static PolygonShape getPolygon(PolygonMapObject polygonObject) {
        return getPolygon(polygonObject.getPolygon());
    }

    public static PolygonShape getPolygon(Polygon polygon) {
        PolygonShape polygonShape = new PolygonShape();

        float[] vertices = polygon.getTransformedVertices();
        float[] worldVertices = new float[vertices.length];

        for (int i = 0; i < vertices.length; ++i) {
            worldVertices[i] = Physics.toUnits(vertices[i]);
        }

        polygonShape.set(worldVertices);

        return polygonShape;
    }

    public static ChainShape getPolyline(PolylineMapObject polylineObject) {
        return getPolyline(polylineObject.getPolyline());
    }

    public static ChainShape getPolyline(Polyline polyline) {
        ChainShape chain = new ChainShape();

        float[] vertices = polyline.getTransformedVertices();
        Vector2[] worldVertices = new Vector2[vertices.length / 2];

        for (int i = 0; i < vertices.length / 2; ++i) {
            worldVertices[i] = new Vector2();
            worldVertices[i].x = Physics.toUnits(vertices[i * 2]);
            worldVertices[i].y = Physics.toUnits(vertices[i * 2 + 1]);
        }

        chain.createChain(worldVertices);

        return chain;
    }
}

Because I use Ashley (an Entity Component System created for libGDX), I create a system which converts all MapObjects from my map's Collision layer into bodies:

public class TiledMapCollisionSystem extends EntitySystem {

    public TiledMapCollisionSystem() {
        MapObjects objects = LevelManager.getActiveMap().getLayers().get(Layers.COLLISION).getObjects();

        for (MapObject object : objects) {
            if (object instanceof TextureMapObject) {
                continue;
            }

            Shape shape;

            if (object instanceof RectangleMapObject) {
                shape = ShapeFactory.getRectangle((RectangleMapObject) object);
            } else if (object instanceof PolygonMapObject) {
                shape = ShapeFactory.getPolygon((PolygonMapObject) object);
            } else if (object instanceof PolylineMapObject) {
                shape = ShapeFactory.getPolyline((PolylineMapObject) object);
            } else if (object instanceof CircleMapObject) {
                shape = ShapeFactory.getCircle((CircleMapObject) object);
            } else {
                continue;
            }

            BodyDef bodyDef = new BodyDef();
            bodyDef.type = BodyDef.BodyType.StaticBody;
            Body body = WorldManager.world.createBody(bodyDef);
            body.createFixture(shape, 1);

            shape.dispose();
        }
    }
}

Note that the map is already loaded and the Box2D world is already created before the system is instantiated. Layers.COLLISION is a simple constant String "Collision". You don't need to create a System if you aren't using Ashley, as you can simply create another utility class which does the same thing.

\$\endgroup\$
0
\$\begingroup\$

First of all you should use a DebugRenderer for testing your Box2D world. Otherwise you will have a hard time figuring out what your world is actually doing:

Box2DDebugRenderer debugRenderer = new Box2DDebugRenderer();

//the below code goes at the end of the render method:
debugRenderer.render(world, camera.combined);

Then another thing that catched my attention is that you never create a Fixture for your player. You just created the shape, but then you do nothing with it. A Fixture is what gives your Body a shape, mass etc. You will have to create a Fixture similar to this and add it to your Body:

BodyDef bodyDef = new BodyDef();
bodyDef.type = BodyType.DynamicBody;

Body body = world.createBody(bodyDef);

CircleShape circle = new CircleShape();
circle.setRadius(8);

//now create fixture and add it to the body:
FixtureDef fixtureDef = new FixtureDef();
fixtureDef.shape = circle;
fixtureDef.density = density; 
fixtureDef.friction = friction;
fixtureDef.restitution = restitution;

body.createFixture(fixtureDef);

circle.dispose(); //dispose circle shape, you don't need anymore.

I didn't dig through all of your code but I think the above should get you started. Also check the box2d libgdx tutorial like for example this box2d-tutorial.

\$\endgroup\$
  • \$\begingroup\$ Thank you for your answer. Right now I have more of an idea how to make this work. But I am thinking about reconstructing my code, because I have a hard time to fit in the mapBuilder and use the Box2d bodies with the current written code. I'm not sure to put it into my "Level" class, wich just loads the level, or into my World class where the level is created. I will need to create a World class to use creating a body if I understood that correctly. So how do I give the player an actual instance of the world without creating it inside the player class? \$\endgroup\$ – Mirco May 19 '15 at 19:12
  • \$\begingroup\$ You could pass the World as a parameter. I usually create a Class called BodyFactory that is responsible for creating all the bodies I need. \$\endgroup\$ – donfuxx May 19 '15 at 19:30
  • \$\begingroup\$ Ye, I guess this is what I will do! I will probably create something like a BodyFactory class for future entities. \$\endgroup\$ – Mirco May 19 '15 at 19:44

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.